ViewModel创建流程详解

2,354 阅读3分钟

ViewModel 获取通过

ViewModelProvider(activity)[VM::class.java]

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

创建流程

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

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 是一个接口:

public interface ViewModelStoreOwner {
	@NonNull
	ViewModelStore getViewModelStore();
}

FragmentActivity 为例,

@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 方法:

		@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 的逻辑:

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

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

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 中。

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

源码.png

构造ViewModel

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

FragmentActivity默认的Factory

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

owner instanceof HasDefaultViewModelProviderFactory
	? ((HasDefaultViewModelProviderFactory) owner)
				.getDefaultViewModelProviderFactory()
	: NewInstanceFactory.getInstance()
	
		// 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

// 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 的官方文档里面可以看到:developer.android.com/topic/libra…

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

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

class MyViewModel(handle: SaveStateHandle) : ViewModel() {}

默认Factory

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

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

获取 ViewModelStoreOwner

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

		@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:

		@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 参数来实现。