提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
ViewModel 是 Jetpack 组件之一,能力是维护数据保证在因配置更改导致的页面重建时 ViewModel 可以存活下来,另外使用 ViewModel 可以很方便的在一个 Activity 的多个 Fragment 中共享数据
创建对象
ViewModel 对象通过 ViewModelProvider 来创建,下面这样就可以拿到一个 TestViewModel 对象
class TestViewModel : ViewModel()
val viewModel = ViewModelProvider(this)[TestViewModel::class.java]
public open class ViewModelProvider(
private val store: ViewModelStore,
private val factory: Factory
) {
public constructor(
owner: ViewModelStoreOwner
) : this(owner.viewModelStore, defaultFactory(owner))
public constructor(owner: ViewModelStoreOwner, factory: Factory) : this(
owner.viewModelStore,
factory
)
}
ViewModelProvider 有三个构造方法但最终都会调用到 ViewModelStore 和 Factory 两个参数的构造方法 ViewModelStore 用来存储 ViewModel 对象 Factory 用来创建 ViewModel 对象,如果第二个参数 factory 不传则取默认的 Factory 默认的 Factory 只能创建没有构造参数的 ViewModel 如果有构造参数则需要自己实现 Factory 传入。这里调用的是一个 ViewModelStoreOwner 参数的构造方法 ViewModelStoreOwner 是一个接口只有一个方法 getViewModelStore 返回 ViewModelStore 对象 ComponentActivity 实现了这个接口所以这里可以传 this
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
}
ensureViewModelStore();
return mViewModelStore;
}
@SuppressWarnings("WeakerAccess")
void ensureViewModelStore() {
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
再回来看一下 defaultFactory 的实现
public open class AndroidViewModelFactory(
private val application: Application
) : NewInstanceFactory() {
public companion object {
internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =
if (owner is HasDefaultViewModelProviderFactory)
owner.defaultViewModelProviderFactory else instance
internal const val DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"
private var sInstance: AndroidViewModelFactory? = null
public fun getInstance(application: Application): AndroidViewModelFactory {
if (sInstance == null) {
sInstance = AndroidViewModelFactory(application)
}
return sInstance!!
}
}
}
defaultFactory 中会判断传入的 owner 对象是否实现了 HasDefaultViewModelProviderFactory 接口,HasDefaultViewModelProviderFactory 接口同样只有一个方法用于返回一个 ViewModelProvider.Factory 对象 ComponentActivity 实现了这个接口并且返回了一个 SavedStateViewModelFactory 对象,这样就两个参数都有了拿到了 ViewModelProvider 对象
@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;
}
创建 ViewModelProvider 对象之后调用 get 方法(这里用到了 Kotlin 的操作符重载)
@MainThread
public open operator fun <T : ViewModel> get(modelClass: Class<T>): T {
val canonicalName = modelClass.canonicalName
?: throw IllegalArgumentException("Local and anonymous classes can not be ViewModels")
return get("$DEFAULT_KEY:$canonicalName", modelClass)
}
@MainThread
public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
var viewModel = store[key]
if (modelClass.isInstance(viewModel)) {
(factory as? OnRequeryFactory)?.onRequery(viewModel)
return viewModel as T
} else {
@Suppress("ControlFlowWithEmptyBody")
if (viewModel != null) {
}
}
viewModel = if (factory is KeyedFactory) {
factory.create(key, modelClass)
} else {
factory.create(modelClass)
}
store.put(key, viewModel)
return viewModel
}
下面来看一下 SavedStateViewModelFactory
public final class SavedStateViewModelFactory extends ViewModelProvider.KeyedFactory {
private static final Class<?>[] ANDROID_VIEWMODEL_SIGNATURE = new Class[]{Application.class,
SavedStateHandle.class};
private static final Class<?>[] VIEWMODEL_SIGNATURE = new Class[]{SavedStateHandle.class};
private final Application mApplication;
private final ViewModelProvider.Factory mFactory;
private final Bundle mDefaultArgs;
private final Lifecycle mLifecycle;
private final SavedStateRegistry mSavedStateRegistry;
public SavedStateViewModelFactory(@Nullable Application application,
@NonNull SavedStateRegistryOwner owner) {
this(application, owner, null);
}
public SavedStateViewModelFactory(@Nullable Application application,
@NonNull SavedStateRegistryOwner owner,
@Nullable Bundle defaultArgs) {
mSavedStateRegistry = owner.getSavedStateRegistry();
mLifecycle = owner.getLifecycle();
mDefaultArgs = defaultArgs;
mApplication = application;
mFactory = application != null
?
: ViewModelProvider.NewInstanceFactory.getInstance();
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
Constructor<T> constructor;
if (isAndroidViewModel && mApplication != null) {
constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
} else {
constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
}
if (constructor == null) {
return mFactory.create(modelClass);
}
SavedStateHandleController controller = SavedStateHandleController.create(
mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
try {
T viewmodel;
if (isAndroidViewModel && mApplication != null) {
viewmodel = constructor.newInstance(mApplication, controller.getHandle());
} else {
viewmodel = constructor.newInstance(controller.getHandle());
}
viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
return viewmodel;
} catch (IllegalAccessException e) {
throw new RuntimeException("Failed to access " + modelClass, e);
} catch (InstantiationException e) {
throw new RuntimeException("A " + modelClass + " cannot be instantiated.", e);
} catch (InvocationTargetException e) {
throw new RuntimeException("An exception happened in constructor of "
+ modelClass, e.getCause());
}
}
@SuppressWarnings("unchecked")
private static <T> Constructor<T> findMatchingConstructor(Class<T> modelClass,
Class<?>[] signature) {
for (Constructor<?> constructor : modelClass.getConstructors()) {
Class<?>[] parameterTypes = constructor.getParameterTypes();
if (Arrays.equals(signature, parameterTypes)) {
return (Constructor<T>) constructor;
}
}
return null;
}
}
按照开始的例子最后会使用全局单例的 ViewModelProvider.AndroidViewModelFactory 实例来创建对象,刚说了 ViewModelProvider.AndroidViewModelFactory 用来创建以 Application 为作用域的 ViewModel 这里并不是为什么还是会使用它呢,看一下它的代码
public open class AndroidViewModelFactory(
private val application: Application
) : NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
try {
modelClass.getConstructor(Application::class.java).newInstance(application)
} catch (e: NoSuchMethodException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: IllegalAccessException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: InstantiationException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: InvocationTargetException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
}
} else super.create(modelClass)
}
}
原来是做了判断如果不是 AndroidViewModel 的子类直接调用了父类的 create 方法
public open class NewInstanceFactory : Factory {
@Suppress("DocumentExceptions")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return try {
modelClass.newInstance()
} catch (e: InstantiationException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: IllegalAccessException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
}
}
}
到这里终于可以得出结论 ViewModel 对象最终是通过 NewInstanceFactory 对象反射创建的
保存实例
在上面的分析中已经知道在创建 mViewModelStore 之前会先通过 getLastNonConfigurationInstance 方法去取,如果是系统配置更改引起的页面重建就可以拿到之前的 ViewModelStore 下面就从这个方法开始分析
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
如果 mLastNonConfigurationInstances 不为空则返回mLastNonConfigurationInstances.activity 而 activity 会在 retainNonConfigurationInstances 中被赋值
NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance();
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.activity = activity;
return nci;
}
public Object onRetainNonConfigurationInstance() {
return null;
}
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;
}
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
父类 Activity 的 retainNonConfigurationInstances 方法中会调用 onRetainNonConfigurationInstance 方法并且 onRetainNonConfigurationInstance 方法在 Activity 中返回了 null 但是 ComponentActivity 重写了它,在这里判断如果 ViewModelStore 对象不会空则创建一个 androidx.activity.ComponentActivity.NonConfigurationInstances 对象持有 viewModelStore 返回然后赋值给了 android.app.Activity.NonConfigurationInstances 的 activity 而 retainNonConfigurationInstances 方法会在页面销毁的时候调用,下面看一下页面重建的逻辑
public void handleRelaunchActivity(ActivityClientRecord tmp,
PendingTransactionActions pendingActions) {
ActivityClientRecord r = mActivities.get(tmp.token);
handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
}
private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
PendingTransactionActions pendingActions, boolean startsNotResumed,
Configuration overrideConfig, String reason) {
handleDestroyActivity(r, false, configChanges, true, reason);
handleLaunchActivity(r, pendingActions, customIntent);
}
public void handleDestroyActivity(ActivityClientRecord r, boolean finishing, int configChanges,
boolean getNonConfigInstance, String reason) {
performDestroyActivity(r, finishing, configChanges, getNonConfigInstance, reason);
}
void performDestroyActivity(ActivityClientRecord r, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
if (getNonConfigInstance) {
try {
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);
}
}
}
}
public Activity handleLaunchActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, Intent customIntent) {
final Activity a = performLaunchActivity(r, customIntent);
return a;
}
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo),
appContext.getAttributionSource());
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity != null) {
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken, r.shareableActivityToken);
}
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
}
return activity;
}
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
IBinder shareableActivityToken) {
mLastNonConfigurationInstances = lastNonConfigurationInstances;
}
从上面的代码可以看出在页面销毁时会将 ViewModelStore 保存在 ActivityThread 在页面重建时会再通过 attach 将保存 ViewModelStore 的 lastNonConfigurationInstances 赋值给 mLastNonConfigurationInstances 在通过 getViewModelStore 获取 ViewModelStore 就会尝试从 mLastNonConfigurationInstances 中取
void ensureViewModelStore() {
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
这样就可以拿到页面销毁前的 ViewModelStore 了并且它持有 ViewModel
销毁对象
既然 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()) {
getViewModelStore().clear();
}
}
}
});
}
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
final void clear() {
onCleared();
}
在 ComponentActivity 中监听自己的生命周期在收到 ON_DESTROY 事件后判断如果是正常退出页面则清空 ViewModelStore 并回调 ViewModel 的 onCleared 方法可以在这个方法里做一些善后工作
总结
ViewModel 只能处理因配置更改导致的页面重建,如果因为资源限制页面被销毁重建时 ViewModel 也会被销毁,如果要处理这种场景可以使用 android.app.Activity#onSaveInstanceState(android.os.Bundle) 或者 savedstate(对前者的封装)
参考与感谢
Android官方架构组件ViewModel:从前世今生到追本溯源 Jetpack ViewModel 抽丝剥茧 ViewModel 这些知识点你都知道吗? 从源码看 Jetpack(6)- ViewModel 源码详解
|