从源码角度看ViewModel基本原理

提问

先抛出问题:ViewModel为什么能够在activity发生配置变更时保留状态?

ViewModel从哪里来

直接看源码,看它是怎么拿到viewModel的,也就是viewModel从哪里来的,这里先拿activity中的viewModels来举例,fragment其实也是一样的:

@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)
}

入参我们一般不传,所以先不看,默认是null,直接看最后,return了一个ViewModelLazy :

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(store, factory).get(viewModelClass.java).also{cached =it}
			} else {
                viewModel
            }
        }

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

第一个入参是想要的viewModel类,后两个参数store和factory先忽略,有个印象即可;可以看到还实现了Lazy接口,为什么?因为我们一般使用方式是:by viewModels<xxx>(),注意这里使用了by,也就是用到了委托,获取值这个操作委托给了getValue()

@kotlin.internal.InlineOnly
public inline operator fun <T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value

也就说最后会去拿value这个属性的值,回到ViewModelLazy,它的value属性的get方法里面不复杂,就是先从缓存cached取,没有就去初始化,然后缓存下来,方便下次取。初始化核心代码是:

ViewModelProvider(store, factory).get(viewModelClass.java)

ViewModelProvider看名字就能知道这个类是提供viewModel的,get方法传入了想要的viewModel类,继续跟:

@MainThread
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);
}

继续调用👇,可以简单理解为key是全类名

@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    ViewModel viewModel = mViewModelStore.get(key); // 从Store中根据key取
    if (modelClass.isInstance(viewModel)) { // 如果取出的是想要的viewModel类实例,就返回
				...省略
        return (T) viewModel; // 直接返回
    } else {
        ...省略
    }
		// 如果取出的不是想要的viewModel类实例,要去创建了
    if (mFactory instanceof KeyedFactory) { // 根据fatory的不同一共有2种创建方式
        viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
    } else {
        viewModel = mFactory.create(modelClass);
    }
    mViewModelStore.put(key, viewModel); // 创建完之后放入store中,方便下次取
    return (T) viewModel;
}

这里出现了store和factory,看名字也能知道一个是负责存储的,一个是负责生产的;再看ViewModelLazy、ViewModelProvider的构造函数中都有这2个参数,之间会不会有关联呢?去搜一下这里store和factory赋值的地方,可以发现都是从构造函数传入的,也就是说这里的store和factory之前初始化ViewModelLazy传入的

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

    return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise) 👈store在这里
}

defaultViewModelProviderFactoryviewModelStore是ComponentActivity中的2个方法,简单看一下这个2个方法返回了什么:

@NonNull
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
    ...省略
    if (mDefaultFactory == null) {
        mDefaultFactory = new SavedStateViewModelFactory(
                getApplication(),
                this,
                getIntent() != null ? getIntent().getExtras() : null);
    }
    return mDefaultFactory;
}

@NonNull
@Override
public ViewModelStore getViewModelStore() {
	...省略
    ensureViewModelStore();
    return mViewModelStore;
}

到这里我们知道store和factory都是actvitiy里面的,store就是ViewModelStore,fatory就是SavedStateViewModelFactory,暂时知道这些就可以了,点到为止。

回到上面的get方法继续分析:

@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    ViewModel viewModel = mViewModelStore.get(key); // 从Store中根据key取
    if (modelClass.isInstance(viewModel)) { // 如果取出的是想要的viewModel类实例,就返回
				...省略
        return (T) viewModel; // 直接返回
    } else {
        ...省略
    }
	// 如果取出的不是想要的viewModel类实例,要去创建了
    if (mFactory instanceof KeyedFactory) { // 根据fatory的不同一共有2种创建方式
        viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
    } else {
        viewModel = mFactory.create(modelClass);
    }
    mViewModelStore.put(key, viewModel); // 创建完之后放入store中,方便下次取
    return (T) viewModel;
}

先分析store,直接看ViewModelStore这个类吧,里面也很简单:

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

    Set<String> keys() {
        return new HashSet<>(mMap.keySet());
    }

    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

其实就持有了一个map,key是viewModel类名,value是viewModel实例。

再来分析factory,SavedStateViewModelFactory是一个KeyedFactory,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;
	// 这里用了反射,找出我们ViewModel的构造函数
    if (isAndroidViewModel && mApplication != null) {
        constructor =findMatchingConstructor(modelClass,ANDROID_VIEWMODEL_SIGNATURE);
    } else {
        constructor =findMatchingConstructor(modelClass,VIEWMODEL_SIGNATURE);
    }
    // 一般来说,我们的viewModel构造函数不会有SavedStateHandle参数,所以这里为null,会从这个mFactory创建
    if (constructor == null) {
        return mFactory.create(modelClass);
    }

		// 如果我们的viewModel构造函数有SavedStateHandle参数,就会直接调用构造函数创建,然后返回
    SavedStateHandleController controller = SavedStateHandleController.create(
            mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
    try {
        T viewmodel;
        if (isAndroidViewModel && mApplication != null) {
            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());
    }
}

接下来直接看mFactory.create(modelClass),mFactory只有一个地方赋值,就是SavedStateViewModelFactory 的构造函数:

