Jetpack全家桶(第3篇)之ViewModel源码篇

1,728 阅读7分钟

注意:本文ViewModel相关使用及源码均是2.2.0版本

基本使用

详细使用请阅读Jetpack全家桶(第3篇)之ViewModel使用篇

class MyViewModel : ViewModel() {
    val users = MutableLiveData<List<User>>()
    
    //省略
    ````
}

class MyActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        // val model = ViewModelProvider(this).get(MyViewModel::class.java)
        val model = ViewModelProvider(this)[MyViewModel::class.java]

        model.users.observe(this, Observer<List<User>>{ users ->
            // update UI
        })
    }
}

以前通过ViewModelProviders.of方法来获取ViewModel已经过时了,现在我们是通过ViewModelProvider方法创建ViewModel对象。

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

ViewModelProivder的构造方法

/**
 * Creates {@code ViewModelProvider}. This will create {@code ViewModels}
 * and retain them in a store of the given {@code ViewModelStoreOwner}.
 * <p>
 * This method will use the
 * {@link HasDefaultViewModelProviderFactory#getDefaultViewModelProviderFactory() default factory}
 * if the owner implements {@link HasDefaultViewModelProviderFactory}. Otherwise, a
 * {@link NewInstanceFactory} will be used.
 */
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
    this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
            ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
            : NewInstanceFactory.getInstance());
}

/**
 * Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
 * {@code Factory} and retain them in a store of the given {@code ViewModelStoreOwner}.
 *
 * @param owner   a {@code ViewModelStoreOwner} whose {@link ViewModelStore} will be used to
 *                retain {@code ViewModels}
 * @param factory a {@code Factory} which will be used to instantiate
 *                new {@code ViewModels}
 */
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
    this(owner.getViewModelStore(), factory);
}

/**
 * Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
 * {@code Factory} and retain them in the given {@code store}.
 *
 * @param store   {@code ViewModelStore} where ViewModels will be stored.
 * @param factory factory a {@code Factory} which will be used to instantiate
 *                new {@code ViewModels}
 */
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
    mFactory = factory;
    mViewModelStore = store;
}

第1种和第2种构造方法中的this调用的是第3种构造方法,最终都调到同一个地方

//第3种构造方法
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
    mFactory = factory;
    mViewModelStore = store;
}

正常业务我们使用第一种构造方法就够了,第二种构造方法多了一个Factory,那么加入Factory对象之后,相比较于以前有什么好处呢?

加了Factory之后,我们可以定义构造方法带参的ViewModel。比如说,如果,我们的一个ViewModel构造方法需要带一个id参数,那么我们可以在Factory的create方法里面创建对象直接带进去。

//第2种构造方法
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
        this(owner.getViewModelStore(), factory);
    }
    
//使用
ViewModelProvider(thisobject : ViewModelProvider.Factory {
           override fun <T : ViewModel> create(modelClass: Class<T>) =
               // MyViewModel(id) as T
                MyViewModel() as T
        })

切忌,我们不要自己创建ViewModel对象,因为自己创建的对象不能保存因为配置更改导致Activity重建的数据,从而完美避开了ViewModel的优点。

针对于ViewModel的源码分析,本文从Factory这个切入点入手,逐步的分析ViewModel的创建和恢复。

ViewModel的源码

Factory就是我们预期的工厂类,用来创建ViewModel对象;

ViewModelStoreOwner从类图上来看,我们熟悉的ComponentActivity和Fragment实现了这个接口(androidX包下)

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        LifecycleOwner,
        ViewModelStoreOwner,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner {
        //省略
        ````
        }
 
public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener, LifecycleOwner,
        ViewModelStoreOwner, SavedStateRegistryOwner {
         //省略
        ````
        }

/**
 * A scope that owns {@link ViewModelStore}.
 * <p>
 * A responsibility of an implementation of this interface is to retain owned ViewModelStore
 * during the configuration changes and call {@link ViewModelStore#clear()}, when this scope is
 * going to be destroyed.
 */
@SuppressWarnings("WeakerAccess")
public interface ViewModelStoreOwner {
    /**
     * Returns owned {@link ViewModelStore}
     *
     * @return a {@code ViewModelStore}
     */
    @NonNull
    ViewModelStore getViewModelStore();
}

owner.getViewModelStore()得到了ViewModelStore对象,ViewModelStore是一个什么东西呢?这个很好理解,ViewModelStore就是用来存储的ViewModel对象的,比如同一个Activity的onCreate方法可能会多次回调,我们在onCreate方法初始化ViewModel的,但是不可能每次onCreate回调都会创建新的ViewModel对象,所以需要有一个东西用来存储的我们之前创建过的ViewModel,这个就是ViewModelStore的作用。

我们再来看一下ViewModelProvider(this).get(MyViewModel::class.java)中的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个过程:

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

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

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对象。

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

在Activity实例中重写onRetainCustomNonConfigurationInstance、getLastNonConfigurationInstance方法,打印数据,当横竖屏旋转屏幕时,会打印这两个方法,一个是activity销毁时调用,一个是activity重建时调用。

    override fun onRetainCustomNonConfigurationInstance(): Any? {
        Log.i("minfo", "onRetainCustomNonConfigurationInstance")
        return super.onRetainCustomNonConfigurationInstance()
    }

    override fun getLastNonConfigurationInstance(): Any? {
        Log.i("minfo", "getLastNonConfigurationInstance")
        return super.getLastNonConfigurationInstance()
    }

ComponentActivity

    static final class NonConfigurationInstances {
        Object custom;
        ViewModelStore viewModelStore;
    }

    @Override
    @Nullable
    public final Object onRetainNonConfigurationInstance() {
        Object custom = onRetainCustomNonConfigurationInstance();

        ViewModelStore viewModelStore = mViewModelStore;
        if (viewModelStore == null) {
            // No one called getViewModelStore(), so see if there was an existing
            // ViewModelStore from our last NonConfigurationInstance
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                viewModelStore = nc.viewModelStore;
            }
        }

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

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    }

    public Object onRetainCustomNonConfigurationInstance() {
        return null;
    }

Activity类中方法:

    @Nullable
    public Object getLastNonConfigurationInstance() {
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    }

该方法会创建NonConfigurationInstances对象,将已有的viewModelStore存入NonConfigurationInstances。

然后调用getLastNonConfigurationInstance,获取activity中的mLastNonConfigurationInstances实例,其中保存着viewModelStore。

同时,我们还可以在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非配置改变(比如点击返回finish按钮)销毁的话,则不会保存之前创建的ViewModel对象,对应的是ViewModelStore的clear方法调用。 如果配置改变,不做操作,并不会清空ViewModelStore里面的内容.

总结   

  1. ViewModel 和 onSaveInstaceState方法区别在于:ViewModel只能保存因为配置更改导致重建的数据,但是它能保存大量和复杂的数据;onSaveInstaceState能保存配置更改导致重建和资源限制导致重建的数据,但是它只能保存少量简单的数据。ViewModel使用SavedStateHandle能够保存资源限制导致重建的数据。
  2. ViewModel的生命周期之所以比Activity的生命周期生命周期,主要重建之后的Activity用的是之前的ViewStore。ViewModelStore保存的是之前的ViewModel,而ViewStore在配置更改导致重建不会清空已有ViewModel。