可能有人觉得我写的 和官网不太一样 我这几篇关于架构组件的博客 是自己在学习的过程中的随笔 也可能有很多错误 只能说作为参考 后面我再使用过程中 会不断纠正自己之前的理解
简介:
上一篇 关于LiveData 的学习 相信都差不多能看懂 我们再使用liveData的过程中 将其放在ViewModel中进行存储管理了 那么究竟什么是ViewModel 他有什么用处 好处呢 ? 我们根据文档 大概了解下
官方:The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations.
ViewModel能够将数据控制 和UI控制分离。也就是当我们UIInactive的时候能够帮助我们存储数据 当active的时候能够提供给我们需要的数据 是不是很棒~ 那我们继续
实现ViewModel
情景: 我们需要从网络中获取用户列表(耗时操作) 然后对获取到的用户列表再activity中进行展示
对于该场景 常用做法不说 这里 我们希望在ViewModel中完成数据的获取和存储 activity只负责展示即可 ok let's try
我们之前已经写过一个模拟股票波动的 ViewModel的用法 我们这里再写个 获取用户列表 的
public class CustomViewModel extends ViewModel{ private UserManagerLiveData userLiveData; public LiveData> getUserLiveData() { if (userLiveData == null) { userLiveData = new UserManagerLiveData(); } return userLiveData; } }
public class User { String name; String sex; public User(String name, String sex) { this.name = name; this.sex = sex; } public String getName() { return name; }
public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", sex='" + sex + '\'' + '}'; } }
public class UserManagerLiveData extends LiveData> { private UserRequest userRequest; private UserRequestListener userRequestListener = userList -> postValue(userList); public UserManagerLiveData() { if (userRequest == null) { userRequest = new UserRequest(); } } @Override protected void onActive() { super.onActive(); userRequest.requestUserList(userRequestListener); } @Override protected void onInactive() { super.onInactive(); } } interface UserRequestListener { void onSuccess(List
userList); }
public class UserRequest { public void requestUserList(UserRequestListener userRequestListener) { ListuserList = new ArrayList<>(); for (int i=0;i<10;i++){ User man = new User("name" + i, "man"); userList.add(man); } new Thread(new Runnable() { @Override public void run() { // 模拟股市波动 1股每1s 涨1块钱 高兴 for (int i = 0; i < 50; i++) { SystemClock.sleep(1000); if (userRequestListener != null) { userRequestListener.onSuccess(userList); } } } }).start(); } }
private void addViewModelLiveData() { CustomViewModel customViewModel = ViewModelProviders.of(this).get(CustomViewModel.class); customViewModel.getUserLiveData().observe(this, userList -> { for (User user : userList) { Log.d(TAG, user.toString()); } }); }
直接上运行结果 不解释啦~
03-28 20:29:22.096 13534-13534/com.example.lifeapplication D/lifeCycle: onPause
03-28 20:29:22.159 13534-13534/com.example.lifeapplication D/lifeCycle: User{name='name0', sex='man'}
03-28 20:29:22.159 13534-13534/com.example.lifeapplication D/lifeCycle: User{name='name1', sex='man'}
03-28 20:29:22.159 13534-13534/com.example.lifeapplication D/lifeCycle: User{name='name2', sex='man'}
03-28 20:29:22.159 13534-13534/com.example.lifeapplication D/lifeCycle: User{name='name3', sex='man'}
03-28 20:29:22.160 13534-13534/com.example.lifeapplication D/lifeCycle: User{name='name4', sex='man'}
03-28 20:29:22.160 13534-13534/com.example.lifeapplication D/lifeCycle: User{name='name5', sex='man'}
03-28 20:29:22.160 13534-13534/com.example.lifeapplication D/lifeCycle: User{name='name6', sex='man'}
03-28 20:29:22.160 13534-13534/com.example.lifeapplication D/lifeCycle: User{name='name7', sex='man'}
03-28 20:29:22.160 13534-13534/com.example.lifeapplication D/lifeCycle: User{name='name8', sex='man'}
03-28 20:29:22.160 13534-13534/com.example.lifeapplication D/lifeCycle: User{name='name9', sex='man'}
03-28 20:29:22.531 13534-13534/com.example.lifeapplication D/lifeCycle: onStop
03-28 20:29:29.353 13534-13534/com.example.lifeapplication D/lifeCycle: onStart
03-28 20:29:29.354 13534-13534/com.example.lifeapplication D/lifeCycle: User{name='name0', sex='man'}
03-28 20:29:29.354 13534-13534/com.example.lifeapplication D/lifeCycle: User{name='name1', sex='man'}
03-28 20:29:29.354 13534-13534/com.example.lifeapplication D/lifeCycle: User{name='name2', sex='man'}
03-28 20:29:29.355 13534-13534/com.example.lifeapplication D/lifeCycle: User{name='name3', sex='man'}
03-28 20:29:29.355 13534-13534/com.example.lifeapplication D/lifeCycle: User{name='name4', sex='man'}
03-28 20:29:29.355 13534-13534/com.example.lifeapplication D/lifeCycle: User{name='name5', sex='man'}
03-28 20:29:29.355 13534-13534/com.example.lifeapplication D/lifeCycle: User{name='name6', sex='man'}
03-28 20:29:29.355 13534-13534/com.example.lifeapplication D/lifeCycle: User{name='name7', sex='man'}
03-28 20:29:29.355 13534-13534/com.example.lifeapplication D/lifeCycle: User{name='name8', sex='man'}
03-28 20:29:29.355 13534-13534/com.example.lifeapplication D/lifeCycle: User{name='name9', sex='man'}
03-28 20:29:29.356 13534-13534/com.example.lifeapplication D/lifeCycle: onResume
03-28 20:29:30.128 13534-13534/com.example.lifeapplication D/lifeCycle: User{name='name0', sex='man'}
我们可以看出ViewModel其实是独立于生命周期持有者所存在的 这样我们可以更容易的写测试用例覆盖ViewModel中的操作
viewModel可以持有liveCycleObservers 比如liveData 具体如何持有 可以看前面两篇文章
如果ViewModel想使用Application的引用 比如去获取系统服务 那么此时可以继承AndroidViewModel 该类包含了一个构造函数能够拿到Application的引用
接下来我们看下ViewModel 的生命周期
官方:ViewModel objects are scoped to the Lifecycle passed to the ViewModelProvider when getting the ViewModel. The ViewModel remains in memory until the Lifecycle it's scoped to goes away permanently: in the case of an activity, when it finishes, while in the case of a fragment, when it's detached.
官方的意思大概是这样:ViewModel对象的生命周期是 通过ViewModelProvider获取的时候 被限定的,这个ViewModel对象一直被存储在内存中 知道activity finish 或者是fragment被detached的时候 被分离 。分离之后作用域永久消失
什么鬼 ??? 举个栗子 之前我们再activity中 通过ViewModelProviders 获取了CustomViewModel对象 那么此时改Viewmodel对象的生命周期已经获得了 和我们activity进行了捆绑 当我们activity finish的时候 ViweModel 才会被分离 差不多 我们继续
我们看下 下面这个图片 这个图片表述了一个activity 经过了一次屏幕旋转之后 ViewModel对应情况 这样就可以验证我们上面说的了 activity oncreate 方法在旋转屏幕的时候被重复调用 但是finish 并没有被调用 所以ViewModel对象一直存在于内存中 换句话说 我们所持有的ViewModel对象只是我们第一次创建的对象 所以这样你就明白为什么 旋转屏幕 我们ViweModel仍然能持有数据了吧
好了 我们可以继续了 根据上面得出的结论 我们看下 ViewModel再两个Fragment之间传递数据
应用场景 :
PS:关于Fragment之间传递数据 是特别常见的 比如我们常用的搜索界面(包含搜索历史) 和搜索结果页
官方是这样说的 : 加入你有一个Fragment(用户选择一个item) 还有一个Fragment显示这个选择 的item 我们通常的做法是定义两个接口 然后再activity中绑定两个接口
这样常见的需求 处理起来如此麻烦 ViewModel完全可以帮助我们简化这样的痛点
实现原理 就是我们上面讲解的生命周期 只要activity不finish 那么改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.
});
}
}
我们 发现 再获取ViewModel对象的时候 我们使用的是getActivity 所以每个Fragment所得到的都是捆绑在Activity上的ViewModel的单例 所以
1.此时我们可以进行Fragment之间的通信了 而且这个通信 我们Activity不需要参与
2.Fragment之间互不影响 也就是Fragment finish不会影响其他Fragment的运行
是不是方便了一些~ ok 继续
使用ViewModel取代Loader
情景:我们经常会用CustomLoader再UI进程中 实现DataSource的异步刷新 那么根据上面我们介绍的 我们可以使用ViewModel将数据和界面相分离 实现类之间的解耦
一些常见的loader的工作方式 如图所示
ViewModel replace Loader工作原理
详细梳理下 ViewModel实现的逻辑
如图所示ViewModel依赖于Room 和LiveData工作的 关于LiveData工作 大家看过之前文章的应该很熟悉 Room是什么鬼 ? 我们下篇文章会具体讲解 大概知道有这么一只鬼 之前我们已经说过很多次了 ViewModel捆绑生命周期持有者之后 能够确保数据能够在横竖屏切换 等等 设备配置改变的时候能让存活 从而保证数据能够存活 (只有当生命周期持有者finish 时才会分离 ) 又复习了一遍。Room的作用是通知LiveData数据发生变化 ,然后LiveData通过setValue/postValue 或者其他方式 通知UI变化
关于 替代的好处 官方翻译过来是这样说的
随着您的数据变得越来越复杂,您可能会选择一个单独的类来加载数据。ViewModel的目的是封装UI控制器的数据,以便在配置更改时让数据存活
还是为了 解耦和数据保活 好了 大概先到这 我们去了解下Room~