前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ViewModel创建过程详解

ViewModel创建过程详解

作者头像
烧麦程
发布2022-05-10 20:48:31
1.2K0
发布2022-05-10 20:48:31
举报
文章被收录于专栏:半行代码半行代码

ViewModel 获取通过

代码语言:javascript
复制
ViewModelProvider(activity)[VM::class.java]

今天来探究下 ViewModel 具体是如何创建出来的。

创建流程

首先会创建一个 ViewModelProvider ,ViewModelProvider 的构造函数如下:

代码语言:javascript
复制
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
 this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
}

public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
 }

ViewModelProvider 里面包括两部分:

  • factory ViewModelProvider.Factory 负责 ViewModel 对象的创建
  • ViewModelStore 负责 ViewModel 的存储

ViewModelStore 从 ViewModelStoreOwner 中获取。

ViewModelStoreOwner 是一个接口:

代码语言:javascript
复制
public interface ViewModelStoreOwner {
 @NonNull
 ViewModelStore getViewModelStore();
}

FragmentActivity 为例,

代码语言:javascript
复制
@Override
public ViewModelStore getViewModelStore() {
 if (mViewModelStore == null) {
  NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
  if (nc != null) {
   mViewModelStore = nc.viewModelStore;
  }
  if (mViewModelStore == null) {
   mViewModelStore = new ViewModelStore();
  }
 }
 return mViewModelStore;
}

如果 getLastNonConfigurationInstance 不为 null, 则取 NonConfigurationInstances 的 ViewModelStore。创建一个新的ViewModelStore。

获取 ViewModel 示例也是通过 ViewModelProvider 的get 方法:

代码语言:javascript
复制
  @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

这时候通过 ViewModelStore 获取 ViewModel 的逻辑:

代码语言:javascript
复制
final ViewModel get(String key) {
        return mMap.get(key);
}

ViewModelStore 内部其实是一个 Map 结构:

代码语言:javascript
复制
public class ViewModelStore {
  private final HashMap<String, ViewModel> mMap = new HashMap<>();

    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }

    final ViewModel get(String key) {
        return mMap.get(key);
    }
}

回到 ViewModelProvider#get:

当 ViewModelStore 里面存在 ViewModel 的时候,会直接复用这个 ViewModel 实例。

如果不存在,则会使用 factory 去创建我们的 ViewModel 实例。

创建完成后,会把 ViewModel 实例存在 ViewModelStore 中。

这几层的分工结构可以用下图表示:

构造ViewModel

看到这里,我们比较关心的点剩下的就是如何使用 Factory 创建 ViewModel,以及 Factory 是怎么来的,有哪些 Factory。

FragmentActivity默认的Factory

我们看下默认情况下,ViewModelStoreOwner 是 FragmentActivity 或者 Activity 的时候,是怎么使用 Factory 的:

代码语言:javascript
复制
owner instanceof HasDefaultViewModelProviderFactory
 ? ((HasDefaultViewModelProviderFactory) owner)
    .getDefaultViewModelProviderFactory()
 : NewInstanceFactory.getInstance()
代码语言:javascript
复制
 
  // Activity
  @NonNull
    @Override
    public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
        if (getApplication() == null) {
            throw new IllegalStateException("Your activity is not yet attached to the "
                    + "Application instance. You can't request ViewModel before onCreate call.");
        }
        if (mDefaultFactory == null) {
            mDefaultFactory = new SavedStateViewModelFactory(
                    getApplication(),
                    this,
                    getIntent() != null ? getIntent().getExtras() : null);
        }
        return mDefaultFactory;
    }

  // Fragment
  @NonNull
    @Override
    public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
        if (mFragmentManager == null) {
            throw new IllegalStateException("Can't access ViewModels from detached fragment");
        }
        if (mDefaultFactory == null) {
            mDefaultFactory = new SavedStateViewModelFactory(
                    requireActivity().getApplication(),
                    this,
                    getArguments());
        }
        return mDefaultFactory;
    }

可以看到 FragmentActivity 和 Fragment 默认的 ViewModel.Factory 都是 SavedStateViewModelFactory :

SavedStateViewModelFactory

代码语言:javascript
复制
// SavedStateViewModelFactory#create
@Override
public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
    if (isAndroidViewModel) {
            constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
        } else {
            constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
        }

     if (constructor == null) {
            return mFactory.create(modelClass);
         }

     T viewmodel;
            if (isAndroidViewModel) {
                viewmodel = constructor.newInstance(mApplication, controller.getHandle());
            } else {
                viewmodel = constructor.newInstance(controller.getHandle());
            }
            viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
            return viewmodel;
}

这里会去尝试匹配构造函数为

  • constructor(Application, SavedStateHandle)
  • constructor(SavedStateHandle)

这两种的 ViewModel

通过名字我们就能知道, SavedStateHandle 是用来处理进程销毁等重建情况的数据恢复的。

这部分在 Android 的官方文档里面可以看到:https://developer.android.com/topic/libraries/architecture/viewmodel-savedstate?hl=zh-cn。

从这里我们也可以知道,ViewModel 之前在进程被系统终止之后其实是不会保存数据给 ViewModel 的,在添加了 SaveStateHandle 之后才支持。

如果需要,我们可以在自己的 ViewModel 里加上 SaveStateHandle 的参数:

代码语言:javascript
复制
class MyViewModel(handle: SaveStateHandle) : ViewModel() {}

默认Factory

当我们的 ViewModel 没有适配 SaveStateHandle 的时候,会继续使用默认的 Factory 去创建 ViewModel,这里默认的是 AndroidViewModelFactory , 父类是 ViewModelProvider.NewInstanceFactory

前者会默认尝试创建 AndroidViewModel 。否则会调用 super 的 create 方法,创建一个普通的 ViewModel

获取 ViewModelStoreOwner

一般我们使用 FragmentActivity 或者 Fragment 作为我们的 ViewModelStoreOwenr, 以 FragmentActivity 为例:

代码语言:javascript
复制
  @Override
    public ViewModelStore getViewModelStore() {
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }

这里可以看到我们有一个 NonConfigureationInstance 对象,当这个对象存在 ViewModelStore 实例的时候,会直接使用这个 ViewModelStore。这个实例会在屏幕旋转、更改显示设置等情况下去保存我们的 ViewModelStore:

代码语言:javascript
复制
  @Override
    @Nullable
    public final Object onRetainNonConfigurationInstance() {
        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;
    }

这也就是为什么Android在发布架构组件之初就说明 ViewModel 不会受屏幕变化影响,始终保持原先实例的原因。

小结

本文介绍的内容比较简单,但是也比较实用,这些内容里有一些点值得我们注意:

  • ViewModel 的创建取决于 ViewModelStore, ViewModelStore 取决于 ViewModelStoreOwner,如果有特殊需求,我们可以自己定义我们的 ViewModelStoreOwner,例如内部提供全局的 ViewModelStore, 来支持页面最小化之类的需求。
  • VIewModel 之前只支持屏幕旋转之类的重建场景的数据恢复功能,在 Activity 1.1.0Fragment 1.2.0 之后,也支持进程回收等情况的数据恢复功能。有类似需求的时候可以通过定义 ViewModel 中加入 SavedStateHandle 参数来实现。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-10-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 半行代码 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 创建流程
  • 构造ViewModel
    • FragmentActivity默认的Factory
      • SavedStateViewModelFactory
        • 默认Factory
        • 获取 ViewModelStoreOwner
        • 小结
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档