美文网首页android
Android Jetpack ViewModel解析

Android Jetpack ViewModel解析

作者: 唠嗑008 | 来源:发表于2019-12-31 17:43 被阅读0次

前言

最近几天把Jetpack中的3剑客Lifecycle+ViewModel+LiveData的使用和原理学习了一遍,这3者也是构建MVVM模式的核心。这个系列的工具,建议大家在官网学习:https://developer.android.google.cn/jetpack

目前网上对这3者使用和源码解析的文章也沉淀了不少,大家可以自行查阅。本文换个方向来跟大家聊聊我所理解的关于ViewModel的一些内容。

系列文章

Android Jetpack ViewModel解析
Android Jetpack LiveData解析
Android Jetpack DataBinding原理浅析(简版)

看看官方对Viewmodel作用的解释

官方的Viewmodel用法例子

官方的解释说明与案例还是很清晰的,所以就不做过多解释了。说一下我在学习过程中的一些疑问吧。

1、在官网和Google的github demo中都建议在mvvm模式中的vm层去继承ViewModel,那为什么一定要继承ViewModel呢?我直接在VM中定义LiveData数据不行吗?是否继承ViewModel的区别在哪?

下面通过一个例子来讲解,这里为了突出核心要点就用近乎伪代码的形式来表述。首先在mvvm的VM层去获取网络数据,实际上是model层执行的,然后当获取数据成功后调用LiveData对象的setValue()方法通知Activity的观察者去刷新UI。

public class NewsViewModel extends ViewModel {
    private NewsModel newsModel;
    //adapter数据源
    private List<ItemNews> mList = new ArrayList<>();
    public MutableLiveData<List<ItemNews>> itemNewsLiveData = new MutableLiveData<>();

    public NewsViewModel() {
        newsModel = new NewsModel();
    }

    public void getNews() {
      mList =   newsModel.getNews();
      //通知活跃的观察者更新数据
      itemNewsLiveData.setValue(mList);
    }
}

Activity里面的观察者

 newsViewModel.itemNewsLiveData.observe(this, new Observer<List<ItemNews>>() {
            @Override
            public void onChanged(@Nullable List<ItemNews> itemNews) {
             newsAdapter.updateAll(itemNews);
            }
        });

这里按照官方的规范,只是在View层持有VM的引用,而VM不持有View的引用。到这一步我就有一个疑问,貌似不继承ViewModel也可以完成VM与View的交互,那引入它到底有什么好处呢?

再回顾下官方的说法:

ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel类让数据可在发生屏幕旋转等配置更改后继续存在。

上面说到ViewModel管理数据是和生命周期相关的,且能持久存储数据。那下面就简单解析一下是如何与生命周期相关的:

官方ViewModel生命周期

这里先说结论,源码验证待会儿再说。

现在可以看出,好处1:
因为ViewModel的生命周期是比Activity还要长,所以ViewModel可以持久保存UI数据,具体来说是Activity因为配置更改或者被系统意外回收的时候,会自动保存数据,然后在Activity重建的时候就可以继续使用销毁之前保存的数据。

好处2:
当使用ViewModel的 Activity 正常退出时,内部会调用ViewModel对象的 onCleared()方法,以便它可以清理资源。首先内部会释放ViewModel数据,同时你也可以在vm层重写这个方法去释放资源,取消网络等。

好处3:
在 Fragment 之间共享数据。这个比较简单,只要2个Fragment获取
ViewModel对象时传入的LifecycleOwner一致就可以共享ViewModel中定义的数据。

现在回头看刚才的例子,当配置改变导致Activity重建的时候,如果没有使用ViewModel存储数据,那么就会重新请求数据重绘页面;如果用ViewModel的话,就能用页面销毁之前保存的数据去直接显示视图。

2、在MVVM中用了ViewModel+LiveData之后还有必要在Activity/Fragment的onDestory()方法中手动取消耗时任务(网络请求)吗?

