首页 > 技术文章 > Android架构组件:ViewMode概述

vivian187 2020-04-28 12:13 原文

作用:

ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据。

问题:

(1) Android 框架可以管理界面控制器(如 Activity, Fragment)的生命周期。由Android 框架响应的某些用户操作或设备事件,用户无法控制。    
当系统销毁或重新创建界面控制器,则存储在其中的任何临时性界面相关数据都会丢失。对于简单的数据,Activity可以使用onSaveInstanceState()存储数据,组件重建后从onCreate()中的Bundle恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,如果数据量较大,如用户列表或位图,则不适合。

(2)界面控制器经常需要进行异步调用,这些调用可能需要一些时间才能返回结果,有可能会出现UI组件已销毁,而请求还未返回的情况。在因配置更改而重新创建对象的情况下,对象可能需要重新发送请求,会造成资源的浪费

(3)应该遵循职责分离原则,将视图数据从界面控制器逻辑中分离出来,这样不会导致界面控制器类代码膨胀

ViewModel的用法:

架构组件为界面控制器提供了 ViewModel 辅助程序类,该类负责为界面准备数据。
在配置更改期间会自动保留 ViewModel 对象,以便它们存储的数据立即可供下一个Activity/Fragment实例使用。如以下示例代码所示:

public class MyViewModel extends ViewModel {
        private MutableLiveData<List<User>> users;
        public LiveData<List<User>> getUsers() {
            if (users == null) {
                users = new MutableLiveData<List<User>>();
                loadUsers();
            }
            return users;
        }

        private void loadUsers() {
            // Do an asynchronous operation to fetch users.
        }
    }

可以从 Activity 访问该列表

 public class MyActivity extends AppCompatActivity {
        public void onCreate(Bundle savedInstanceState) {
            // Create a ViewModel the first time the system calls an activity's onCreate() method.
            // Re-created activities receive the same MyViewModel instance created by the first activity.

            MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
            model.getUsers().observe(this, users -> {
                // update UI
            });
        }
    }

如果Activity被重新创建了,它会收到被之前Activity创建的相同MyViewModel实例。当所属Activity终止后,框架调用ViewModel的onCleared()方法清除资源。

因为ViewModel在指定的Activity或Fragment实例外存活,它应该永远不能引用一个View,或持有任何包含Activity context引用的类。如果ViewModel需要Application的context(如获取系统服务),可以扩展AndroidViewmodel,并拥有一个构造器接收Application。

ViewModel 的生命周期

ViewModel 对象存在的时间范围是获取ViewModel时传递给ViewModelProvider的Lifecycle。ViewModel将一直留在内存中,直到限定其存在时间范围的Lifecycle 永久消失:对于 Activity,是在Activity完成时;而对于Fragment,是在Fragment分离时。

上图左侧为Activity的生命周期过程,期间有一个旋转屏幕的操作;右侧则为ViewModel的生命周期过程。
一般通过如下代码初始化ViewModel:

viewModel = ViewModelProviders.of(this).get(UserProfileViewModel.class);
// this参数一般为Activity或Fragment,因此ViewModelProvider可以获取组件的生命周期。

Activity在生命周期中可能会触发多次onCreate(),而ViewModel则只会在第一次onCreate()时创建,然后直到最后Activity销毁。

在Fragment间共享数据

一个Activity中的多个Fragment相互通讯是很常见的。假设有一个Fragment,在该Fragment 中,用户从列表中选择一项,还有另一个Fragment,用于显示选定项的内容。这种情况不太容易处理,因为这两个 Fragment都需要定义某种接口描述,并且所有者Activity必须将两者绑定在一起。此外,这两个 Fragment 都必须处理另一个Fragment尚未创建或不可见的情况。

可以使用 ViewModel 对象解决这一常见的难点。这两个Fragment可以使用其 Activity 范围共享 ViewModel 来处理此类通信,如以下示例代码所示:

public class SharedViewModel extends ViewModel {
        private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

        public void select(Item item) {
            selected.setValue(item);
        }

        public LiveData<Item> getSelected() {
            return selected;
        }
    }

    public class MasterFragment extends Fragment {
        private SharedViewModel model;
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
            itemSelector.setOnClickListener(item -> {
                model.select(item);
            });
        }
    }

    public class DetailFragment extends Fragment {
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
            model.getSelected().observe(this, { item ->
               // Update the UI.
            });
        }
    }

注意:这两个Fragment都会检索包含它们的Activity。这样,当这两个Fragment各自获取ViewModelProvider时,它们会收到相同的SharedViewModel实例(其范围限定为该Activity)。
此方法具有以下优势:

* Activity 不需要执行任何操作,也不需要对此通信有任何了解。
* 除了SharedViewModel约定之外,Fragment不需要相互了解。如果其中一个Fragment 消失,另一个Fragment将继续照常工作。
* 每个Fragment 都有自己的生命周期,而不受另一个Fragment的生命周期的影响。如果一个Fragment替换另一个Fragment,界面将继续工作而没有任何问题。

参考:
https://developer.android.google.cn/topic/libraries/architecture/viewmodel.html#java
https://blog.csdn.net/qq_24442769/article/details/79426609
https://blog.csdn.net/zhuzp_blog/article/details/78910535

推荐阅读