JetPack 之 ViewModel

image.png

Q:ViewModel是什么?
ViewModel是MVVM架构的一个层级,用来联系View和model之间的关系。

官方文档解释:

ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据。

  • 注重生命周期的方式

由于ViewModel的生命周期是作用于整个Activity的,所以就节省了一些关于状态维护的工作,最明显的就是对于屏幕旋转这种情况,以前对数据进行保存读取,而ViewModel则不需要,他可以自动保留数据。

其次,由于ViewModel在生命周期内会保持局部单例,所以可以更方便Activity的多个Fragment之间通信,因为他们能获取到同一个ViewModel实例,也就是数据状态可以共享了。

  • 存储和管理界面相关的数据

ViewModel层的根本职责,就是负责维护界面上UI的状态,其实就是维护对应的数据,因为数据会最终体现到UI界面上。所以ViewModel层其实就是对界面相关的数据进行管理,存储等操作。

Q:请谈谈使用ViewModel的好处?
ViewModel类旨在以注重生命周期的形式存储和管理界面相关的数据。

1.ViewModel类让数据可在发生屏幕旋转等配置更改后继续留存。

2.界面控制器经常需要进行需要一些时间才会返回的异步调用 ,界面控制器需要管理这些调用,并确保系统在其销毁后清理这些调用以避免潜在的内存泄漏。此项管理需要大量的维护工作,并且在为配置更改重新创建对象的情况下,会造成资源的浪费,因为对象可能需要重新发出已经发出过的调用。

3.诸如 Activity 和 Fragment 之类的界面控制器主要用于显示界面数据、对用户操作做出响应或处理操作系统通信(如权限请求)。如果要求界面控制器也负责从数据库或网络加载数据,那么会使类越发膨胀。为界面控制器分配过多的责任可能会导致单个类尝试自己处理应用的所有工作,而不是将工作委托给其他类。以这种方式为界面控制器分配过多的责任也会大大增加测试的难度。

从界面控制器逻辑中分离出视图数据所有权的操作更容易且更高效。

Q:为什么不能在ViewModel中引用view、Lifecycle或任何可能持有Activity Context的类?

ViewModels存在的时间比View或LifecycleOwners的特定实例存在的时间更长——如果您将Activity旋转3次,则您刚刚创建了三个不同的Activity实例,但是只有一个ViewModel实例。

如果 ViewModel 需要 Application 上下文(例如,为了查找系统服务),它可以扩展 AndroidViewModel 类并设置用于接收 Application 的构造函数,因为 Application类会扩展 Context

Q:ViewModel是否对数据使用了持久化?

ViewModel 持有 UI 中的临时数据,但是他们不会进行持久化。一旦相关联的 UI 控制器(fragment/activity)被销毁或者进程停止了,ViewModel 和所有被包含的数据都将被垃圾回收机制标记。

Q:onSaveInstanceState()和ViewModel的区别?

onSaveInstanceState() ->这个回调是为了保存两种情况下的少量 UI 相关的数据:

  • 应用进程在后台时由于内存限制而被终止
  • 配置更改

onSaveInstanceState() 是被系统在 activity stopped但没有 finished 时调用的,而不是在用户显式地关闭 activity 或者在其他情形而导致 finish() 被调用的时候调用。
onSaveInstanceState() 不是被设计来存储类似 bitmap 这样的大的数据的。onSaveInstanceState() 方法被设计用来存储那些小的与 UI 相关的并且序列化或者反序列化不复杂的数据。如果被序列化的对象是复杂的话,序列化会消耗大量的内存。由于这一过程发生在主线程的配置更改期间,它需要快速处理才不会丢帧和引起视觉上的卡顿。

onRetainNonConfigurationInstance()->onSaveInstanceState方法保存数据到Bundle,Bundle是有类型限制和大小限制的,而且也要在主线程序列化和反序列化数据,而onRetainNonConfigurationInstance是保存数据到Object,类型和大小都没有限制。

Fragment.setRetainInstance(true)->创建一个保留 fragment 的好处是这可以保存类似 image 那样的大型数据集或者网络连接那样的复杂对象。

ViewModel->只能在配置更改相关的销毁的情况下保留,而不能在被终止的进程中存留。这使 ViewModel 成为搭配 setRetainInstance(true)(实际上,ViewModel 在幕后使用了一个 fragment 并将 setRetainInstance 方法中的参数设置为 true) 一块使用的 fragment 的一种替代品。

image.png

Q:ViewModel实现机制包含了什么设计模式?

工厂模式:ViewModelProviders 实际相当于一个 简单工厂 模式,根据不同的参数构造不同的 ViewModelProvider;而 Facotry 则是一个 工厂方法 模式可以实现不同的具体 Factory 来构造不同的 ViewModel。

