Jetpack 源码分析(三) - ViewModel源码分析

  最初,我认为ViewModel的原理是非常简单,包括网络上有很多相关的源码分析的文章都是这么认为。但是当我再一次认认真真的官方文档之后,才知道ViewModel涉及到的东西远不及我们所知道的那些。
  众所周知,ViewModel保存的数据,在配置更改之后依然也是存在的。但是这就是引出了一个问题,ViewModelonSaveIntanceState方法有什么区别?因为我们都知道onSaveIntanceState方法就是用来保存和恢复UI的状态和数据的。这个是本文分析的一个重点。
  同时,ViewModel只会保存因为配置更改导致Activity重建相关的数据,并不能保存因为Activity退到后台,因资源限制(比如内存限制,电量限制等)导致被杀掉之前的数据--这就是onSaveIntanceState方法干的事。但是如果我们想要ViewModel能够拥有这部分的能力,又应该怎么办呢?这就设计到SavedStateHandle,这也是本文就要分析的一个重点。特别注意的是,我大概看了一下网络上相关的资料,发现在关于这部分的资料特别的少,大家似乎都不太重视这一块知识,但是我认为这一块的知识特别有必要介绍一下,所以本文会花费很多的笔墨分析它。
  本文参考资料:

  1. ViewModel 概览
  2. ViewModel 的已保存状态模块
  3. ViewModels : A Simple Example
  4. ViewModels: Persistence, onSaveInstanceState(), Restoring UI State and Loaders
  5. Architecture SavedStateHandle — the ViewModel’s complement
  6. Android ViewModels: State persistence — SavedState

  注意:本文ViewModel相关源码均是2.2.0版本。

1. 概述

  按照老规矩,我们先来介绍我们对ViewModel已有的认识吧。在MVVM中,我们都知道ViewModel承担着一个重要角色--数据持有者。这个怎么来理解呢?我们用一个具体的场景来看看。
  假设一个Activity界面有一个RecyclerView正在展示,同时RecyclerView的数据是从服务端请求过来的。当我们的手机第一次进入这个界面,手机是竖屏的,数据正常的请求,RecyclerView拿到数据也正常的展示。但是此时,如果我此时将手机横屏的话,会怎么样呢?会经历如下几个过程:

  1. 原来的Activity对象正常的走生命周期进行销毁。
  2. 新的Activity会被创建,同时生命周期也会走到onResume。
  3. 数据的恢复。如果我们没有使用ViewModel用来存储数据,那么就会重新从服务端请求数据;如果我们ViewModel来存储数据,则不会重新请求数据,而是将竖屏前数据保存,并且成功恢复到横屏的Activity上去。

  我们可以用官方的一张图来简单的描述一下这个过程:


  从这张图里面,我们可以看出,ViewModel的生命周期要比Activity长一点。因为配置更改导致的Activity销毁,ViewModel不会跟着销毁,只有Activity正常finish(比如用户点击back退出,或者退到后台杀掉App等),ViewModel才会销毁。
  上面说的都是大家都知道的知识,本文会从源码角度去分析为什么ViewModel能实现这种效果。
  同时还会介绍SavedStateHandle,这个也在前面也说过了。因为我们都知道,ViewModel不能保存Activity被系统杀掉然后重建的数据,而借助SavedStateHandle,ViewModel便能解决这个问题。说句题外话,估计是Google看到大家怨声载道---为啥ViewModel只能保存配置更改的数据,才随便实现这个类来满足大家。正所谓大家苦ViewModel久矣。

2. 基本使用

  在正式分析源码之前,我们先来看看ViewModel是怎么使用(虽然大家都比较熟悉)。首先,我们创建一个ViewModel子类,类里面有一个LiveData对象:

class MyViewModel : ViewModel() {
    val mNameLiveData = MutableLiveData<String>()
}

  然后我们在Activity里面使用它:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val textView = findViewById<TextView>(R.id.textView)
        val viewModel = ViewModelProvider(this)[MyViewModel::class.java]
        viewModel.mNameLiveData.observe(this, Observer {
            textView.text = it
        })
    }
}

  我们的例子非常简单,这里就不过多的介绍。需要提一句的是,在最新的ViewModel中,以前通过ViewModelProviders.of方法来获取ViewModel已经过时了,现在我们是通过ViewModelProvider方法创建ViewModel对象。通常来说,我们还需要往ViewModelProider构造方法里面传递一个工厂类对象,如下:

        ViewModelProvider(this, object : ViewModelProvider.Factory {
            override fun <T : ViewModel> create(modelClass: Class<T>) =
                MyViewModel() as T
        })

  当然,我们可以不带Factory对象。那么加入Factory对象之后,相比较于以前有什么好处呢?加了Factory之后,我们可以定义构造方法带参的ViewModel。比如说,如果,我们的一个ViewModel构造方法需要带一个id参数,那么我们可以在Factory的create方法里面创建对象直接带进去。
  切忌,我们不要自己创建ViewModel对象,因为自己创建的对象不能保存因为配置更改导致Activity重建的数据,从而完美避开了ViewModel的优点。
  针对于ViewModel的源码分析,本文从Factory这个切入点入手,逐步的分析ViewModel的创建和恢复。

2. ViewModel的创建

  首先,我们先来看ViewModelProivder的构造方法。ViewModelProivder有很多构造方法,不过最终都调到同一个地方:

    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }

  从这个方法中,我们看到两个变量,mFactory就是我们预期的工厂类,用来创建ViewModel对象;mViewModelStore是一个什么东西呢?这个很好理解,mViewModelStore就是用来存储的ViewModel对象的,比如同一个Activity的onCreate方法可能会多次回调,我们在onCreate方法初始化ViewModel的,但是不可能每次onCreate回调都会创建新的ViewModel对象,所以需要有一个东西用来存储的我们之前创建过的ViewModel,这个就是ViewModelStore的作用。同时在后面的内容,ViewModel生命周期比Activity的生命周期长也是因为这个类。
  那么mViewModelStore对象是从哪里传过来,我们清楚的记得构造方法里面我们并没有传这个变量。

    public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
        this(owner.getViewModelStore(), factory);
    }

  我们可以看到从ViewModelStoreOwner获取的,那么那么些类是这个借口的实现类呢?从类图上来看,我们熟悉的ComponentActivity和Fragment实现了这个接口(这里我说的都是androidX包下)。这里,先不对ViewModelStoreOwner进行展开,后面在分析ViewModel的恢复会详细的讲解。
  总之,上面两个变量都非常的重要。
  我们再来看一下get方法,因为真正获取ViewModel对象就是通过这个方法的。

    public <T extends ViewModel> T get(@NonNull Class<T> 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方法没有做什么事情,构造了一个默认的key,然后调用另一个get方法。我们来看看:

    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

  这个get方法总的来说,主要分为以下2个过程:

  1. 先通过key从ViewModelStore(缓存)获取ViewModel对象,如果缓存中存在,直接返回。Activity经过横屏重建之后,返回ViewMode的对象l就是这里返回。
  2. 如果缓存不存在,那么通过Factory创建一个对象,然后放在缓存中,最后返回。

  默认的Facotry,我们这里不用过多的解释,就是简单的调用create方法创建ViewModel对象。那么那个OnRequeryFactoryKeyedFactory又是一个什么东西呢?KeyedFactory跟默认的Facotry比较,其实就是create方法多了一个参数,这个不用过多的纠结。
  这里的核心是OnRequeryFactory,我们发现,当ViewModel是从ViewModelStore获取的,就是回调一次OnRequeryFactoryonRequery方法。这里回调onRequery方法有什么作用呢?这个设计到后面的SavedStateHandle,我们都知道当Activity因为资源限制被系统杀掉之后重新创建会恢复之前的状态,这里的onRequery方法的作用就是--为在这中情况能保存数据做做准备(往SavedStateRegistry里面注册SavedStateProvider)。那么可能有人就要问了ViewModel没有从缓存获取ViewModel,为啥不回调onRequery方法呢?我想说的是,注册最后都是要做的是,只是做工作可能不太一样,所以看不到onRequery方法的回调。这里,我先粗略描述一下,后面的内容会详细的分析这一块的内容,大家先对此有一个印象就行。

3. ViewModel的恢复

  在前面的概述中,我们已经知道ViewModel的生命周期要比Activity长一点。那ViewModel是怎么做到的呢?对于这个问题,我猜大家首先想到的是缓存,并且这个缓存是被static关键字修饰的。正常来说,这个实现方案是没有问题的,我们也能找到具体的例子,比如Eventbus就是这么实现的。
  那么在ViewModel中,这个是怎么实现的呢?我们都知道ViewModel是从一个ViewModelStore缓存里面的获取,我们看了ViewModelStore的源码,发现它的内部并没有通过静态缓存实现。那么它是怎么实现Activity在onDestroy之后(重建),还继续保留已有的对象呢?
  这个我们可以从ComponentActivitygetViewModelStore方法去寻找答案:

    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 nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }

  getViewModeStrore方法的目的很简单,就是获取一个ViewModelStrore对象。那么这个ViewModelStore可以从哪里获取呢?我们从上面的代码中可以找到两个地方:

  1. 从NonConfigurationInstances获取。
  2. 创建一个新的ViewModelStore对象。

  第二点我们不用看,关键是NonConfigurationInstancesNonConfigurationInstances这是什么东西?这里,我简单解释一下NonConfigurationInstances

