ViewModel原理分析

认识 ViewModel

ViewModel 是一种用来存储和管理UI相关数据的类。

ViewModel 的作用可以从两个方面去理解:

  • UI界面控制器:在最初的MVC模式中,由于 Activity / Fragment 承担的职责过重,因此在后续的 MVP、MVVM 模式中,选择将 Activity / Fragment 中与视图无关的职责抽离出来,在 MVP 模式中叫作 Presenter,在 MVVM 模式中叫作 ViewModel。使用 ViewModel 来承担界面控制器的职责,并且配合 LiveData / Flow 实现数据驱动。
  • 数据存储:由于 Activity 存在因配置变更销毁重建的机制,会造成 Activity 中的所有瞬态数据丢失,例如网络请求得到的用户信息、视频播放信息或者异步任务都会丢失。而 ViewModel 的特点是生命周期长于 Activity ,因此能够应对 Activity 因配置变更而重建的场景,在重建的过程中恢复 ViewModel 数据,从而降低用户体验受损。

ViewModel 生命周期示意图:

使用 ViewModel

使用步骤:

  1. 自定义 ViewModel 继承 ViewModel。
  2. 在自定义 ViewModel 中编写获取UI数据的逻辑。
  3. 配合使用 LiveData / Flow 实现数据驱动。
  4. 在 Activity / Fragment中 获取 ViewModel 实例。
  5. 监听或收集 ViewModel 中的 LiveData / Flow 数据,进行对应的UI更新。

简单示例:

class TestFlowViewModel : ViewModel() {
    private val _state: MutableStateFlow<Int> = MutableStateFlow(0)
    val state: StateFlow<Int> get() = _state
    private val _live: MutableLiveData<String> = MutableLiveData<String>()
    val live: LiveData<String> get() = _live
    
    fun test() {
        _live.value = "1"
        for (state in 1..5) {
            viewModelScope.launch(Dispatchers.IO) {
                delay(100L * state)
                _state.emit(state)
            }
        }
    }
}

Activity / Fragment 中相关代码:

private val viewModel: TestFlowViewModel by viewModels()

lifecycleScope.launch {
    launch {
        repeatOnLifecycle(Lifecycle.State.STARTED) {
            viewModel.state.collect {
                Log.d(TAG, "state: $it ")
            }
        }
    }
    delay(100)
    viewModel.test()
}
viewModel.live.observe(this) {
    Log.d(TAG, "it: $it")
}

在 Activity / Fragment中 获取 ViewModel 实例有两种方式,一种是通过ViewModelProvider获取,也key自定义ViewModelProvider.Factory,

private val viewModel = ViewModelProvider(this).get(TestFlowViewModel::class.java)

另一种就是上面示例里面使用的方式:使用 Kotlin by 委托属性,本质上是间接使用了 ViewModelProvider。

@MainThread
public inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
    noinline extrasProducer: (() -> CreationExtras)? = null,
    noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
    val factoryPromise = factoryProducer ?: {
        defaultViewModelProviderFactory
    }

    return ViewModelLazy(
        VM::class,
        { viewModelStore },
        factoryPromise,
        { extrasProducer?.invoke() ?: this.defaultViewModelCreationExtras }
    )
}

public class ViewModelLazy<VM : ViewModel> @JvmOverloads constructor(
    private val viewModelClass: KClass<VM>,
    private val storeProducer: () -> ViewModelStore,
    private val factoryProducer: () -> ViewModelProvider.Factory,
    private val extrasProducer: () -> CreationExtras = { CreationExtras.Empty }
) : 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,
                    extrasProducer()
                ).get(viewModelClass.java).also {
                    cached = it
                }
            } else {
                viewModel
            }
        }

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

分析 ViewModel 原理

ViewModel 创建过程

上面说到了创建 ViewModel 实例的方法最终都是通过 ViewModelProvider 完成的。ViewModelProvider 可以理解为创建 ViewModel 的工具类,它需要 2 个参数:

  • ViewModelStoreOwner: 它对应于 Activity / Fragment 等持有 ViewModel 的宿主,它们内部通过 ViewModelStore 维持一个 ViewModel 的映射表,ViewModelStore 是实现 ViewModel 作用域和数据恢复的关键;
  • Factory: 它对应于 ViewModel 的创建工厂,缺省时将使用默认的 NewInstanceFactory 工厂来反射创建 ViewModel 实例。

创建 ViewModelProvider 工具类后,通过 get() 方法来获取 ViewModel 的实例。get() 方法内部首先会从 ViewModelStore 中取缓存,没有缓存才会通过 ViewModel 工厂创建实例再缓存到 ViewModelStore 中。

    @MainThread
    public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
        // 先从 ViewModelStore 中取缓存
        val viewModel = store[key]
        // 存在 ViewModel,直接返回
        if (modelClass.isInstance(viewModel)) {
            (factory as? OnRequeryFactory)?.onRequery(viewModel!!)
            return viewModel as T
        } else {
            @Suppress("ControlFlowWithEmptyBody")
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        val extras = MutableCreationExtras(defaultCreationExtras)
        extras[VIEW_MODEL_KEY] = key
        // AGP has some desugaring issues associated with compileOnly dependencies so we need to
        // fall back to the other create method to keep from crashing.
        // 不存在则使用 ViewModel 工厂创建实例,并放入 ViewModelStore
        return try {
            factory.create(modelClass, extras)
        } catch (e: AbstractMethodError) {
            factory.create(modelClass)
        }.also { store.put(key, it) }
    }

