JetPack之ViewModel

734 阅读8分钟

1、ViewModel

1.1、ViewModel概述

ViewModel类中的注释:

  • ViewModel is a class that is responsible for preparing and managing the data for an Activity or a Fragment. It also handles the communication of the Activity / Fragment with the rest of the application (e.g. calling the business logic classes).

    ViewModel是一个用来准备和管理Activity或Fragment中数据的类,它负责处理Activity和Fragment与应用其他部分的通信。

  • A ViewModel is always created in association with a scope (a fragment or an activity) and will be retained as long as the scope is alive. E.g. if it is an Activity, until it is finished.In other words, this means that a ViewModel will not be destroyed if its owner is destroyed for a configuration change (e.g. rotation). The new owner instance just re-connects to the existing model.

    ViewModel创建时总是和一个Activity或者Fragment关联,只要他们处于存活的状态,ViewModel就会一直存在。ViewModel不会因为owner的配置编程而销毁,新的owner示例会重新连接已经存在的ViewModel。

  • The purpose of the ViewModel is to acquire and keep the information that is necessary for an Activity or a Fragment. The Activity or the Fragment should be able to observe changes in the ViewModel. ViewModels usually expose this information via LiveData or Android Data Binding. You can also use any observability construct from you favorite framework.

    ......,Activity或Fragment应该能够观察ViewModel的改变,ViewModel通常通过LiveData来暴露这些信息,......

  • ViewModel's only responsibility is to manage the data for the UI. It should never access your view hierarchy or hold a reference back to the Activity or the Fragment.

    ViewModel仅仅负责管理界面的数据,它不应该访问视图或者持有 Activity和Fragment的引用。

  • ViewModels can also be used as a communication layer between different Fragments of an Activity. Each Fragment can acquire the ViewModel using the same key via their Activity. This allows communication between Fragments in a de-coupled fashion such that they never need to talk to the other Fragment directly.

    ViewModel还可以作为同一个Activity中不同Fragment之间的通信层,每个Fragment可以通过Activity使用相同的key来获取ViewModel,这使得Fragment之间通过解耦的方式进行通信。

1.2、ViewModel基本使用

  • 自定义ViewModel继承ViewMode, 如果 ViewModel 中需要使用 Context 则继承 AndroidViewModel;
  • 在Activity或者Fragment中通过val vm by viewModels<TVM>()获取ViewModel实例。

2、ViewModel相关源码解析

2.1、ViewModel实例获取

val vm by viewModels<VM>()干了啥?

@MainThread
public inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
    noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
    val factoryPromise = factoryProducer ?: {
        defaultViewModelProviderFactory
    }

    return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
}

viewModelsComponentActivity的一个拓展方法,传入一个Factory工厂实例,不传时默认使用defaultViewModelProviderFactory作为创建ViewModel的工厂,返回Lazy<VM>类型,实际创建了一个ViewModelLazy对象并返回。
这里使用了Kotlin的属性委托。使用 reified 和 inline 将方法内联到调用处,泛型参数直接替换,避免了传递 Class 类型的参数。

2.1.1、Factory

androidx.lifecycle.ViewModelProvider.Factory, 是ViewModelProvider类中的一个接口,用于创建ViewModel实例

public interface Factory {
    <T extends ViewModel> T create(@NonNull Class<T> modelClass);
}

2.1.2、Lazy

接口,用于延迟初始化

public interface Lazy<out T> {
    public val value: T
    public fun isInitialized(): Boolean
}

2.1.3、ViewModelLazy

属性委托对应的类,提供getValue和setValue方法(当属性为 var 时才有setValue)

public class ViewModelLazy<VM : ViewModel> (
    private val viewModelClass: KClass<VM>,
    private val storeProducer: () -> ViewModelStore,
    private val factoryProducer: () -> ViewModelProvider.Factory
) : Lazy<VM> {
    private var cached: VM? = null

    override val value: VM
        get() {
            val viewModel = cached
            return if (viewModel == null) {
                val factory = factoryProducer()
                val store = storeProducer()
                // 通过 ViewModelProvider 去取
                ViewModelProvider(store, factory).get(viewModelClass.java).also {
                    cached = it
                }
            } else {
                viewModel
            }
        }

    override fun isInitialized(): Boolean = cached != null
}

2.1.4、ViewModelProvider

相当于一个工具类,从 ViewModelStore 中取出或者生成对应的 ViewModel。

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;
}

2.2、ViewModel的销毁

2.2.1、ViewModel的clear方法

