首先我们要知道ViewModel是什么?怎么用?内部怎么实现的?好处是什么?
ViewModel 是一种用来存储和管理UI相关数据的类。但不同的是,它支持在系统配置发生改变的时候自动对数据进行保存。谷歌推荐我们配合 LiveData使用,当然你也可以往里面存任何数据或者其他参数等。
为什么我们要使用ViewModel?因为对于一些简单的数据,我们可以通过在Activity的 onSaveInstanceState()方法中存储,然后在onCreate()中进行恢复,但是这种方式只适合存储少量的数据,并且是能被序列化和反序列化的数据。而对那些大量的数据则不适用,比如一个 User 或者 Bitmap 的 List。此外,它也使得 View 的数据持有者和 UI controller 逻辑更加分离,便于解耦和测试。
Activity配置更改重建时(比如屏幕旋转)保留数据
UI组件(Activity与Fragment、Fragment与Fragment)间实现数据共享。
一、ViewModel的使用
1.引入ViewModel
def lifecycle_version = "2.0.0"
// ViewModel and LiveData
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
2. 简单使用起来
public class UserModel extends ViewModel {
public final MutableLiveData<User> mUserLiveData = new MutableLiveData<>();
public UserModel() {
//模拟从网络加载用户信息
mUserLiveData.postValue(new User(1, "name1"));
}
//模拟 进行一些数据操作
public void doSomething() {
User user = mUserLiveData.getValue();
if (user != null) {
user.age = 1;
mUserLiveData.setValue(user);
}
}
}
3.这时候在Activity中就可以使用ViewModel了. 其实就是一句代码简单实例化,然后就可以使用ViewModel了.
//构建ViewModel实例
final UserModel userModel = ViewModelProviders.of(this).get(UserModel.class);
//让TextView观察ViewModel中数据的变化,并实时展示
userModel.mUserLiveData.observe(this, new Observer<User>() {
@Override
public void onChanged(User user) {
mContentTv.setText(user.toString());
}
});
findViewById(R.id.btn_test).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//点击按钮 更新User数据 观察TextView变化
userModel.doSomething();
}
});
这个时候,我们点击一下按钮(user中的age变为15),我们可以旋转手机屏幕(这个时候其实Activity是重新创建了,也就是onCreate()方法被再次调用,但是ViewModel其实是没有重新创建的,还是之前那个ViewModel),但是当我们旋转之后,发现TextView上显示的age居然还是15,,,,这就是ViewModel的魔性所在.ViewModel也常用于Activity和Fragment之间进行逻辑传值处理等。提下ViewModel的生命周期了,它只有在Activity销毁之后,它才会自动销毁(所以别让ViewModel持有Activity引用啊,会内存泄露的). 下面引用一下谷歌官方的图片,将ViewModel的生命周期展示的淋漓尽致.

二、ViewModel源码解析
UserModel userModel = ViewModelProviders.of(this).get(UserModel.class);
首先看下ViewModelProviders.of(this)




这里通过factory单例工厂获取factory,再看.get(MyViewModel.class)逻辑:

Java中获取类名主要有三个方法供我们使用:getName(), getCanonicalName() 和getSimpleName(),通过类名的键值对进行缓存

这里进行ViewModel的获取,如果ViewModelStore中有缓存直接获取缓存,如果没有,就新创建一个,然后再存储起来返回。这里的创建是通过AndroidViewModelFactory,通过反射创建ViewModel实体类

我们这里重点关注下ViewModelStore这个类,

在ComponentActivity中,onRetainNonConfigurationInstance()也具有相同的目的来处理类似的请求,其主要是由于旋转设备而更改显示模式,进而触发这个方法的调用。


这里就是通过从ViewModelStrore中的一样的key获取同一个VIewModel来进行数据的操作的,屏幕旋转或者通过Fragment传值getActivity()来进行使用
网友评论