NonConfigurationInstances其实就是一个Wrapper,用来包装一下因为不受配置更改影响的数据,包括我们非常熟悉的Fragment,比如说,一个Activity上面有一个Fragment,旋转了屏幕导致Activity重新创建,此时Activity跟之前的不是同一个对象,但是Fragment却是同一个。这就是通过NonConfigurationInstances实现的(有兴趣的同学可以了解一下Fragment这个case,还挺重要的,这里就不解释了。)。

  上面说的内容看上去跟我们想要的效果挺像的。是的,ViewModelStore在Activity重建前后能保持同一个对象就是通过NonConfigurationInstances实现的。也就是说在getViewModelStore方法里面,从NonConfigurationInstances获取的ViewModelStore对象其实就是上一个Activity的。同时,我们还可以在ComponentActivity里面看到一段代码:

        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    if (!isChangingConfigurations()) {
                        getViewModelStore().clear();
                    }
                }
            }
        });

  从上面的代码中,我们可以到如果Activity是因为配置更改导致onDestroy方法的回调,并不会清空ViewModelStore里面的内容,这就能保证当Activity因为配置更改导致重建重新创建的ViewModel对象跟之前创建的对象是同一个。反之,如果Activity是正常销毁的话,则不会保存之前创建的ViewModel对象,对应的是ViewModelStore的clear方法调用。其实这个clear方法还跟kotlin里面的协程有关,这里就不过多解释了,有兴趣的同学可以看看ViewModel.viewModelScope
  现在我们来看一下NonConfigurationInstances为啥能保证Activity重建前后,ViewModeStore是同一个对象呢?主要满足大家的好奇心。
  我们直接从ActivityThreadperformDestroyActivity方法去寻找答案(是不是很慌)。我们知道,performDestroyActivity方法最后会回调到Activity的onDestroy方法,我们可以通过这个方法可以找到ActivtyThread在Activity onDestroy之前做了保存操作。

    ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
            int configChanges, boolean getNonConfigInstance, String reason) {
        // ······
        performPauseActivityIfNeeded(r, "destroy");
        // Activity的onStop方法回调
        if (!r.stopped) {
            callActivityOnStop(r, false /* saveState */, "destroy");
        }
        if (getNonConfigInstance) {
            // ······
            // retainNonConfigurationInstances方法的作用就是创建一个对象
            r.lastNonConfigurationInstances= r.activity.retainNonConfigurationInstances();
            // ······
        }
        // ······
        // Activity的onDestroy方法回调
        mInstrumentation.callActivityOnDestroy(r.activity);
        // ······
        return r;
    }

  从上面的代码中,我们可以看到,在Activity的onStop和onDestroy之间,会回调retainNonConfigurationInstances方法,同时记录到ActivityClientRecord中去。这里retainNonConfigurationInstances方法返回的对象就是我们之前看到的NonConfigurationInstances对象。
  那么又在哪里恢复已保存的NonConfigurationInstances对象呢?这个可以从performLaunchActivity方法找到答案。performLaunchActivity方法的作用就是启动一个Activity,Activity重建肯定会调用这个方法。在performLaunchActivity方法里面,调用了Activity的attach方法,在这个方法,Google将已有的NonConfigurationInstances赋值给了新的Activity对象。
  到这里,我们就知道为啥NonConfigurationInstances能保证ViewModelStore在Activity重建前后是同一个对象,同时也知道为啥ViewModel的生命周期比Activity的生命周期要长一点。这里我先不总结,为了方便大家理解,最后我会统一的总结。

