ViewModel 在 Activity 中的实现原理

系统源码参考自 Android API 30 Platform,以及 androidx.lifecycle:lifecycle-viewmodel:2.2.0


在文章开始之前,有如下几点疑惑,先记录下来:

  1. ViewModel#onCleared() 的调用时机 ?
  2. ViewModel 与 onSaveInstanceState() 有联系吗?或者有什么区别吗?
  3. ViewModel 是否涉及序列化与反序列化?
  4. 一个 Activity 中是否支持多个 ViewModel ?
  5. ViewModelProvider#get() 的使用时机。

1. 与 ViewModel 相关的类

1.1 ViewModelStoreViewModelStoreOwner

ViewModel 实现的关键就是 ViewModelStore,其内部有一个 HashMap<String, ViewModel> 用于存储多个 ViewModel 实例,即本质上就是一个用来维护多个 ViewModel 实例的 map 容器。

public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();

    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }

    final ViewModel get(String key) {
        return mMap.get(key);
    }

    ...
}

另外,还涉及到 ViewModelStoreOwner 接口。

public interface ViewModelStoreOwner {
    @NonNull
    ViewModelStore getViewModelStore();
}

1.2 androidx.activity.ComponentActivity

androidx.activity.ComponentActivity(源码基于 androidx.activity:activity:1.1.0),就是我们常用的 androidx.appcompat.app.AppCompatActivity的父类。

androidx.activity.ComponentActivity 继承自 androidx.core.app.ComponentActivity,且实现了 ViewModelStoreOwner 接口,同时内部维护了对应的成员变量 mViewModelStoreViewModelStore 类型的)。
在这里插入图片描述

// androidx.activity.ComponentActivity

@NonNull
@Override
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 为 ComponentActivity 中的内部静态类
        NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            // Restore the ViewModelStore from NonConfigurationInstances
            mViewModelStore = nc.viewModelStore;
        }
        // 如果前面都没有获取到 ViewModelStore 实例,则这里直接实例化一个新的
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
    return mViewModelStore;
}

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

getLastNonConfigurationInstance()android.app.Activity 的成员方法。

// android.app.Activity

/**
 * Retrieve the non-configuration instance data that was previously
 * returned by {@link #onRetainNonConfigurationInstance()}.  This will
 * be available from the initial {@link #onCreate} and
 * {@link #onStart} calls to the new instance, allowing you to extract
 * any useful dynamic state from the previous instance.
 *
 * <p>Note that the data you retrieve here should <em>only</em> be used
 * as an optimization for handling configuration changes.  You should always
 * be able to handle getting a null pointer back, and an activity must
 * still be able to restore itself to its previous state (through the
 * normal {@link #onSaveInstanceState(Bundle)} mechanism) even if this
 * function returns null.
 *
 * <p><strong>Note:</strong> For most cases you should use the {@link Fragment} API
 * {@link Fragment#setRetainInstance(boolean)} instead; this is also
 * available on older platforms through the Android support libraries.
 *
 * @return the object previously returned by {@link #onRetainNonConfigurationInstance()}
 */
@Nullable
public Object getLastNonConfigurationInstance() {
  	// mLastNonConfigurationInstances 则是在 Activity 中定义的内部静态类 ComponentActivity.NonConfigurationInstances 类型
    return mLastNonConfigurationInstances != null
            ? mLastNonConfigurationInstances.activity : null;
}

static final class NonConfigurationInstances {
    Object activity;
    HashMap<String, Object> children;
    FragmentManagerNonConfig fragments;
    ArrayMap<String, LoaderManager> loaders;
    VoiceInteractor voiceInteractor;
}

android.app.Activity 内部维护着成员变量 mLastNonConfigurationInstances,其是在 Activity#attach() 中被赋值的,其中 lastNonConfigurationInstances 则是从前一个被销毁的 Activity 传递过来的(具体就在第 2 部分展开讲)。

getLastNonConfigurationInstance() 的作用就是为了从先前的 Activity 实例获取维护的动态的状态,即通过 mLastNonConfigurationInstances 实现,且在初始的 onCreate()onStart() 中可用(会在Activity#performResume() 中被置为 null)。另外,与 getLastNonConfigurationInstance() 对应的是 onRetainNonConfigurationInstance()(后面会说到)。

// android.app.Activity

final void attach(..., NonConfigurationInstances lastNonConfigurationInstances, ...) {
    ...
    mLastNonConfigurationInstances = lastNonConfigurationInstances;
    ...
}

从上述一连串逻辑可以知道,androidx.activity.ComponentActivity#mViewModelStore 本质上对应着 android.app.Activity#mLastNonConfigurationInstances.activity.viewModelStore


2. 在 Activity 的实现原理

接下来要讲的是 ViewModel 在 Activity 的原理,其实也对应着 android.app.Activity#mLastNonConfigurationInstances.activity 在 Activity 销毁重建之间的传递逻辑。

当系统配置发生变更(如切换语言等)、横竖屏切换等导致 Activity 销毁重建时,此时会有如下调用逻辑:

在这里插入图片描述

2.1 ActivityThread#handleRelaunchActivityInner()

上述流程是在应用进程中进行的,主要分为两个部分,原有 Activity 的销毁,以及新 Activity 的重建,相关逻辑集中在 ActivityThread#handleRelaunchActivityInner() 中,

// ActivityThread

/**
 * @param r 需要被销毁的 activity 对应的 ActivityClientRecord 实例
 */
private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
        List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
        PendingTransactionActions pendingActions, boolean startsNotResumed,
        Configuration overrideConfig, String reason) {
    ...
    // 第四个参数即为最终在 performDestroyActivity() 中用到的 getNonConfigInstance,且为 true
    // 变量 r 最终传递到了 performDestroyActivity() 中,并且 r.lastNonConfigurationInstances 被赋值了
    handleDestroyActivity(r.token, false, configChanges, true, reason);
    ...
    // destory 上一个 activity 之后,然后 launch 目标 activity,
    // 从而将之前 Activity 的 retainNonConfigurationInstances() 传递到目标 Activity
    handleLaunchActivity(r, pendingActions, customIntent);
}

2.2 ActivityThread#handleDestroyActivity()

@Override
public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
        boolean getNonConfigInstance, String reason) {
    // 进一步调用 ActivityThread#performDestroyActivity(),且 getNonConfigInstance 从 handleRelaunchActivityInner() 传入,且恒为 true
    ActivityClientRecord r = performDestroyActivity(token, finishing, configChanges, getNonConfigInstance, reason);
    ...
}

ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
        int configChanges, boolean getNonConfigInstance, String reason) {
    // 根据 token 获取到在应用进程维护的对应的 ActivityClientRecord 实例 r,且与需要被销毁的 activity 存在关联,即 r.activity 
    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;
        }
        // Calls Activity#onPause(), "destroy" is a param for "reason"
        performPauseActivityIfNeeded(r, "destroy");
        if (!r.stopped) {
            // Calls Activity#onStop() and Activity#onSaveInstanceState(Bundle), and updates the client record's state.
            callActivityOnStop(r, false /* saveState */, "destroy");
        }
        // 在上游调用方(handleRelaunchActivityInner())中被设置为 true
        if (getNonConfigInstance) {
            try {
                // 调用需要被销毁的 activity.retainNonConfigurationInstances(),从而获取到需要被保留的 ViewModelStore,
                // 且先临时赋值给 r.lastNonConfigurationInstances
                r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
            } catch (Exception e) {
                ...
            }
        }
        try {
            r.activity.mCalled = false;
            // Calls Activity#onDestory()
            mInstrumentation.callActivityOnDestroy(r.activity);
            ...
        } catch (SuperNotCalledException e) {
            ...
        }
        r.setState(ON_DESTROY);
    }
    ...
    return r;
}

需要注意的是,performDestroyActivity() 并不是单纯的处理 Activity#onDestory() 的,而是为了 destory activity 处理一系列逻辑。