@MainThread
final void clear() {
    mCleared = true;
    if (mBagOfTags != null) {
        synchronized (mBagOfTags) {
            for (Object value : mBagOfTags.values()) {
                // see comment for the similar call in setTagIfAbsent
                closeWithRuntimeException(value);
            }
        }
    }
    onCleared();
}
// 关闭所有依赖 ViewModel 的 Closeable 实例
private static void closeWithRuntimeException(Object obj) {
    if (obj instanceof Closeable) {
        try {
            ((Closeable) obj).close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
// 提供给用户对自己的 ViewModel 进行相关资源释放清理的时机
protected void onCleared() {
}

ViewModel的clear方法中主要干了两件事情:

  • 遍历 mBagOfTags 这个HashMap,将其中 Closeable 类型的对象调用 close 方法
  • 调用 onCleared 方法执行用户拓展的清理逻辑

其中 mBagOfTagssetTagIfAbsent 方法中被填充内容。

<T> T setTagIfAbsent(String key, T newValue) {
    T previous;
    synchronized (mBagOfTags) {
        previous = (T) mBagOfTags.get(key);
        if (previous == null) {
            mBagOfTags.put(key, newValue);
        }
    }
    T result = previous == null ? newValue : previous;
    if (mCleared) {
        closeWithRuntimeException(result);
    }
    return result;
}

通过 AS 的 Find Usage 查找调用该方法的位置查找到如下代码:

val ViewModel.viewModelScope: CoroutineScope
        get() {
            val scope: CoroutineScope? = this.getTag(JOB_KEY)
            if (scope != null) {
                return scope
            }
            return setTagIfAbsent(JOB_KEY,
                CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate))
        }

internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
    override val coroutineContext: CoroutineContext = context

    override fun close() {
        coroutineContext.cancel()
    }
}

这是 ViewModel ktx 库中拓展方法,可以看到我们平时在 ViewModel 中使用的拓展属性 viewModelScope 实际类型是 CloseableCoroutineScope,并且其 close 方法中取消了所有 Job, 因此我们在 ViewModel 中利用 viewModelScope 启动的协程会在 ViewModel 销毁时自动取消。

2.2.2、clear方法调用时机

同样利用 AS 的 Find Usage 查找到该方法在 ViewModelStore 的 clear 方法中被调用,进一步向上查找,发现在 ComponentActivity 调用了 ViewModelStore 的 clear 方法。在其构造方法中有如下一段代码:

// 注册一个生命周期事件的观察者
getLifecycle().addObserver(new LifecycleEventObserver() {
    @Override
    public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
        if (event == Lifecycle.Event.ON_DESTROY) {
            mContextAwareHelper.clearAvailableContext();
            // 没有发生配置变更, 清理所有关联的ViewModel
            if (!isChangingConfigurations()) {
                getViewModelStore().clear();
            }
        }
    }
});

2.3、Activity配置变更时ViewModel的恢复

2.3.1、保存

在配置变更时会先销毁当前的 Activity 然后重建,Activity 销毁会走到 ActivityThread 中的performDestroyActivity方法中,该方法调用了 Activity 的 retainNonConfigurationInstances方法,并将返回结果保存到 ActivityClientRecord 对象中。

ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
        int configChanges, boolean getNonConfigInstance, String reason) {
    ActivityClientRecord r = mActivities.get(token);
    Class<? extends Activity> activityClass = null;
    if (r != null) {
        activityClass = r.activity.getClass();
        r.activity.mConfigChangeFlags |= configChanges;
        if (finishing) {
            r.activity.mFinished = true;
        }

        performPauseActivityIfNeeded(r, "destroy");

        if (!r.stopped) {
            callActivityOnStop(r, false /* saveState */, "destroy");
        }
        if (getNonConfigInstance) {
            try {
            // 获取 NonConfigurationInstances
                r.lastNonConfigurationInstances
                        = r.activity.retainNonConfigurationInstances();
            } catch (Exception e) {
            }
        }
        
        // ......
        
    return r;
}

Activity的retainNonConfigurationInstances中,首先调用了 onRetainNonConfigurationInstance 方法,该方法在 Activity 中是一个空方法,在 ComponentActivity 中实现了该方法。之后创建了 activityNonConfigurationInstances 实例,并将 onRetainNonConfigurationInstance 的返回值赋值给了成员 activity。

NonConfigurationInstances retainNonConfigurationInstances() {
    // 1、先调用 onRetainNonConfigurationInstance
    Object activity = onRetainNonConfigurationInstance();
    HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
    FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

    mFragments.doLoaderStart();
    mFragments.doLoaderStop(true);
    ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();

    if (activity == null && children == null && fragments == null && loaders == null
            && mVoiceInteractor == null) {
        return null;
    }
    // 2、创建 NonConfigurationInstances 对象并进行赋值
    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.activity = activity;
    nci.children = children;
    nci.fragments = fragments;
    nci.loaders = loaders;
    if (mVoiceInteractor != null) {
        mVoiceInteractor.retainInstance();
        nci.voiceInteractor = mVoiceInteractor;
    }
    return nci;
}

ComponentActivity 中将定义了自己内部的 NonConfigurationInstances,并将 mViewModelStore 实例进行保存。

public final Object onRetainNonConfigurationInstance() {
    // 1、自定义的保存内容
    Object custom = onRetainCustomNonConfigurationInstance();

    ViewModelStore viewModelStore = mViewModelStore;
    if (viewModelStore == null) {
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            viewModelStore = nc.viewModelStore;
        }
    }

    if (viewModelStore == null && custom == null) {
        return null;
    }
    
    // 2、保存 viewModelStore
    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.custom = custom;
    nci.viewModelStore = viewModelStore;
    return nci;
}