引入了ViewModelLiveData之后,可以实现vmview的解耦,只是view引用vm,而vm是不持有view的引用的。在activity退出之后即是还有网络在继续也不会引发内存泄漏和空指针异常,所以不在ondestory取消网络也是可以的。如果你还是想取消,可以重写onCleared()方法去做。

原理解析

ViewModel源码真的很简单,就几个类,实际上实际上真正的业务处理核心就只有ViewModelProvider,其他都是辅助。

  • ViewModel类是一个抽象方法,只有一个空方法onCleared(),它会在onDestory的时候被调用,所以你可以重写这个方法,在里面做些释放资源、取消网络的操作。
  • AndroidViewModel是继承ViewModel的,就多了一步,提供Application参数
  • ViewModelStore:和名字一样,就是存储ViewModel的,它里面定义了一个HashMap来存储ViewModel,key值是ViewModel全路径+一个默认的前缀。大概这样:key=android.arch.lifecycle.ViewModelProvider.DefaultKey:com.zx.mvvmdemo.vm.NewsViewModel
  • ViewModelStoreOwner:是一个接口,只有一个方法,是获取ViewModelStore对象的
    ViewModel核心类

通常定义ViewModel之后,通过如下方式获取ViewModel对象

newsViewModel = ViewModelProviders.of(this).get(NewsViewModel.class);

of传FragmentActivity或者Fragment对象。
这里主要分析2个问题:

  • 1、ViewModel是如何创建的?
  • 2、ViewModel是如何保存数据的?

这里不按照执行流程去阅读源码那样分析,因为这么详细的内容已经有人写了,我就讲一下关键的思路,细节的话,推荐一遍不错的:
Android ViewModel,再学不会你砍我

从入口来看,是外观模式,ViewModelProviders就是外观类,真正的业务处理是在ViewModelProvider注意前者多了一个s,核心就在ViewModelProviderget()方法

ViewModelProvider.get

逻辑大致如下:

首先从ViewModelStore的HashMap这个缓存中取取数据,如果有直接返回ViewModel,如果没有通过ViewModelProvider内部的工厂类去创建,ViewModel的创建具体来说是通过反射区做的,并且保存在缓存中,这样就获取到了ViewModel对象。

到这里,问题1就解决了。下面看看问题2,数据是如何保存的。
数据保存是在FragmentActivity中实现的。

因为配置变化导致Activity销毁重建以前的方案是保存数据在onSaveInstanceState,而还原在onRestoreInstanceState,但是这2个方法是有很大瑕疵的,于是在新的SDK中FragmentActivity内置了2个新的方法onRetainNonConfigurationInstancegetLastNonConfigurationInstance,更多细节可以参考:
https://www.cnblogs.com/dengxianzhi/articles/2248655.html

其中onRetainNonConfigurationInstance是在onStop() 和 onDestroy()之间被调用,它内部会保存ViewModel数据;而在onCreate的时候会调用getLastNonConfigurationInstance来恢复数据。

借用刚才文章的一张图来说明保存恢复数据流程 保存恢复数据流程

总结

1、让mvvm的vm去继承ViewModel,并在其内部定义、管理UI所需的数据,不仅可以让View和ViewModel解耦,同时其内部自动关联生命周期,可以减少在Activity的生命周期方法中写大量的样板代码。

2、ViewModel保存数据这个功能还是有点用的。一开始我觉得当屏幕旋转的时候你可以通过configChanges的设置来阻止它的重建,这样就不需要viewmodel保存数据了。但是其它的一些意外情况也是有可能导致Activity重建的,比如当前activity在填写一堆表单数据,中间打开了其他app,在后台被回收了,后面再进入的时候如果没有保存就得重填,如果你的UI数据放在ViewModel的话就会自动回复,这样看来,ViewModel的保存数据功能是不是还挺有用呢

3、利用ViewModel共享数据可以更简洁,减少很多不必要的接口。

4、友情提示:在创建ViewModel对象的时候不能手动去new一个,而是通过Provider的方式去获取,这样的话才能利用ViewModel的那些优势。

相关文章

网友评论

    本文标题:Android Jetpack ViewModel解析

    本文链接:https://www.haomeiwen.com/subject/ietvoctx.html