ViewModelStore 是 ViewModel 存储器,内部通过 LinkedHashMap 存储 ViewModel。

ViewModelStoreOwner 是一个接口,ViewModelStore 的持有者是ViewModelStoreOwner 的实现类,包括有ComponentActivity和Fragment,它们内部都保存着一个 ViewModelStore。

interface ViewModelStoreOwner {

    /**
     * The owned [ViewModelStore]
     */
    val viewModelStore: ViewModelStore
}

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
    ContextAware,
    LifecycleOwner,
    ViewModelStoreOwner ... {
		
    private ViewModelStore mViewModelStore;
    private ViewModelProvider.Factory mDefaultFactory;

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

正因为 ViewModel 宿主内部都保存着一个 ViewModelStore ,因此在同一个宿主上重复调用 ViewModelProvider#get() 返回同一个 ViewModel 实例,这也就做到了 fragment 共享 activity 的ViewModel 实例以及 fragment 之间共享 ViewModel。

为什么 Activity 在屏幕旋转重建后可以恢复 ViewModel?

上面说到 ViewModel 其实是被保存在 ViewModelStore 里,所以 Activity 在屏幕旋转重建后恢复 ViewModel 其实是重新获取到了原有的 ViewModelStore。那么 Activity 里的 ViewModelStore 究竟是怎么生成的呢?

ViewModelStore 的生成

ComponentActivity 在构造函数中设置了对 lifecycle 的监听,当 activity 的生命周期变化时会调用ensureViewModelStore方法,确保 ViewModelStore 的存在。

        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                ensureViewModelStore();
                getLifecycle().removeObserver(this);
            }
        });

    @SuppressWarnings("WeakerAccess") /* synthetic access */
    void ensureViewModelStore() {
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
    }

当 ViewModelStore 不存在时,ensureViewModelStore 方法会进行 ViewModelStore 的生成,首先通过 getLastNonConfigurationInstance 获取到 NonConfigurationInstances,并从中获取 ViewModelStore,若 ViewModelStore 仍未空,则创建 new 一个新的。

看到这,知道了 ViewModelStore 的生成来源于两处,一处为 NonConfigurationInstances 中获取,另一处是新创建。那么 Activity 在屏幕旋转重建后获取到了原有的 ViewModelStore 是不是就是从 NonConfigurationInstances 中获取的呢?

接下去看一下 Activity 在屏幕旋转重建后,ViewModelStore 都干什么去了呢?

Activity 因配置变更而重建时(比如屏幕旋转),可以将页面上的数据或状态可以定义为 2 类:

  • 配置数据:例如窗口大小、多语言字符、多主题资源等,当设备配置变更时,需要根据最新的配置重新读取新的数据,因此这部分数据在配置变更后便失去意义,自然也就没有存在的价值;
  • 非配置数据:例如用户信息、视频播放信息、异步任务等非配置相关数据,这些数据跟设备配置没有一点关系,如果在重建 Activity 的过程中丢失,不仅没有必要,而且会损失用户体验(无法快速恢复页面数据,或者丢失页面进度)。
    基于以上考虑,Activity 是支持在设备配置变更重建时恢复非配置数据的,源码中存在 NonConfiguration 字眼的代码,就是与这个机制相关的代码。

当 Activity 因配置变更而重建时,ActivityThreadhandleRelaunchActivity 方法会执行,先 handleDestroyActivity 销毁 Activity,然后 handleLaunchActivity 重建 Activity。

Activity 销毁过程

在 handleDestroyActivity 方法里执行到 performDestroyActivity 时,会执行 activity 的 retainNonConfigurationInstances 方法,将非配置数据临时存储在当前 Activity 的 ActivityClientRecord(当前进程内存)。

    void performDestroyActivity(ActivityClientRecord r, boolean finishing,
            int configChanges, boolean getNonConfigInstance, String reason) {
        Class<? extends Activity> activityClass = null;
        if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
        activityClass = r.activity.getClass();
        r.activity.mConfigChangeFlags |= configChanges;
        if (finishing) {
            r.activity.mFinished = true;
        }

        performPauseActivityIfNeeded(r, "destroy");

        if (!r.stopped) {
            callActivityOnStop(r, false /* saveState */, "destroy");
        }
        if (getNonConfigInstance) {
            try {
                // 将非配置数据临时存储在 ActivityClientRecord
                r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
            } catch (Exception e) {
                if (!mInstrumentation.onException(r.activity, e)) {
                    throw new RuntimeException("Unable to retain activity "
                            + r.intent.getComponent().toShortString() + ": " + e.toString(), e);
                }
            }
        }
        ...
    }