其中,需要关注的是 r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances()r.activity 即需要被销毁的 Activity 实例。

// android.app.Activity

NonConfigurationInstances retainNonConfigurationInstances() {
  	// onRetainNonConfigurationInstance() 会在 androidx.activity.ComponentActivity 被重写
    Object activity = onRetainNonConfigurationInstance();
    ...
    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.activity = activity;
    ...
    return nci;
}
// androidx.activity.ComponentActivity

/**
 * Called by the system, as part of destroying an
 * activity due to a configuration change, when it is known that a new
 * instance will immediately be created for the new configuration.  You
 * can return any object you like here, including the activity instance
 * itself, which can later be retrieved by calling
 * {@link #getLastNonConfigurationInstance()} in the new activity
 * instance.
 */
@Override
public final Object onRetainNonConfigurationInstance() {

  	// mViewModelStore 会在 getViewModelStore() 的时候被赋值或者初始化
    ViewModelStore viewModelStore = mViewModelStore;
  	// 如果 viewModelStore 为 null,则表示 getViewModelStore() 从没有被调用过,即上层使用者在当前需要被销毁的 Activity 中
    // 没有实例化过 ViewModelProvider,即进一步表明没有创建过 ViewModel。
    // 但是反过来,没有创建过 ViewModel,getViewModelStore() 也有可能会被调用(具体见第 4 节问题(5))
    if (viewModelStore == null) {
        // No one called getViewModelStore(), so see if there was an existing
        // ViewModelStore from our last NonConfigurationInstance
        NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
      	// 尝试经由 getLastNonConfigurationInstance() 看是否之前有 viewModelStore
        if (nc != null) {
            viewModelStore = nc.viewModelStore;
        }
    }
    if (viewModelStore == null && custom == null) {
        return null;
    }
  	// 走到此处时,viewModelStore 如果还不为 null,则表示之前历代被销毁的 Activity(也包含当前将要被销毁的 Activity) 有维护着一个 viewModelStore 实例,
    // 因此在当前 Activity 被销毁的时候,还需要将其传承下去(即传递到下一个将被重建的 Activity)
    NonConfigurationInstances nci = new NonConfigurationInstances();
    ...
    nci.viewModelStore = viewModelStore;
    return nci;
}

androidx.activity.ComponentActivity#onRetainNonConfigurationInstance()中,主要是为了得到当前需要被销毁的 Activity 维护的 mViewModelStore,如果为 null,则进一步通过父类方法 android.app.Activity#getLastNonConfigurationInstance() 去获取 android.app.Activity#mLastNonConfigurationInstances.activity

android.app.Activity#mLastNonConfigurationInstances 在第 1 部分有讲,是从上一次被销毁的 Activity 传递过来的,而 mLastNonConfigurationInstances.activity 即对应着上一次被销毁的 Activity 维护的 mViewModelStore

因此,androidx.activity.ComponentActivity#onRetainNonConfigurationInstance() 关于 ViewModel 实现的意义,简单的说就是要拿到之前历代被销毁的 Activity(也包含当前将要被销毁的 Activity)维护的且一直传递着的 viewModelStore 实例,使其接着继续传递到下一个被重建的 Activity 中。

然后回到 performDestroyActivity()

r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances()

