概览
ViewModel
旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel
让数据可在发生屏幕旋转等配置更改后继续留存。
-
数据缓存: 应用的某个
Activity
中可能包含用户列表。因配置更改而重新创建Activity
后,新Activity
必须重新提取用户列表。对于简单的数据,Activity
可以使用onSaveInstanceState()
方法从onCreate()
中的捆绑包恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据,如用户列表或位图。 -
异步调用管理:
Activity
经常需要进行异步调用,这些调用可能需要一些时间才能返回结果。Activity
需要管理这些调用,并确保系统在其销毁后清理这些调用以避免潜在的内存泄露。此项管理需要大量的维护工作,并且在因配置更改而重新创建对象的情况下,会造成资源的浪费,因为对象可能需要重新发出之前的调用。 -
代码责任分离:
Activity
主要用于显示界面数据、对用户操作做出响应或处理操作系统通信(如权限请求)。如果要求Activity
也负责从数据库或网络加载数据,那么会使类越发膨胀。为Activity
分配过多的责任可能会导致单个类尝试自己处理应用的所有工作,而不是将工作委托给其他类。以这种方式为Activity
分配过多的责任也会大大增加测试的难度。
从 Activity
、 Fragment
中分离出视图数据所有权的做法更易行且更高效。
项目依赖
打开 项目 的 build.gradle
文件并添加 google()
代码库,如下所示:
allprojects {
repositories {
google()
}
}
然后, 在 app
的 build.gradle
添加 ViewModel
组件代码库,如下所示:
def lifecycle_version = "2.2.0"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
ViewModel 的生命周期
ViewModel
对象存在的时间范围是获取 ViewModel
时传递给 ViewModelProvider
的 Lifecycle
。ViewModel
将一直留在内存中,直到限定其存在时间范围的 Lifecycle
永久消失。对于 Activity
、Fragment
,ViewModel
生命周期即:在被初始化后直至对应页面的 onDestroy
被回调后。
下图 说明了 Activity
经历屏幕旋转而后结束的过程中所处的各种生命周期状态。同样适用于 Fragment
的生命周期。
在 Fragment 之间共享数据
两个 Fragment 可以使用其 Activity 范围共享 ViewModel 来处理此类通信,如以下示例代码所示:
class SharedViewModel : ViewModel() {
val selected = MutableLiveData<Item>()
fun select(item: Item) {
selected.value = item
}
}
class MasterFragment : Fragment() {
private lateinit var itemSelector: Selector
// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
itemSelector.setOnClickListener { item ->
// Update the UI
}
}
}
class DetailFragment : Fragment() {
// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->
// Update the UI
})
}
}
从示例中可以看出:
- Activity 不需要执行任何操作,也不需要对此通信有任何了解。
- 除了 SharedViewModel 约定之外,Fragment 不需要相互了解。如果其中一个 Fragment 消失,另一个 Fragment 将继续照常工作。
- 每个 Fragment 都有自己的生命周期,而不受另一个 Fragment 的生命周期的影响。如果一个 Fragment 替换另一个 Fragment,界面将继续工作而没有任何问题。
延伸
1、AndroidViewModel
由于 ViewModel
生命周期可能长与Activity
生命周期,所以为了避免内存泄漏,Google
禁止在 ViewModel
中持有 Context
、Activity
或 View
的引用。如果一定需要使用 Context
, 可以继承 AndroidViewModel
类,内部维护了一个 ApplicationContext
。
public class AndroidViewModel extends ViewModel {
@SuppressLint("StaticFieldLeak")
private Application mApplication;
public AndroidViewModel(@NonNull Application application) {
mApplication = application;
}
/**
* Return the application.
*/
@SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
@NonNull
public <T extends Application> T getApplication() {
return (T) mApplication;
}
}
2、保存状态(SavedStateHandle)
开篇已经说过,ViewModel
对象可以处理配置更改,因此您无需担心旋转时或其他情况下的状态。但是,如果您需要处理系统发起的进程终止,则可以使用 onSaveInstanceState()
作为备用方式。
添加依赖:
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
需要使用可扩展 AbstractSavedStateVMFactory
的Factory 来创建ViewModel
,或者在Activity
、Fragment
中使用默认Factory创建它们。
private val mViewModel: ViewModelSavedState by lazy {
ViewModelProvider(this).get(ViewModelSavedState::class.java)
}
ViewModel
添加一个接收 SavedStateHandle
的构造函数
class SavedStateViewModel(private val state: SavedStateHandle) : ViewModel() {
private val NAME_KEY = "name"
fun getName(): LiveData<String?>? {
return savedStateHandle.getLiveData(NAME_KEY)
}
fun saveNewName(newName: String?) {
savedStateHandle.set(NAME_KEY, newName)
}
}
测试SavedStateHandle
方法:
- 保存状态数据到
SavedStateHandle
- 点击手机
Home
键,返回桌面 - 使用命令
adb shell ps -A |grep com.view.model.explore
查看当前App是否存活 - 使用命令
adb shell am kill com.view.model.explore
结束App进程 - 重新进入应用,查看是否可以通过
SavedStateHandle
获取到对应数据
ViewModel源码分析
ViewModel的源码非常简单,从生命周期我们可以看出主要包括初始化和清除俩个关键点
1、ViewModelProvider初始化
ViewModel实例化代码:
ViewModelProvider(this).get(ViewModel::class.java)
首先看一下 ViewModel
的构造方法:
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
//获取Activity、Fragment中的mViewModelStore
this(owner.getViewModelStore(), factory);
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
熟悉 ViewModel
的同学应该清楚,可以通过自定义 Factory
的方式来实例化不同构造函数的 ViewModel
。
主要看下使用默认Factory
来实例化 ViewModel
的流程。若当前 owner
为 HasDefaultViewModelProviderFactory
,则使用默认的Factory
来处理初始化,否则每次初始化都会生成一个新的ViewModel
实例。NewInstanceFactory
的逻辑非常简单,就不看了。
Activity
与Fragment
基类均有实现HasDefaultViewModelProviderFactory
接口,两个实现方案类似,下面看一下Activity
的实现代码:
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;
}
很简单的逻辑。创建了一个可以保存 SavedState
的 Factory
,后续将由它来创建 ViewModel
。对于Factory中的具体逻辑,将在后续获取ViewModel
实例处分析。
2、获取ViewModel实例
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//根据key值,获取mViewModelStore中以HashMap形式缓存的ViewModel
ViewModel viewModel = mViewModelStore.get(key);
//mViewModelStore已有modelClass生成的ViewModel实例,返回该实例
if (modelClass.isInstance(viewModel)) {
//回调OnRequeryFactory接口
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;
}
if (mFactory instanceof KeyedFactory) {
//根据key生成ViewModel
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
//normal生成ViewModel
viewModel = (mFactory).create(modelClass);
}
//将生成的ViewModel缓存到mViewModelStore
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
简单的说,首先会根据key
去mViewModelStore
的缓存中找是否已有实例生成,若有则返回该实例,若无缓存则创建新的实例并缓存到mViewModelStore
中。
看一下mViewModelStore
中缓存实例的put方法,这里做了一层保护,若缓存的key中已存在实例,则回调onCleared
方法,结束生命周期。
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
最后,看一下默认SavedStateViewModelFactory
创建ViewModel
的主要实现:
public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
Constructor<T> constructor;
//查找构造方法中是否有SavedStateHandle参数
if (isAndroidViewModel) {
constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
} else {
constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
}
// doesn't need SavedStateHandle
if (constructor == null) {
return mFactory.create(modelClass);
}
SavedStateHandleController controller = SavedStateHandleController.create(
mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
if (isAndroidViewModel) {
viewmodel = constructor.newInstance(mApplication, controller.getHandle());
} else {
viewmodel = constructor.newInstance(controller.getHandle());
}
viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
return viewmodel;
}
由此可知默认Factory支持继承自ViewModel
和AndroidViewModel
的子类在构造方法中添加SavedStateHandle
参数
3、ViewModel销毁
销毁的方法Activity与Fragment也类似。即当前生命周期回调 onDestroy
且不是configuration
改变时,会清空mViewModelStore
中的ViewModel
缓存。
public void onDestroy() {
mCalled = true;
FragmentActivity activity = getActivity();
boolean isChangingConfigurations = activity != null && activity.isChangingConfigurations();
if (mViewModelStore != null && !isChangingConfigurations) {
mViewModelStore.clear();
}
}
接着看一下ViewModelStore
的clear
方法,至此结束。
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
参考
ViewModel 概览(Android developers文档)
ViewModel 的已保存状态模块
Incorporate Lifecycle-Aware Components(CodeLabs)
网友评论