4. SavedStateHandle

  通过前面的内容,我们可以知道ViewModel保存和恢复的数据范围仅限于配置更改导致的重建,并不支持因为资源限制导致Activity重建(或许不应该称为重建?)的情况。但是,大家对此的呼声却从来没有停歇,Google因此新增了一个SavedStateHandle类,用来满足我们的要求。
  在正式介绍SavedStateHandle之前,我们先看看onSaveInstanceStateViewModel之间的区别。

(1). onSaveInstanceState和ViewModel的区别

  我们先来看看onSaveInstanceState:

  1. onSaveInstanceState方法能保存的数据的场景:1. 由于资源限制,Activity被系统杀掉;2. 配置更改。
  2. onSaveInstanceState方法只能保存少量简单的数据,大量和复杂数据最后不要放在onSaveInstanceState方法保存。因为onSaveInstanceState方法是通过Bundle保存数据,如果数据的量太大或者太复杂,会阻塞UI线程,从而影响Activity生命周期的执行。

  我们在来看看ViewModel:

  1. ViewModel能保存的数据的场景:配置更改。不支持由于限制导致Activity重建的场景。
  2. ViewModel支持保存大量和复杂的数据,比如说RecyclerView的data。

  从上面的内容,我们可以很明显的看出来各自的缺点和优点。既然Google爸爸在强力推荐View层和Model层分离的原则,那么ViewModel支持限制导致Activity重建的场景就显得尤为重要了。这不SavedStateHandle就出来了。

(2). SavedStateHandle的基本使用

  在分析SavedStateHandle源码之前,我们先来看看SavedStateHandle是怎么使用的吧。
  首先定义一个构造方法带SavedStateHandle参数的ViewModel:

class MyViewModelWithSaveStateHandle(private val saveStateHandle: SavedStateHandle) : ViewModel() {

    // 需要在资源限制导致重建的场景下保存的数据
    // 用LiveData暴露,不能让外部直接通过LiveData去修改内部的值
    val mAgeLiveData: LiveData<Int> = saveStateHandle.getLiveData(KEY_AGE)

    // 普通的数据
    val mNameLiveData = MutableLiveData<String>()


    fun setAge(age: Int) {
        saveStateHandle.set(KEY_AGE, age)
    }

    companion object {
        const val KEY_AGE = "key_age";
    }
}

  其次,通过AbstractSavedStateViewModelFactory或者SavedStateViewModelFactory创建ViewModel对象:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val viewModel = ViewModelProvider(
            this,
            SavedStateViewModelFactory(application, this)
        )[MyViewModelWithSaveStateHandle::class.java]
    }
}

  如果我们的ViewModel构造方法只带一个SavedStateHandle参数或者带有一个Application参数和SavedStateHandle参数,可以直接使用SavedStateViewModelFactory。如果构造方法还带有其他的参数,此时需要继承AbstractSavedStateViewModelFactory实现我们自己的工厂类。在使用AbstractSavedStateViewModelFactory时,我们需要注意一点:create方法带的SavedStateHandle参数一定传递到ViewModel里面去
  通过上面的2步,我们就可以在ViewModel保存由于资源限制导致重建的数据。是不是很简单呢?大家是不是如饥似渴的想要了解内部实现原理了呢?
  现在我们准备分析它的内部实现原理,为啥说是准备呢?因为在正式看SavedStateHandle之前,我们先来看看几个东西,我们只有了解完这几个东西,分析SavedStateHandle才不会懵逼。

(3). SavedState components

  我们先来看一下SavedState components里面几个组件,分别是:SavedStateRegistryOwnerSavedStateRegistryControllerSavedStateRegistrySavedStateProvider。一下子怼上来4个不明所以的类,我猜测大家多多少少都有点懵逼。在这里我先分别这4个类的作用:

  1. SavedStateRegistryOwner:一个接口,有一个getSavedStateRegistry方法,作用是提供SavedStateRegistry对象。该接口主要实现类有Activity和Fragment。
  2. SavedStateRegistryController:SavedStateRegistry的控制类,主要有两个方法:performRestore方法的作用恢复数据;performSave方法主要保存数据。Activity和Fragment直接操作类就是该类。
  3. SavedStateRegistry: 主要是从UI控制器(Activity或者Fragment等)恢复数据,或者需要保存的数据写入UI控制器的Bundle里面;外部可以通过registerSavedStateProvider方法注册SavedStateProvider,这样SavedStateRegistry在保存数据会SavedStateProvider提供的数据。SavedStateRegistryController主要操作类就是该类。
  4. SavedStateProvider: 主要是提供保存和恢复的数据。该接口只有一个saveState方法,主要的作用将需要保存的数据用Bundle包装起来。

  可以用一张图来描述他们之间的关系:


  对这4个类有了大概的认识之后,我们就从源码的角度来看看这个4个类是怎么工作。首先我们来看看ComponentActivity:


public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        LifecycleOwner,
        ViewModelStoreOwner,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner {

    private final SavedStateRegistryController mSavedStateRegistryController =
            SavedStateRegistryController.create(this);
    // ······
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mSavedStateRegistryController.performRestore(savedInstanceState);
        // ······
    }

    @CallSuper
    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        // ······
        mSavedStateRegistryController.performSave(outState);
    }
    // ······
}

  我们从ComponentActivity源码可以看出来,SavedStateRegistryController主要分为两个过程:

  1. 在onCreate方法调用了performRestore方法,主要的作用是恢复已保存的数据。
  2. 在onDestroy方法调用了performSave方法,主要的作用保存数据。

  因此,我们也分为两个过程来分析,分别从保存数据和恢复和数据来分析。

(A). 保存数据

  SvedStateRegistryController的performSave方法做什么,只是调用了SavedStateRegistry的performSave方法。我们直接看SavedStateRegistry的performSave方法:

    void performSave(@NonNull Bundle outBundle) {
        Bundle components = new Bundle();
        if (mRestoredState != null) {
            components.putAll(mRestoredState);
        }
        for (Iterator<Map.Entry<String, SavedStateProvider>> it =
                mComponents.iteratorWithAdditions(); it.hasNext(); ) {
            Map.Entry<String, SavedStateProvider> entry1 = it.next();
            components.putBundle(entry1.getKey(), entry1.getValue().saveState());
        }
        outBundle.putBundle(SAVED_COMPONENTS_KEY, components);
    }

  performSave方法里面主要做了三件事:

  1. 如果mRestoredState不为空,表示之前恢复的数据还没有被消费完,需要将没有消费的数据再一次保存。
  2. 遍历所有注册的SavedStateProvider,将所有的SavedStateProvider提供的数据保存起来。
  3. 将所有保存的数据全部写入到Activity的Bundle对象。注意:outBundle是Activity需要保存的数据Bundle。

  保存的数据过程还是比较简单的,我们再来看看恢复数据的过程。

(B). 恢复数据

  我们先来看看SvedStateRegistryControllerperformRestore方法:

    public void performRestore(@Nullable Bundle savedState) {
        Lifecycle lifecycle = mOwner.getLifecycle();
        if (lifecycle.getCurrentState() != Lifecycle.State.INITIALIZED) {
            throw new IllegalStateException("Restarter must be created only during "
                    + "owner's initialization stage");
        }
        lifecycle.addObserver(new Recreator(mOwner));
        mRegistry.performRestore(lifecycle, savedState);
    }

  相比于performSave方法,performRestore多了一些操作。但是我们不需要去关心,我们的重点还是应该放在SavedStateRegistryperformRestore方法:

    void performRestore(@NonNull Lifecycle lifecycle, @Nullable Bundle savedState) {
        if (mRestored) {
            throw new IllegalStateException("SavedStateRegistry was already restored.");
        }
        if (savedState != null) {
            mRestoredState = savedState.getBundle(SAVED_COMPONENTS_KEY);
        }

        lifecycle.addObserver(new GenericLifecycleObserver() {
            @Override
            public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_START) {
                    mAllowingSavingState = true;
                } else if (event == Lifecycle.Event.ON_STOP) {
                    mAllowingSavingState = false;
                }
            }
        });

        mRestored = true;
    }

  SavedStateRegistryperformRestore方法做的操作从savedState获取之前保存的数据,然后赋值给mRestoredState。到这里,我们可能有疑问了?这里恢复数据只看到获取之前数据,但是没看到赋值到ViewModel。
  其实SavedStateRegistry里面还有一个方法,我们一直没看--consumeRestoredStateForKey方法。我们来看看它的实现:

    public Bundle consumeRestoredStateForKey(@NonNull String key) {
        if (!mRestored) {
            throw new IllegalStateException("You can consumeRestoredStateForKey "
                    + "only after super.onCreate of corresponding component");
        }
        if (mRestoredState != null) {
            Bundle result = mRestoredState.getBundle(key);
            mRestoredState.remove(key);
            if (mRestoredState.isEmpty()) {
                mRestoredState = null;
            }
            return result;
        }
        return null;
    }

  consumeRestoredStateForKey方法的作用就是通过key从mRestoredState获取保存的数据。是不是看到一点苗头了?不错,外部类就是通过该方法来获取之前保存的数据。

