ViewModel 数据保存和恢复原理源码分析过程

要理解ViewModel恢复和保存原理,首先需要看看ViewModel的使用方式

private val vm: TestVM by *viewModels*()
@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)
}
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()
								//这里就是创建ViewModel的地方
                ViewModelProvider(store, factory).get(viewModelClass.java).also{
cached =it
                }
} else {
                viewModel
            }
        }

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

ViewModel 由ViewModelProvider创建,来看看ViewModelProvider的构造方法

public constructor(
    owner: ViewModelStoreOwner
) : this(owner.viewModelStore, defaultFactory(owner))

public constructor(owner: ViewModelStoreOwner, factory: Factory) : this(
    owner.viewModelStore,
    factory
)

ViewModelStoreOwner是一个接口

public interface ViewModelStoreOwner {
    ViewModelStore getViewModelStore();
}

ViewModelStoreOwner接口只有一个方法,getViewModelStore(),返回一个ViewModelStore,

ViewModelStore是存储缓存类

ViewModelStore负责存储ViewModel,虽然已经找到了ViewModel的缓存方式,但是ViewModelStore的存储谁负责?,要分析这个问题,就需要先分析ViewModelStoreOwner的实现类

ComponentActivity

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;
}
void ensureViewModelStore() {
    if (mViewModelStore == null) {
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) { 
            mViewModelStore = nc.viewModelStore;
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
}

当mViewModelStore等于null的时候,会先从NonConfigurationInstances获取缓存,如果获取不到,就直接创建

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

custom可以暂时不管

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

getLastNonConfigurationInstance是Activity的方法,mLastNonConfigurationInstances 如果不为空,就获取mLastNonConfigurationInstances.activity

mLastNonConfigurationInstances是什么?

来看Activity.java

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

看到这里已经大概清楚了,ComponentActivity.NonConfigurationInstances保存ViewModelStore,而Activity.NonConfigurationInstances 保存ComponentActivity.NonConfigurationInstances,那触发保存的方法是什么?

通过代码引用发现Activity.NonConfigurationInstances被引用在ActivityClientRecord.lastNonConfigurationInstances字段中
在这里插入图片描述

在这里插入图片描述

ActivityClientRecord具体是什么,我们这里先不管,找到这里还还不能知道具体保存的问题,继续查看引用,在performDestroyActivity中对NonConfigurationInstances#lastNonConfigurationInstances赋值了,知道Activity的启动原理的同学应该都知道它是干嘛的,后面我也会写一篇文章来分析其具体源码
这里来分析下这个方法,我删除了其他无用代码

final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
public static final class ActivityClientRecord {
Activity.NonConfigurationInstances lastNonConfigurationInstances;
...
}
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
        int configChanges, boolean getNonConfigInstance, String reason) {
...
//如果getNonConfigInstance为true,就对lastNonConfigurationInstances重新赋值
    ActivityClientRecord r = mActivities.get(token);
        if (getNonConfigInstance) {
                r.lastNonConfigurationInstances
                        = r.activity.retainNonConfigurationInstances()
        }
	...
		//既然保存了,为什么这里却移除了?
     mActivities.remove(token);
}

问题:

1.getNonConfigInstance 什么时候true?

2.mActivities移除了当前Activity的ActivityClientRecord,那Activity#NonConfigurationInstances到底是怎么被保存下来的?

3.retainNonConfigurationInstances()是干什么?

先看看问题3

Activity#retainNonConfigurationInstances

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

    if (activity == null && children == null && fragments == null && loaders == null
            && mVoiceInteractor == null) {
        return null;
    }

    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.activity = activity;
  ...
    return nci;
}

onRetainNonConfigurationInstance如果是null,直接返回null,否则赋值到新建的NonConfigurationInstances中并返回

Activity#onRetainNonConfigurationInstance

public Object onRetainNonConfigurationInstance() {
    return null;
}

Activity#onRetainNonConfigurationInstance并没有返回具体的值,来看ComponentActivity#onRetainNonConfigurationInstance

public final Object onRetainNonConfigurationInstance() {
    // Maintain backward compatibility.
    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;
    }

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

onRetainNonConfigurationInstance是final的,被不能被重写,这个方法比较简单就是对viewModelStore重新存储在NonConfigurationInstances

再找到performDestroyActivity的引用链,handleRelaunchActivityInner→handleDestroyActivity→performDestroyActivity

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

    final Intent customIntent = r.activity.mIntent;
    if (!r.paused) {
        performPauseActivity(r, false, reason, null /* pendingActions */);
    }
    if (!r.stopped) {
        callActivityOnStop(r, true /* saveState */, reason);
    }
		//这里对getNonConfigInstance赋值为true
    handleDestroyActivity(r.token, false, configChanges, true, reason);

   ...
		//问题2 在这里也清楚了,虽然ActivityClientRecord被移除了,但是又传入了handleLaunchActivity
    handleLaunchActivity(r, pendingActions, customIntent);
}

说明:handleRelaunchActivityInner在重新启动Activity调用

public Activity handleLaunchActivity(ActivityClientRecord r,
        PendingTransactionActions pendingActions, Intent customIntent) {

		...
    final Activity a = performLaunchActivity(r, customIntent);

		...

    return a;
}

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
   ....
            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);

     
	...
    return activity;
}
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;
    ...
}

handleLaunchActivity→performLaunchActivity→attach,通过引用链发现 NonConfigurationInstances最终被传入到Activity#attach方法中,对mLastNonConfigurationInstances重新赋值

源码分析完成后,通过流程图我们来总结一下

ViewMode保存流程图在这里插入图片描述

ViewModel恢复流程图

在这里插入图片描述

ViewModel获取流程图

在这里插入图片描述

文字总结:

1.ViewModel的恢复和保存发生在Activity实例未发生改变的情况下

2.ViewModel保存节点在destroy的时候

3.ViewModel的恢复节点在Activity#attach方法中

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值