顺着调用路径下来,在 Activity 销毁的时候,会将 ViewModelStore 保存到 NonConfigurationInstances 中,而该实例又会保存在 ActivityThread 的 ActivityClientRecord 中。

2.3.1、恢复

从保存过程可知,我们保存的是 ViewModelStore 对象,而 ViewModel 正是保存在 ViewModelStore 中,ComponentActivity 实现了 ViewModelStoreOwner 接口,其 getViewModelStore 方法就是用来获取 ViewModelStore 的,因此回复的过程应该在该方法内实现。

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.");
    }
    ensureViewModelStore();
    return mViewModelStore;
}

直接调用 ensureViewModelStore 然后返回 mViewModelStore 成员,因此ensureViewModelStore方法内应该完成了mViewModelStore的初始化。

void ensureViewModelStore() {
    if (mViewModelStore == null) {
        // 1、获取 NonConfigurationInstances (ComponentActivity 中的)
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance(); // 返回的是Object
        if (nc != null) {
            // 2、获取其中的 ViewModelStore
            mViewModelStore = nc.viewModelStore;
        }
        // 3、没有获取到,对应首次创建,直接new一个
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
}

可以看到通过 getLastNonConfigurationInstance 获取到了 ComponentActivity 内部的 NonConfigurationInstances 类型,然后从中取 viewModelStore。到这里,我们就要了解getLastNonConfigurationInstance如何拿到实例的。

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

getLastNonConfigurationInstance是Activity类中的方法,当mLastNonConfigurationInstances属性不为 null时,返回activity字段(Object)类型。mLastNonConfigurationInstances 是Activity中的NonConfigurationInstances类型。
搜索Activity的代码,发现mLastNonConfigurationInstances是在attach方法中被赋值的。我们知道attach方法是在ActivityThread的performLaunchActivity方法中被调用的。

activity.attach(appContext, this, getInstrumentation(), r.token,
        r.ident, app, r.intent, r.activityInfo, title, r.parent,
        r.embeddedID, r.lastNonConfigurationInstances, config,
        r.referrer, r.voiceInteractor, window, r.configCallback,
        r.assistToken);

可以看到调用时直接传递了ActivityClientRecordlastNonConfigurationInstances。那么这里的 ActivityClientRecord 是从哪里来的呢。我们知道activity的attach是在ActivityThread的performLaunchActivity方法中调用的,因此我们可以在该方法出下一个断点,通过查看函数调用堆栈来查找 ActivityClientRecord 是从哪里传过来的。

  • AS中进行Framework源码的调试

    因为真机上的代码通常被手机厂商修改过,调试时可能对应不上,因此这里选择使用在AS中创建模拟器来调试。比如我们需要调试ActivityThread对应的代码,依赖的SDK版本可能和调试机器上的版本不一致。因此我们可以下载手机对应SDK版本的ActivityThread的源码,之后确定ActivityThread的package(android.app);之后创建一个同名的package,然后再将代码拷贝到该package下即可进行调试。

本人在Android-5.1的模拟器上进行了调试,最终发现在 performRelaunchActivity 方法中通过下面的代码获取了ActivityClientRecord。之后又将其传递给handleLaunchActivity方法,一直传递到performLaunchActivity方法中,最后传到attach方法中。

ActivityClientRecord r = mActivities.get(tmp.token);
// ......
handleLaunchActivity(r, currentIntent);

// mActivities的定义
final ArrayMap<IBinder, ActivityClientRecord> mActivities
    = new ArrayMap<IBinder, ActivityClientRecord>();

可以看到 mActivities 是ActivityThread类的一个成员变量,因此 ActivityClientRecord 是保存在 ActivityThread 中的,因此只要应用程序没有被销毁,这些内容就不会丢失。(正常Destroy应该会从中移除对应的ActivityClientRecord)。