Jetpack系列组件--ViewModel从相遇到相知

2020/2/10 4:08:36

本文主要是介绍Jetpack系列组件--ViewModel从相遇到相知,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一、什么是ViewModel
  • 一句话概括ViewModel可以简单理解为带有生命周期的数据存储对象。一般会和 LivedataDataBinding 等其他组件进行组合使用。

不要将太多的逻辑处理、数据获取、存储放到 ViewModel 类,它仅仅作为 UI 数据的保存(暴露获取数据的方法),数据存储和获取可以创建 Repository 类(页面数据不复杂的情况,直接在ViewModel中读写也是可以的,灵活应用嘛)

二、 ViewModel的优势
  • 页面配置更改数据不丢失
    当设备因配置更改(横竖屏旋转,软键盘模式,设备分辨率,权限开关)导致Activity/Fragment重建,ViewModel中的数据并不会因此而丢失(数据保存读取下面讲),配合LiveData可以在页面重建后立马能收到最新保存的数据用以重新渲染页面。

  • 生命周期感应
    ViewModel中难免会做一些网络请求或数据的处理,可以复写onCleared()方法,终止清理一些操作,释放内存。该方法在宿主onDestroy时被调用。

  • 数据共享
    对于单Activity,对Fragment的页面,可以使用ViewModel实现页面之间的数据共享

三、如何引入ViewModel
  //包含了 viewmodel 和 livedata
  api 'androidx.lifecycle:lifecycle-extensions:2.1.0'

  //或者指明使用viewmodel
  api "androidx.lifecycle:lifecycle-viewmodel:2.1.0"

四、ViewModel如何使用

ViewModel的使用非常简单,这行代码便可以得到ViewModel对象。可以看到,允许传入ActivityFragment对象。所以对于单Activity,多Fragment的场景,我们可以利用这一特性,实现多Fragment页面之间的数据共享,页面通信。

MyViewModel viewModel = ViewModelProviders.of(Activity/Fragment).get(MyViewModel.class);
五、ViewModel数据存储,读取
  • 我们知道,在ActivityonSaveInstanceStateonRestoreInstanceState两个方法中可以存储key-value基本类型的数据
    这两个方法是在应用内存不足,页面被回收时触发。

  • ViewModel数据的存储是在ComponentActivity的 onRetainNonConfigurationInstance()方法中保存ViewModel对象,在getLastNonConfigurationChildInstances方法中恢复ViewModel对象。
    这两个方法恰巧是在页面因配置更改时被触发

  • 实际上除了上面四个方法外,Activity还提供了保存\恢复Dialog对话框的方法saveManagedDialogs, restorManaagedDialog。这俩方法也是在内存不足时被调用,和onSaveInstanceState调用时机一样。

六、ViewModel源码分析

ViewModel本身就是一个抽象类,没错,整个ViewModel的设计就是很简洁,我们需要到ViewModelProvider中继续分析

public abstract class ViewModel {
    
    protected void onCleared() {
    }
   ......省略其他无关紧要的代码
}

ViewModelProvider顾名思义,ViewModel提供者,它里面有一个对象ViewModelStore,用来存储当前页面一个个的ViewModel实例对象

ViewModelStore 内部就是一个HashMap。用以key-value存储ViewModel,非常简单。

@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
        @Nullable Factory factory) {
    Application application = checkApplication(activity);
    if (factory == null) {
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    //在获取ViewModelProvider时要求传入ViewModelStore,此时就从Activity中获取了
   //还要求传入Factory,用以以何种方式创建ViewModel,默认是AndroidViewModelFactory。
   //该Factory允许ViewModel构造函数中携带一个Application参数。
   //对象的创建如下,如果创建失败,则调用空参构造函数创建ViewModel对象。
  //modelClass.getConstructor(Application.class).newInstance(mApplication);
    return new ViewModelProvider(activity.getViewModelStore(), factory);
}

关键点来了,想要ViewModel不随着宿主重建而销毁,那就要保证ViewModelStore不随着宿主重建而销毁。
那么ViewModelStore又是在什么时机被保存起来的呢?通过查看源码原来是利用ComponentActivity中的onRetainNonConfigurationInstancegetLastNonConfigurationInstance方法。
我们看一下ComponentActivity中的getViewModelStore方法:

public ViewModelStore getViewModelStore() {
        if (getApplication() == null) {
            throw new IllegalStateException("Your activity is not yet attached to the "
                    + "Application instance. You can't request ViewModel before onCreate call.");
        }
        if (mViewModelStore == null) {
  //获取NonConfigurationInstances,如果不为空再获取viewModelStore
  //当配置发生改变可以重写onRetainCustomNonConfigurationInstance方法传入自定义数据,当需要时再通过getLastNonConfigurationInstance获取
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
          //如果没有获取到,创建ViewModelStore对象
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }

再来看一下ComponentActivityonRetainNonConfigurationInstance如何保存viewModelStore

public final Object onRetainNonConfigurationInstance() {
        Object custom = onRetainCustomNonConfigurationInstance();
        ViewModelStore viewModelStore = mViewModelStore;
        if (viewModelStore == null) {
            // 如果NonConfigurationInstance保存了viewModelStore,把它取出来
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                viewModelStore = nc.viewModelStore;
            }
        }

        if (viewModelStore == null && custom == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom; 
  	    //把viewModelStore放到NonConfigurationInstances中并返回
        nci.viewModelStore = viewModelStore;
         //这样当页面被重建而销毁时ViewModelStore就被保存起来了。
        return nci;
    }

onRetainNonConfigurationInstance何时被调用,数据又是怎样保存的呢?了解过Activity启动流程的都知道ActivityThread,它控制着Activity的生命周期,当ActivityThread执行performDestroyActivity这个方法时,会调用Activity#retainNonConfigurationInstances获取到保存的数据并保存到ActivityClientRecord中。

ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
            int configChanges, boolean getNonConfigInstance, String reason) {
        ...
           //保存retainNonConfigurationInstances中的数据到ActivityClientRecord中
         ActivityClientRecord r = mActivities.get(token);
         r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
        ...
        return r;

当页面重建完成,ActivityThread执行了performLaunchActivity方法时,会调用Activityattach方法,便会把刚刚存储的数据,传递进去。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
       activity.attach(......, r.lastNonConfigurationInstances,.....);
}
七、ViewModel生命周期

由此可见,ViewModel的生命周期比宿主要长。但宿主被销毁时ViewModelonCleared方法会被调用,在这里可以做清理释放的工作。
image.png

点击查看更多内容


这篇关于Jetpack系列组件--ViewModel从相遇到相知的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程