该处逻辑的作用就是得到 android.app.Activity.NonConfigurationInstances 实例对象(而该对象会间接(NonConfigurationInstances#activity.viewModelStore)持有着之前历代被销毁的 Activity(也包含当前将要被销毁的 Activity)维护的一直传递着的 viewModelStore 实例),并将其赋值到给 r.lastNonConfigurationInstances

小节

ActivityThread#handleDestroyActivity() 的作用就是为了处理要被销毁的 Activity 的,以及在销毁之前,获取到之前历代被销毁的 Activity(也包含当前将要被销毁的 Activity)维护的一直传递着的 viewModelStore 实例。

2.2 ActivityThread#handleLaunchActivity()

@Override
public Activity handleLaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, Intent customIntent) {
    ...
    // 进一步调用 performLaunchActivity(),并把 r 传递进去
    final Activity a = performLaunchActivity(r, customIntent);
    ...
    return a;
}

可以看到,handleLaunchActivity() 会进一步调用 performLaunchActivity(),关键的就是参数 r,其是最开始是从 handleRelaunchActivityInner() 中传递过来的,且在 handleLaunchActivity() 之前,r 被传入 performDestroyActivity() 中,实现了 r.lastNonConfigurationInstances 持有要被传递到重建 Activity 中的 viewModelStore 实例。

/**  Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    Activity activity = null;
    try {
        // 创建新的 Activity 实例
        java.lang.ClassLoader cl = appContext.getClassLoader();
        activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
        ...
    } catch (Exception e) {
        ...
    }
    try {
        ...
        if (activity != null) {
            ...
            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);
            if (customIntent != null) {
                activity.mIntent = customIntent;
            }
            // 清除 old activity 对应的 r.lastNonConfigurationInstances 的引用
            r.lastNonConfigurationInstances = null;
            ...
    } catch (Exception e) {
        ...
    }
    return activity;
}

接着是 performLaunchActivity(),内部处理关于重建 Activity 的逻辑,如创建新的 Activity 实例等,其中,关键的是,会调用新的 Activity 实例的 attach(),并传入 r.lastNonConfigurationInstances,从而实现将之前的 viewModelStore 实例传入到新的 Activity 之中(与之前说的 Activity#attach()相呼应)。

小节

ActivityThread#handleLaunchActivity() 的作用则是处理新 Activity 的重建流程,并把之前维护的 viewModelStore 实例传递到新 Activity 中。


至此, ViewModel 在 Activity 中的实现的关键就是这些了。这一系列的流程,都是在应用进程中处理的,这其实就能说明 ViewModel 的实现的本质,其实就是对象在内存中保留与传递。


3. ViewModel#onCleared() 调用时机

3.1 Old ViewModel 被覆盖时

在使用 ViewModelProvider#get() 获取 ViewModel 实例的时候,最终都会走到如下方法:

// ViewModelProvider.java

@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    ViewModel viewModel = mViewModelStore.get(key);
    // tag 1
    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;
}

其中,方法参数 key,其取值会有两种,第一种是由用户自定义的,即直接调用 ViewModelProvider#get(key, modelClass),此时 key 完全由用户自己控制。还有一种是调用 ViewModelProvider#get(modelClass),此时 key 为默认的,是与 modelClass 的 class name 相关的关键字。

private static final String DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"
    
默认的 key = DEFAULT_KEY + ":" + modelClass.getCanonicalName()

因此对于用户自定义 key 的情况,此时就需要通过 tag 1 处的逻辑,来判断重复调用 ViewModelProvider#get(key, modelClass2) 且 key 与之前的值重复时,通过重复的 key 获取到的 ViewModel1 实例类型是否与 modelClass2 对应,如果不对应的话,则表示要基于 modelClass2 来生成一个新的 ViewModel2 实例保存起来,且把老的 ViewModel1 在 mViewModelStore 中覆盖掉(通过调用 mViewModelStore.put()),然后回调老的 ViewModel1.onCleared()

// ViewModelStore.java

final void put(String key, ViewModel viewModel) {
    ViewModel oldViewModel = mMap.put(key, viewModel);
    if (oldViewModel != null) {
        oldViewModel.onCleared();
    }
}

需要注意,Class#isInstance() 的作用与 instanceof(Java) 是等价的,只不过 instanceof 是关键字,isInstance() 是 Class 的成员方法。因此对于通过相同的 key 来获取 ViewModel 实例时,如果前(子类)后(父类)的 modelClass 存在继承关系时,则会后面的父类 ViewModel 不会被实例化。

open class FatherViewModel: ViewModel()

class SubViewModel: FatherViewModel()

// 同时调用下面两行的逻辑,都使用相同的 key                                    
ViewModelProvider(owner).get("test_key", SubViewModel::class.java) // line 1
ViewModelProvider(owner).get("test_key", FatherViewModel::class.java) // line 2

在 line1 会先生成 SubViewModel 实例对象 model1 并存到 mViewModelStore 中,然后在 line 2 的时候,因为都是用的 test_key,因此从 mViewModelStore 中获取到 model1 之后,再判断 FatherViewModel::class.java.isInstance(model1),此时条件成立,则会直接返回 model1。

3.1 androidx.activity.ComponentActivity 正常 destroy 时

androidx.activity.ComponentActivity 的无参构造方法会通过 lifecycle 回调监听 ON_DESTROY 事件,在判断到 Activity 是正常被销毁时,而不是因为配置发生改变去销毁 Activity 而 ON_DESTROY 时,会正常调用 ViewModelStore#clear()

// androidx.activity.ComponentActivity.java

public ComponentActivity() {
    Lifecycle lifecycle = getLifecycle();
    ...
    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();
                }
            }
        }
    });
    ...
}
// ViewModelStore.java
public final void clear() {
    for (ViewModel vm : mMap.values()) {
    	// ViewModel#clear() 内部会调用 ViewModel#onCleared()
        vm.clear();
    }
    mMap.clear();
}

4. 问题的解答

在文章最前面留了几个小问题,这里统一的处理下。

(1)ViewModel#onCleared() 的调用时机 ?

这个参考上文的第 3 节即可。

(2)ViewModel 与 onSaveInstanceState() 有联系吗?或者有什么区别吗?

可以参考 getLastNonConfigurationInstance() 的注释:
Note that the data you retrieve here should only be used as an optimization for handling 
configuration changes. You should always be able to handle getting a null pointer back, 
and an activity must still be able to restore itself to its previous state (through the
normal onSaveInstanceState(Bundle) mechanism) even if this function returns null.

两者的相同之处,都是为了保留数据。但是使用场景不一样:

- ViewModel 是为了在系统配置改变导致 Activity 销毁重建时维护保留数据用的,数据是维护在应用进程内存中的,
  即动态的;

- onSaveInstanceState(outState: Bundle) 则是为了应对 Activity 被切换到后台时可能会被系统回收的情况,
  涉及到应用进程的销毁重建,会涉及跨进程通信。

因此对于 ViewModel 中维护的数据,也需要在 onSaveInstanceState() 时把必要的数据维护到 outState 中,避免在
切换到后台时被系统回收而导致应用进程重建时数据的丢失。

(3)ViewModel 是否涉及序列化与反序列化?

显然,是不涉及的,数据不涉及跨进程通信,都是直接在当前应用进程的内存中传递。

(4)一个 Activity 中是否支持多个 ViewModel ?

准确的说,是支持多个 key 不一致的 ViewModel 对象,因为 ViewModelStore 的本质是一个维护 Key-ViewModel 的
map 容器。

(5)ViewModelProvider#get() 的使用时机。

显而易见,最早的调用时机是要在 Activity#attach() 之后,这个在 
androidx.activity.ComponentActivity#getViewModelStore()  中也有提到:

Your activity is not yet attached to the Application instance. 
You can't request ViewModel before onCreate call.

那是不是一定要在 onCreate()、onStart() 中调用呢?在 onResume() 中是否可以呢?毕竟在 1.2 小节提到了
android.app.Activity.getLastNonConfigurationInstance() 与 mLastNonConfigurationInstances,
以及 mLastNonConfigurationInstances 的有效周期是在初始的 onCreate()、onStart(),之后会在
Activity#performResume() 中被置为 null。

先说答案,ViewModelProvider#get() 在 onResume() 也能够正常使用。

这是因为 androidx.activity.ComponentActivity#getViewModelStore()  在 onCreate() 之前就会被
系统调用到(具体调用时机有待探究,但是可以通过重写 getViewModelStore() 来证明 ),因此对于借助 
mLastNonConfigurationInstances 的 ViewModelSrtore,并不会受到其会在 Activity#performResume() 
中被置为 null 的影响。
  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值