val ViewModel1 = ViewModelProviders.of(activity,factory).get(ViewModel::class.java)
val ViewModel2 = ViewModelProviders.of(fragment,factory).get(ViewModel::class.java)

Q:ViewModel是如何创建的,又是什么时候被销毁?

1. 根据Activity或Fragment使用ViewModelProviders.of方法获得ViewModelProvider实例

创建ViewModelProvider实例需要两个参数ViewModelStoreOwner以及Factory

  • Factory -> 定义了创建ViewModel的行为接口。里面只有一个create方法,用于子类自行决定如何实现一个ViewModel对象的创建。
public interface Factory {
    <T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
  • ViewModelStoreOwer -> ViewModelStore的持有者,提供了获取ViewModelStore实例的方法,ViewModelStore就是个HashMap,通过key来获取ViewModel对象。
public interface ViewModelStoreOwner {
    ViewModelStore getViewModelStore();
}

2. 使用ViewModelProvider.get()创建需要的ViewModel

public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            return (T) viewModel;
        }
        viewModel = mFactory.create(modelClass);
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    }
  • 使用ViewModel Class的canonicalName作为ViewModel在ViewModelStore中的唯一标识。
  • 通过唯一标识,先查询一下ViewModelStore中是否有该ViewModel对象,如果有则直接返回。
  • 如果ViewModelStore中没有该ViewModel对象,则通过Factory工厂类mFactory.create反射创建出ViewModel对象,存入ViewModelStore中,并返回给调用者。

这里如果没有传入Factory则会判断ViewModel类型是否是AndroidViewModel,如果是,则使用默认的AndroidViewModelFactory,如果不是则使用AndroidViewModelFactory的父类NewInstanceFactory,即调用Class的无参构造函数创建ViewModel对象

Q: ViewModel的销毁

  • Activity中的销毁

在ComponentActivity的构造方法中,可以看到通过Lifecycle在ON_DESTROY事件中销毁ViewModel。

public ComponentActivity() {
    getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    if (!isChangingConfigurations()) {
                        // 销毁ViewModel
                        getViewModelStore().clear();
                    }
                }
            }
        });
 }
  • Fragment中的销毁

首先通过代码跟踪到ViewModelStore的clear()方法调用的地方,在FragmentManagerViewModel类的clearNonConfigState()方法中找到了ViewModel的销毁逻辑。
如果继续跟踪代码可以看到代码的调用栈是 FragmentStateManager::destroy() -> (Fragment状态切换)->FragmentManager::dispatchDestroy()->FragmentActivity::onDestory()。

void clearNonConfigState(@NonNull Fragment f) {
    ...
    // Clear and remove the Fragment's ViewModelStore
    ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
    if (viewModelStore != null) {
        // 销毁ViewModel
        viewModelStore.clear();
        mViewModelStores.remove(f.mWho);
    }
}

Q:为什么不同的Fragment使用相同的Activity对象来获取ViewModel,可以轻易的实现ViewModel共享?

它们其实共享的是Activity的ViewModel。因为不同的Fragment使用相同的Activity对象来获取ViewModel,在创建ViewModel之前都会先从Activity提供的ViewModelStore中先查询一遍是否已经存在该ViewModel对象。
实现的逻辑在Fragment的activityViewModel()中:

inline fun <reified VM : ViewModel> Fragment.activityViewModels(
    noinline factoryProducer: (() -> Factory)? = null
) = createViewModelLazy(VM::class, { requireActivity().viewModelStore },
    factoryProducer ?: { requireActivity().defaultViewModelProviderFactory })

在activityViewModels()的实现中可以看到是requireActivity()获取的viewModelStore。以此来实现共享ViewModel。

Q:ViewModel为什么可以实现屏幕旋转而不销毁?

1.Activity实现了ViewModelStoreOwner接口,创建了ViewModelStore对象。

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

2.当Activity意外销毁时,onRetainNonConfigurationInstance函数被回调,在此函数中对ViewModelStore对象进行了保存。

public final Object onRetainNonConfigurationInstance() {
        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
            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;
    }

3.当Activity重建时,onCreate方法中会先获取getLastNonConfigurationInstance,如果其中的ViewModelStore对象不为空,就直接引用,不再重新创建ViewModelStore对象了。

ViewModel 和 LiveData:模式 + 反模式

ViewModels: Persistence, onSaveInstanceState(), Restoring UI State and Loaders

字节高工面试灵魂7问:Android架构组件—ViewModel这些知识点一定要掌握!

Android mvvm框架之ViewModel原理

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容