(4). SavedStateViewModelFactory

  在看了4个类的实现之后,我们现在回过头来看看SavedStateViewModelFactory在创建ViewModel做了哪些操作。我们直接看create方法:

    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
        boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
        Constructor<T> constructor;
        if (isAndroidViewModel) {
            constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
        } else {
            constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
        }
        // doesn't need SavedStateHandle
        if (constructor == null) {
            return mFactory.create(modelClass);
        }

        SavedStateHandleController controller = SavedStateHandleController.create(
                mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
        try {
            T viewmodel;
            if (isAndroidViewModel) {
                viewmodel = constructor.newInstance(mApplication, controller.getHandle());
            } else {
                viewmodel = constructor.newInstance(controller.getHandle());
            }
            viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
            return viewmodel;
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Failed to access " + modelClass, e);
        } catch (InstantiationException e) {
            throw new RuntimeException("A " + modelClass + " cannot be instantiated.", e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException("An exception happened in constructor of "
                    + modelClass, e.getCause());
        }
    }

  在create方法中,我们需要注意两点:

  1. 通过SavedStateHandleController的create方法创建了一个对象。在这个方法中,我们能找到很多的问题答案,比如说将已保存的数据恢复到SavedStateHandle、向SavedStateRegistry中注册SavedStateProivder以保证能成功保存数据。
  2. 通过反射创建ViewModel的对象。从这里,我们可以知道为啥SavedStateViewModelFactory的使用受限于ViewModel构造方法了。同时从反射中可以出来,将已有数据的SavedStateHandle传递到ViewModel。这就是能解释ViewModel为啥加一个参数,就是保存资源限制导致重建的数据的原因。

  我们再来看看SavedStateHandleController的create方法:

    static SavedStateHandleController create(SavedStateRegistry registry, Lifecycle lifecycle,
            String key, Bundle defaultArgs) {
        Bundle restoredState = registry.consumeRestoredStateForKey(key);
        SavedStateHandle handle = SavedStateHandle.createHandle(restoredState, defaultArgs);
        SavedStateHandleController controller = new SavedStateHandleController(key, handle);
        controller.attachToLifecycle(registry, lifecycle);
        tryToAddRecreator(registry, lifecycle);
        return controller;
    }

  create方法主要创建一个SavedStateHandleController对象,除了创建对象之外,还做了其他几件事:

  1. 调用consumeRestoredStateForKey方法,获取之前已保存的数据。并且将获取的数据创建一个SavedStateHandle,以供ViewModel使用。
  2. 调用attachToLifecycle。这里做的操作主要是将SavedStateHandleSavedStateProvider注册到SavedStateRegistry里面,以保证以下保存数据的时机能成功SavedStateHandle所支持的数据。

  到此,我们将SavedStateHandle里面一个数据者,它内部持有的数据主要是从保存和恢复的,这也是为啥ViewModel需要保存和恢复的数据要通过SavedStateHandle来初始化。

5. 总结

  到此为止,ViewModel相关分析就结束了。在这里,我对本文做一个简单的总结。

  1. ViewModel 和 onSaveInstaceState方法区别在于:ViewModel只能保存因为配置更改导致重建的数据,但是它能保存大量和复杂的数据;onSaveInstaceState能保存配置更改导致重建和资源限制导致重建的数据,但是它只能保存少量简单的数据。ViewModel使用SavedStateHandle能够保存资源限制导致重建的数据。
  2. ViewModel的生命周期之所以比Activity的生命周期生命周期,主要重建之后的Activity用的是之前的ViewStore。ViewModelStore保存的是之前的ViewModel,而ViewStore在配置更改导致重建不会清空已有ViewModel。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342