一、是什么
ViewModel 是 Android Jetpack 架构组件中的核心类,设计目标是为 UI 层(Activity/Fragment)提供生命周期感知的数据管理。它的核心特性是:在配置更改(如屏幕旋转)时自动保留数据,避免 UI 控制器(Activity/Fragment)因重建导致数据丢失。
二、涉及的类
public class MyViewModel extends ViewModel {
}
public class MainActivity extends AppCompatActivity {
private MyViewModel viewModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化 ViewModel
viewModel = new ViewModelProvider(this).get(MyViewModel.class);
}
}
三、源码分析
3.1 创建MyViewModel实例
viewModel = new ViewModelProvider(this).get(MyViewModel.class);
注意到ViewModel不是new,为什么不能new?不能直接实例化?
ViewModel 的核心功能是在配置更改(如屏幕旋转)时保留数据,并在 Activity/Fragment 完全销毁时自动清理。
直接 new
的问题:
- 若手动实例化 ViewModel(如
MyViewModel viewModel = new MyViewModel()
),系统无法将 ViewModel 实例与当前 Activity/Fragment 的生命周期绑定。 - 当 Activity 因配置更改被销毁重建时,ViewModel 实例会被重新创建,导致数据丢失。
下面我们进入ViewModelProvider,看看他是如何创建出来的。
public constructor(
owner: ViewModelStoreOwner
) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))
创建ViewModelProvider。Factory,是一个接口,为什么需要呢?实例化我们的MainViewModel,通过反射完成。 创建ViewModels后并将它们保存在给定ViewModelStoreOwner的存储中。
get方法会将需要反射的viewmodel的class对象给保存起来。
@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)
}
然后通过factory进行创建,创建以后,在保存到store里面。
@Suppress("UNCHECKED_CAST")
@MainThread
public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
val viewModel = store[key]
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.
return try {
factory.create(modelClass, extras)
} catch (e: AbstractMethodError) {
factory.create(modelClass)
}.also { store.put(key, it) }
}
3.2 为什么activity重建,数据不会丢失呢?
ComponentActivity
在 onRetainNonConfigurationInstance()
方法中保留 ViewModelStore
。旋转屏幕的时候,会调用onRetainNonConfigurationInstance方法
为什么需要 viewModelStore = nc.viewModelStore
?不是为null?
-
首次创建 Activity:
- Activity 未调用过
getViewModelStore()
,因此mViewModelStore
为null
。 - 此时若发生配置变更(如第一次旋转屏幕),系统调用
onRetainNonConfigurationInstance()
。 - 由于
mViewModelStore
为null
,代码尝试从 上一个NonConfigurationInstances
(即nc
)中获取viewModelStore
。 - 若这是首次创建,
nc
也为null
,最终nci.viewModelStore
仍为null
。
- Activity 未调用过
-
第二次创建 Activity(第一次配置变更后):
- 新的 Activity 实例在
onCreate()
中会调用getViewModelStore()
初始化mViewModelStore
。 - 此时
mViewModelStore
会被创建。
- 新的 Activity 实例在
public constructor(
owner: ViewModelStoreOwner
//被调用了owner.viewModelStore
) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))
-
再次发生配置变更(如第二次旋转屏幕):
- 系统再次调用
onRetainNonConfigurationInstance()
。 - 此时
mViewModelStore
已存在,直接保存到新的nci.viewModelStore
中。 - 后续的新 Activity 实例将复用这个
viewModelStore
。
- 系统再次调用
3.3 为什么在页面销毁的时候ViewModel就消失了?
因为结合了lifecycle