// 获取 Activity 的非配置相关数据
NonConfigurationInstances retainNonConfigurationInstances() {
    // 构造 Activity 级别的非配置数据
    Object activity = onRetainNonConfigurationInstance();
    ...
    // 构造 Fragment 级别的费配置数据数据
    FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
    ...
    // 构造并返回 NonConfigurationInstances 非配置相关数据类
    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.activity = activity;
    nci.fragments = fragments;
    ...
    return nci;
}

// 默认返回 null,由 Activity 子类定义
public Object onRetainNonConfigurationInstance() {
    return null;
}

看一下onRetainNonConfigurationInstance在 ComponentActivity 中的实现:

    public final Object onRetainNonConfigurationInstance() {
        // Maintain backward compatibility.
        Object custom = onRetainCustomNonConfigurationInstance();

        ViewModelStore viewModelStore = mViewModelStore;
        if (viewModelStore == null) {
            // No one called getViewModelStore(), so see if there was an existing
            // ViewModelStore from our last NonConfigurationInstance
            // 这一个 if 语句是处理异常边界情况: 
            // 如果重建的 Activity 没有调用 getViewModelStore(),那么旧的 Activity 中的 ViewModel 并没有被取出来, 
            // 因此在准备再一次存储当前 Activity 时,需要检查一下旧 Activity 传过来的数据。
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                viewModelStore = nc.viewModelStore;
            }
        }
        
        // ViewModelStore 为空说明当前 Activity 和旧 Activity 都没有 ViewModel,没必要存储和恢复
        if (viewModelStore == null && custom == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        // 保存 ViewModelStore 对象
        nci.viewModelStore = viewModelStore;
        return nci;
    }

看到了 ViewModelStore 实例的保存,就这样,当 Activity 被销毁时,ViewModelStore 实例被保存进了 NonConfigurationInstances 中,进而被临时存储在了 ActivityClientRecord 里。

Activity 重建过程

在 handleLaunchActivity 方法里执行到 performLaunchActivity 时,会执行 activity 的attach方法,并将 ActivityClientRecord中的 NonConfigurationInstances 传入。

// 在 Activity#attach() 中传递旧 Activity 的数据
NonConfigurationInstances mLastNonConfigurationInstances;

final void attach(Context context, ActivityThread aThread,
    ...
    NonConfigurationInstances lastNonConfigurationInstances) {
    ...
    mLastNonConfigurationInstances = lastNonConfigurationInstances;
    ...
}

至此,旧 Activity 的数据就传递到新 Activity 的成员变量 mLastNonConfigurationInstances 中了,ViewModelStore 将从 mLastNonConfigurationInstances 中获取。

ViewModel 数据清除

ViewModel 的数据又是在什么时候会被清除呢?

ViewModel 中有一个 clear 方法用于数据清除,在 ViewModelStore#clear() 方法中被调用。

上面提到 ViewModelStore 的生成是 ComponentActivity 在构造函数中设置了对 lifecycle 的监听,当 activity 的生命周期变化时会调用ensureViewModelStore方法,确保 ViewModelStore 的存在。

ComponentActivity 在构造函数中设置对 lifecycle 的监听可不只有这一处,ViewModel 数据的清除也是通过对 lifecycle 的监听,当 Activity 进入 destroyed 状态,并且 Activity 不处于配置变更重建的阶段,将调用 ViewModelStore#clear() 清除 ViewModel 数据。

        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();
                    }
                }
            }
        });
相关推荐
数据猎手小k1 小时前
AndroidLab:一个系统化的Android代理框架,包含操作环境和可复现的基准测试,支持大型语言模型和多模态模型。
android·人工智能·机器学习·语言模型
你的小102 小时前
JavaWeb项目-----博客系统
android
风和先行2 小时前
adb 命令查看设备存储占用情况
android·adb
@小博的博客2 小时前
C++初阶学习第十弹——深入讲解vector的迭代器失效
数据结构·c++·学习
AaVictory.3 小时前
Android 开发 Java中 list实现 按照时间格式 yyyy-MM-dd HH:mm 顺序
android·java·list
南宫生3 小时前
贪心算法习题其四【力扣】【算法学习day.21】
学习·算法·leetcode·链表·贪心算法
懒惰才能让科技进步4 小时前
从零学习大模型(十二)-----基于梯度的重要性剪枝(Gradient-based Pruning)
人工智能·深度学习·学习·算法·chatgpt·transformer·剪枝
love_and_hope4 小时前
Pytorch学习--神经网络--搭建小实战(手撕CIFAR 10 model structure)和 Sequential 的使用
人工智能·pytorch·python·深度学习·学习
似霰4 小时前
安卓智能指针sp、wp、RefBase浅析
android·c++·binder
大风起兮云飞扬丶4 小时前
Android——网络请求
android