android ViewModel 是如何存储/恢复数据的

QA

  • 配置变更时 ViewModel、onSaveIntanceState 对于数据的保存和恢复有什么区别?
  • onSaveInstanceState和ViewModel在数据缓存方面的区别?
  • ViewModel 是如何在配置变化重建恢复数据的?
  • SaveStateHandle 的作用?

ViewModel 创建

获取实例时通过下面这个方法:

kotlin 复制代码
ViewModelProvider(this).get(MyViewModel::class.java)

public ViewModelProvider(ViewModelStoreOwner owner, Factory factory) {
    // activity/fragment 是直接创建 ViewModelStore 或者 恢复时系统赋值的
    this(owner.getViewModelStore(), factory);
}

public <T extends ViewModel> T get(String key, Class<T> modelClass) {
    //ViewModelStore 内部维护了一个用于储存VM的HashMap。
    ViewModel viewModel = mViewModelStore.get(key);

    if (modelClass.isInstance(viewModel)) {
        if (mFactory instanceof OnRequeryFactory) {
            ((OnRequeryFactory) mFactory).onRequery(viewModel);
        }
        // 缓存里有直接结束
        return (T) viewModel;
    } else {
    }
    if (mFactory instanceof KeyedFactory) {
        //如果无缓存直接通过 factory 创建
        viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
    } else {
        viewModel = (mFactory).create(modelClass);
    }
    //然后放到 缓存里
    mViewModelStore.put(key, viewModel);
    return (T) viewModel;
}

// 默认的 factory 创建实例。
private fun <T : ViewModel> create(modelClass: Class<T>, app: Application): T {
    return if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
        try {
            modelClass.getConstructor(Application::class.java).newInstance(app)
        } catch (e: NoSuchMethodException) {
            RuntimeException("Cannot create an instance of $modelClass", e)
        }
    } else super.create(modelClass)
}
  • ViewModelStoreOwner: 接口,AppCompatActivity/Fragment实现了该接口。
  • vm 的实例是 ViewModelStore 中拿的,ViewModelStor 内部维护了一个用于储存VM的HashMap。
  • ViewModelStore 是被 activity/fragment 持有的,每个 Activity(Fragment) 都会拥有一个 ViewModelStore 实例。

所以关键问题是看 ViewModelStore 创建路径。

ViewModel 的恢复

ComponentActivity getViewModelStore:

ini 复制代码
public ViewModelStore getViewModelStore() {
    if (mViewModelStore == null) {
        NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            // Restore the ViewModelStore from NonConfigurationInstances
            mViewModelStore = nc.viewModelStore;
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
    return mViewModelStore;
}
  • 如果有的话直接从NonConfigurationInstances获取。
  • 否则,创建一个新的ViewModelStore对象。

NonConfigurationInstances是什么东西?NonConfigurationInstances 用来缓存不受配置更改影响的数据,

同时,我们还可以在ComponentActivity里面看到一段代码:

less 复制代码
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();
            }
        }
    }
});

如果Activity是因为配置更改导致onDestroy方法的回调,并不会清空ViewModelStore里面的内容, 即 ViewModel 的缓存。

NonConfigurationInstances 的赋值是在 Activity 的 attach 方法,系统将上次的 NonConfigurationInstances赋值给了新的Activity对象。

ViewModel 的保存

NonConfigurationInstances为啥能保证Activity重建前后,ViewModeStore是同一个对象呢?

performDestroyActivity方法最后会回调到Activity的onDestroy方法,我们可以通过这个方法可以找到ActivtyThread在Activity onDestroy之前做了保存操作。

ini 复制代码
 public final Object onRetainNonConfigurationInstance() {
     //配置变化时缓存viewModelStore 到 NonConfigurationInstances
    ViewModelStore viewModelStore = mViewModelStore;
    if (viewModelStore == null) {
        // No one called getViewModelStore(), so see if there was an existing
        // ViewModelStore from our last NonConfigurationInstance
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            viewModelStore = nc.viewModelStore;
        }
    }

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

ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason) {
    if (getNonConfigInstance) {
        // ······
        // retainNonConfigurationInstances方法的作用就是创建一个对象
        r.lastNonConfigurationInstances= r.activity.retainNonConfigurationInstances();
        // ······
    }
    // Activity的onDestroy方法回调
    mInstrumentation.callActivityOnDestroy(r.activity);
    return r;
}

配置变化时会回调onRetainNonConfigurationInstance方法,将mViewModelStore保存在NonConfigurationInstances,同时performDestroyActivity时将NonConfigurationInstances记录到ActivityClientRecord中去。

小结:VM -> ViewModelStore->NonConfigurationInstances -> ActivityThread 中的 ActivityClientRecord。中间通过 activity 来处理保存、恢复。

onSaveInstanceState和ViewModel

两者在数据处理方面的不同:

  • ViewModel最终是交给 ActivityThread 中的 ActivityClientRecord 暂存的,进程被回收ViewModel 也就被回收。
  • ViewModel能恢复因配置变更导致重建的数据,并不能恢复因资源限制(比如内存限制,电量限制等)进程杀死导致的数据恢复。
  • onSaveInstanceState 可以保存因系统限制进程被杀或者配置变化的数据,这些数据存储在内存中,而不是硬盘上,由android 系统来管理。
  • ViewModel支持保存大量和复杂的数据,比如说RecyclerView的data(ActivityThread持有)。

SavedStateHandle

ViewModel 通过SavedStateHandle类处理数据与onSaveInstanceState()的方式没有任何区别

kotlin 复制代码
class MyViewModel(private val saveStateHandle: SavedStateHandle) : ViewModel() {
    val info = saveStateHandle.get<String>("KEY_INFO") // 取值
    val infoFlow = saveStateHandle.getStateFlow("KEY_INFO", "") // 以 flow 形式

    fun setInfo(info: String) { // 设置值
        saveStateHandle.set("KEY_INFO", info)
    }
}

//通过AbstractSavedStateViewModelFactory或者SavedStateViewModelFactory创建ViewModel对象:
private val viewModel by lazy {
    ViewModelProvider(
        this, SavedStateViewModelFactory(application, this)
    )[MyViewModel::class.java]
}

比如在Fragment中是如何save的?

scss 复制代码
// 触发销毁时
void performSaveInstanceState(Bundle outState) {
    ......
    onSaveInstanceState(outState);
    mSavedStateRegistryController.performSave(outState);
    ......
}

//也是保存到bundle里
void performSave(@NonNull Bundle outBundle) {
    Bundle components = new Bundle();
    if (mRestoredState != null) {
        components.putAll(mRestoredState);
    }
    for (Iterator<Map.Entry<String, SavedStateProvider>> it =
            mComponents.iteratorWithAdditions(); it.hasNext(); ) {
        Map.Entry<String, SavedStateProvider> entry1 = it.next();
        components.putBundle(entry1.getKey(), entry1.getValue().saveState());
    }
    outBundle.putBundle(SAVED_COMPONENTS_KEY, components);
}
相关推荐
千里马学框架2 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台2 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone2 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc2 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo2 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077002 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼2 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone3 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen3 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone3 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui