Android(安卓)Jetpack Components of ViewModel 学习笔记
Android Jetpack Components of Lifecycle 学习笔记
Android Jetpack Components of LiveData 学习笔记
Android Jetpack Components of ViewModel 学习笔记
Demo 地址:https://github.com/mengzhinan/Lifecycle_LiveData_ViewModel_demo
ViewModel Google 文档:https://developer.android.google.cn/topic/libraries/architecture/viewmodel
环境配置:
与 Lifecycle 和 LiveData 略有不同,在其基础上,还需要导入依赖:
// viewmodel 需要添加的配置implementation 'android.arch.lifecycle:extensions:1.1.1'
ViewModel 是什么?
ViewModel 类的设计目的是以生命周期意识的方式存储和管理与UI相关的数据。ViewModel 类允许数据在配置更改(如屏幕旋转)中生存。
以我的理解总结为:
1、ViewModel 可以实现在同一个 Activity 对象下的多个 Fragment 之间数据共享。
2、ViewModel 可以实现在手机屏幕旋转前后数据共享,避免不必要的重复数据请求。
先从 Demo 使用看起吧。还是上一篇文章的 LiveData Demo:
public class DataUtil { private MutableLiveData name = new MutableLiveData<>(); public LiveData getNameLiveData(){ return name; } public void requestHead() { // 模拟请求网络或 DB,然后更新数据 name.setValue("requestHead success"); } public void requestList() { // 模拟请求网络或 DB,然后更新数据 name.setValue("requestList success"); }}
public class LiveDataActivity extends AppCompatActivity { private DataUtil dataUtil; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); dataUtil = new DataUtil(); dataUtil.getNameLiveData().observe(this, data -> { // 得到数据更新 }); } @Override protected void onResume() { super.onResume(); dataUtil.requestHead(); dataUtil.requestList(); }}
上面代码其实有个问题,如果 DataUtil 重复创建的话,将会对应的重新创建 MutableLiveData 对象,故无法实现数据共享。先别钻牛角,看 ViewModel 的用法:
public class ListViewModel extends ViewModel { private MutableLiveData headLiveData; private MutableLiveData listLiveData; public LiveData getHeadLiveData() { if (headLiveData == null) { headLiveData = new MutableLiveData<>(); } return headLiveData; } public LiveData getListLiveData() { if (listLiveData == null) { listLiveData = new MutableLiveData<>(); } return listLiveData; } public void requestHead() { // TODO: 2019-07-29 headLiveData.setValue("head request success"); } public void requestList() { // TODO: 2019-07-29 listLiveData.setValue("list request success"); }}
public class ViewModelActivity extends AppCompatActivity { private ListViewModel listViewModel; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); listViewModel = ViewModelProviders.of(this).get(ListViewModel.class); listViewModel.getHeadLiveData().observe(this, data->{ // 头部数据更新 }); listViewModel.getListLiveData().observe(this, data->{ // list 数据更新 }); } @Override protected void onResume() { super.onResume(); listViewModel.requestHead(); listViewModel.requestList(); }}
代码说明:
DataUtil 逻辑更改为了 ListViewModel,然后继承了 ViewModel 类。其他的没有本质变化。
在 ViewModelActivity 中,唯一的变化是 ListViewModel 的创建方式变为了:
listViewModel = ViewModelProviders.of(this).get(ListViewModel.class);
我们先来分析 ViewModel 类:
public abstract class ViewModel { /** * This method will be called when this ViewModel is no longer used and will be destroyed. * * It is useful when ViewModel observes some data and you need to clear this subscription to * prevent a leak of this ViewModel. */ @SuppressWarnings("WeakerAccess") protected void onCleared() { }}
这是一个抽象类,其中只有一个方法 onCleared(),当 ViewModel 所在的 Activity 销毁时,回调此方法清理资源。
ViewModel 有一个比较常用的子类 AndroidViewModel,提供 application 对象:
/** * Application context aware {@link ViewModel}. * * Subclasses must have a constructor which accepts {@link Application} as the only parameter. *
*/public class AndroidViewModel extends ViewModel { @SuppressLint("StaticFieldLeak") private Application mApplication; public AndroidViewModel(@NonNull Application application) { mApplication = application; } /** * Return the application. */ @SuppressWarnings("TypeParameterUnusedInFormals") @NonNull public T getApplication() { //noinspection unchecked return (T) mApplication; }}
我们再回头分析 ViewModel 获取的方法:
ViewModelProviders.of(this).get(ListViewModel.class);
先追查 get() 方法:
@NonNull @MainThread public T get(@NonNull Class modelClass) { String canonicalName = modelClass.getCanonicalName(); if (canonicalName == null) { throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels"); } return get(DEFAULT_KEY + ":" + canonicalName, modelClass); }
继续追查 get() 重载方法:
@NonNull @MainThread public T get(@NonNull String key, @NonNull Class modelClass) { ViewModel viewModel = mViewModelStore.get(key); if (modelClass.isInstance(viewModel)) { //noinspection unchecked return (T) viewModel; } else { //noinspection StatementWithEmptyBody if (viewModel != null) { // TODO: log a warning. } } viewModel = mFactory.create(modelClass); mViewModelStore.put(key, viewModel); //noinspection unchecked return (T) viewModel; }
如果传递的 ViewModel 已经缓存了,则把缓存的 ViewModel 对象直接返回;否则才创建新的对象。
那么 mViewModelStore 是如何做到缓存的呢?
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) { mFactory = factory; this.mViewModelStore = store; }
发现 mViewModelStore 是在 ViewModelProvider 的构造函数中赋值的。追查到 ViewModelProviders 的 of 方法中:
@NonNull @MainThread public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) { Application application = checkApplication(activity); if (factory == null) { factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application); } return new ViewModelProvider(ViewModelStores.of(activity), factory); }
追索到 ViewModelStores 的 of 方法:
@NonNull @MainThread public static ViewModelStore of(@NonNull FragmentActivity activity) { if (activity instanceof ViewModelStoreOwner) { return ((ViewModelStoreOwner) activity).getViewModelStore(); } return holderFragmentFor(activity).getViewModelStore(); }
找到 HolderFragment 中的 holderFragmentFor() 方法:
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public static HolderFragment holderFragmentFor(FragmentActivity activity) { return sHolderFragmentManager.holderFragmentFor(activity); }
其实看到这儿,基本可以看出原理:
在 Activity 中创建一个 HolderFragment 对象,并添加到 FragmentManager 中。在 HolderFragment 中保存 ViewModel 集合。
因此,当 Activity 销毁时,也会同步销毁 HolderFragment。
那为什么说手机屏幕旋转前后,ViewModel 不会销毁呢?
追查代码,HolderFragment 类的构造函数:
public HolderFragment() { setRetainInstance(true); }
setRetainInstance(true); 这行代码应该是设置避免在屏幕旋转时重建 Fragment。
所以,总结 ViewModel 的特点:
ViewModel 是依附 HolderFragment 保存在 Activity 的 FragmentManager 中的。在同一个 Activity 下的任意位置、任意 Fragment 中通过 ViewModelProviders.of(this).get(xxx) 方式获取 ViewModel,都会是同一个对象,故避免了重复请求,实现数据共享。
由于 HolderFragment 的特点,可避免屏幕旋转后的数据丢失,避免重复请求。
Demo 地址:https://github.com/mengzhinan/Lifecycle_LiveData_ViewModel_demo
更多相关文章
- android 读取指定路径数据库文件
- Android(安卓)transformClassesWithDexForAdh5Debug 的解决方法
- Android基站+联网+google数据库定位
- android 数据库工具类MyDbHelper
- 浅谈Java中Collections.sort对List排序的两种方法
- mybatisplus的坑 insert标签insert into select无参数问题的解决
- python起点网月票榜字体反爬案例
- 类和 Json对象
- Python list sort方法的具体使用