public SavedStateViewModelFactory(@Nullable Application application,
        @NonNull SavedStateRegistryOwner owner,
        @Nullable Bundle defaultArgs) {
    mSavedStateRegistry = owner.getSavedStateRegistry();
    mLifecycle = owner.getLifecycle();
    mDefaultArgs = defaultArgs;
    mApplication = application;
    mFactory = application != null
            ? ViewModelProvider.AndroidViewModelFactory.getInstance(application)
            : ViewModelProvider.NewInstanceFactory.getInstance();
}

这个2个factory的功能大同小异,AndroidViewModelFactory是反射调用有Application参数的构造函数,NewInstanceFactory是调用无参构造函数,总得来说都是反射创建对象。

到这里可以小结一下:

🤟 by viewModels<xxx>()是通过activity的store来存取viewModel,如果没有初始化的话会通过factory来创建viewModel

ViewModelStore存哪里去

现在知道了viewmodel是从store来的,那为什么activity重建viewModel还在呢,顺藤摸瓜看看store有没有被赋值给其他变量存起来了,在acitivity中只有一处:

@Override
@Nullable
@SuppressWarnings("deprecation")
public final Object onRetainNonConfigurationInstance() {
    
	...省略
	// 注意这里的NonConfigurationInstances是AppcomponentActivity里的内部类
    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.custom = custom;
    nci.viewModelStore = viewModelStore;
    return nci;
}

到这里梳理一下,viewmodel被存在store里面,store被存在这个NonConfigurationInstances里面,那么继续猜想,这个nci又会被存在哪里呢,看看onRetainNonConfigurationInstance被谁调了:

NonConfigurationInstances retainNonConfigurationInstances() {
    Object activity = onRetainNonConfigurationInstance();
    
	...省略

	// 注意这里的NonConfigurationInstances是Activity里的内部类
    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.activity = activity;
    
		...省略
    return nci;
}

这里又被赋值给了nci,套娃了,也就是说:vm←store←nci←nci,那么这个最外层的nci又存到哪去了呢,源码已经到头了,找不到调用方了,只能新建一个demo来打断点继续跟:

 at androidx.fragment.app.FragmentActivity.onRetainNonConfigurationInstance(FragmentActivity.java:569)
	  at android.app.Activity.retainNonConfigurationInstances(Activity.java:2423)
	  at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:4469)
	  at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:4515)
	  at android.app.ActivityThread.handleRelaunchActivityInner(ActivityThread.java:4799)
	  at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4732)
	  at android.app.servertransaction.ActivityRelaunchItem.execute(ActivityRelaunchItem.java:69)
	  at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
	  at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
	  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1816)
	  at android.os.Handler.dispatchMessage(Handler.java:106)
	  at android.os.Looper.loop(Looper.java:193)
	  at android.app.ActivityThread.main(ActivityThread.java:6718)
	  at java.lang.reflect.Method.invoke(Method.java:-1)
	  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
	  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

通过断点可以知道调用处是ActivityThread.performDestroyActivity,那么进去看看

ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
        int configChanges, boolean getNonConfigInstance, String reason) {

		ActivityClientRecord r = mActivities.get(token);

		...省略
		if (getNonConfigInstance) {
        	try {
           		r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
       		}
			...省略
		}

		...省略
		try {
			...省略
       		 mInstrumentation.callActivityOnDestroy(r.activity); // 调用ondestory()
        ...省略
   		}

		...省略
}

这里能发现,之前最外层的nci被赋值给了ActivityClientRecord的lastNonConfigurationInstances,然后才调用onDestory,也就是说在onDestory之前,store就已经被存下来了,存在ActivityClientRecord里面,而ActivityClientRecord存在mActivities里面,mActivities是个map,是ActivityThread的属性,而ActivityThread可是整个进程相关的,这里延伸出2个问题:

  1. 只要进程不销毁,理论上vm可以一直存在,可是我们一般情况下主动销毁activity,vm也会销毁,也就说存在某个时机,vm←store←nci←nci←ActivityClientRecord←ActivityThread,这条链路上的某个节点会被销毁,不然的话vm会一直存在,这肯定是不合理的,稍后再探讨。
  2. 既然和进程相关,那么进程被意外杀死(比如内存不足),那么vm就没了。难道就没办法了吗,不是的,通过配合SavedStateHandle即可保证进程被杀死后还能恢复数据,这是另外的知识点了,这里就不再展开。

知道了store间接被存到了activityThread里,重新创建的activity怎么知道要从activityThread这里去拿呢?我们去看看store被赋值的地方,在activity中store只有一个地方赋值:

void ensureViewModelStore() {
	// 如果store为空,就去创建
    if (mViewModelStore == null) {
		// 注意这里的NonConfigurationInstances是AppComponentActivity的内部类
        NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
		// 先从NonConfigurationInstances中拿
        if (nc != null) {
            mViewModelStore = nc.viewModelStore;
	    }
		// 如果NonConfigurationInstances没有,那么直接new
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
}

最后直接new一个也能理解,也就是上次是正常销毁activity的情况下,直接新建一个store,这也是合理的。那开头先要从getLastNonConfigurationInstance拿是从哪里拿呢,继续跟:

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

// 注意这里的NonConfigurationInstances是Activity的内部类
// activity属性是一个object,实际上是NonConfigurationInstances类,不要被名字误导了
static final class NonConfigurationInstances {
        Object activity; // 实际上是AppComponentActivity的内部类NonConfigurationInstances 
        HashMap<String, Object> children;
        FragmentManagerNonConfig fragments;
        ArrayMap<String, LoaderManager> loaders;
        VoiceInteractor voiceInteractor;
}

也就是说store是从这个mLastNonConfigurationInstances中拿的,梳理一下链路:vm→store→nci→nci,那么看看mLastNonConfigurationInstances赋值的地方,在activity的attach()中:

final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor voiceInteractor,
        Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
		...省略

		mLastNonConfigurationInstances = lastNonConfigurationInstances;

		...省略
}

那attch()又会被activityThread的performLaunchActivity() 调用,进去看看:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
		...省略

		activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);

		...省略
		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 (r.isPersistable()) {
        	mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
    	} else {
        	mInstrumentation.callActivityOnCreate(activity, r.state);
   		}

		...省略
		synchronized (mResourcesManager) {
        mActivities.put(r.token, r); // 注意这里把token和r对应起来了
    }
		...省略
}

这里可以看到,先创建activity,然后把nci赋值进去,然后再调用onCreate,这个nci也是从ActivityClientRecord中拿的,那怎么能证明存的时候那个r和取的时候这个r是同一个r呢,继续看performLaunchActivity被谁调了,一个是startActivityNow(),一个是handleLaunchActivity(),前者是新启动一个activity的时候才被调,那么只可能是后者,继续跟handleLaunchActivity()被谁调了,是handleRelaunchActivityInner()

private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
        List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
        PendingTransactionActions pendingActions, boolean startsNotResumed,
        Configuration overrideConfig, String reason) {

		...省略
		handleDestroyActivity(r.token, false, configChanges, true, reason);
		
		...省略
		handleLaunchActivity(r, pendingActions, customIntent);
}

跟到这里,发现一个熟悉的字眼destory,这里的handleDestroyActivity里面调用了之前说过的performDestroyActivity把nci存在了r里面,然后又把这个r传给了handleLaunchActivity,这样重新创建的activity才能拿到nci→nci→store→vm,形成一个闭环。

总算跟完了,但还没结束,还记得之前发现的问题吗,在正常销毁actvitiy的时候,vm←store←nci←nci←ActivityClientRecord←ActivityThread这条链路必须有某个节点被销毁,不然vm就一直在,一级一级来,首先vm是最底层的,它没法自己把自己销毁掉,继续上一层store,可以看到有clear方法:

public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();
	...省略
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

没错,这里就会把vm给清除掉,那么调用时机呢,继续跟

public ComponentActivity() {
		getLifecycle().addObserver(new LifecycleEventObserver() {
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
            if (event == Lifecycle.Event.ON_DESTROY) {
                // Clear out the available context
                mContextAwareHelper.clearAvailableContext();
                // And clear the ViewModelStore
                if (!isChangingConfigurations()) { // 非配置变更才会走
                    getViewModelStore().clear();
                }
            }
        }
    });
}

在ComponentActivity构造函数中对生命周期进行了监听,监听的是destory事件,如果走到了onDestory,就清除vm,但不是无脑清除,而是要在非配置变更的时候清除,所以加了一层判断,isChangingConfigurations() 也有注释说明,如果activity是配置变更重建,那么返回true,否则返回false,所以正常情况下都会返回false,也就是清除vm。

Fragment又怎么处理

以上只是actvitiy中的viewModel,如果是fragment呢?

获取vm的时候,会去fragment拿store,fragment又去mFragmentManager拿store,mFragmentManager都会有一个专属的FragmentManagerViewModel(以下简称FMVM),用来存储不同fragment的store,所以当前Fragment的vm是存储在当前Fragment的mFragmentManager的FMVM,这里有点拗口,可以看下面的图加强理解。因为当前Fragment的mFragmentManager一般是上级的FragmentManager,而上级又分为Activity和Fragment,所以要分情况讨论:

  1. 如果是附着在activity上的fragment,当前Fragment的mFragmentManager就是activity的FragmentManager,它的FMVM存在Activity的Store里面,而当前Fragment的Store是存在FMVM里面,所以可以理解为当前Fragment的Store存在Activity的Store里。
  2. 如果是附着在fragment中的fragment,当前Fragment的mFragmentManager就是父fragment的ChildFragmentManager,它的FMVM是存在父Fragment的m FragmentManager的FMVM中,所以可以理解为当前Fragment的Store存在父级的FMVM中,从这里也可以发现FMVM不仅可以存Store,也可以存FMVM

下面图中,箭头代表存储的方向:
在这里插入图片描述

总结

最后总结起来就是一个链路:

ViewModel ← ViewModelStore ← ComponentActivity.NonConfigurationInstances ← Activity.NonConfigurationInstances ← ActivityClientRecord ← ActivityThread

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值