IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> ViewModel 数据保存和恢复原理源码分析过程 -> 正文阅读

[移动开发]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的启动原理的同学应该都知道它是干嘛的,后面我也会写一篇文章来分析其具体源码
在这里插入图片描述

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重新赋值

总结:

1.ViewModel的保存只会在重启当前Activity才会调用

2.ViewModel存储在ViewModelStore里

3.ViewModelStore在Activity#attach中赋值

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-02-07 13:50:15  更:2022-02-07 13:52:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 14:37:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码