这是我参与更文挑战的第15天,活动详情查看: 更文挑战
ViewModel 概览
简介
ViewModel
是一种被设计为通过能感知生命周期的方式来存储和管理 UI 相关的数据的类。它让数据能存活过配置变更(如屏幕旋转)而不被杀死。
定义
ViewModel
是一个抽象类,类中只定义了一个空实现的 onCleared()
方法。
public abstract class ViewModel {
/**
* This method will be called when this ViewModel is no longer used and will be destroyed.
* <p>
* It is useful when ViewModel observes some data and you need to clear this subscription to
* prevent a leak of this ViewModel.
*/
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
}
复制代码
ViewModel 里面不要引用 View、或者任何持有 Activity 类的 context , 否则会引发内存泄漏问题。
当 ViewModel
需要 Application 类的 context 来获取资源、查找系统服务等,可以继承 AndroidViewModel
类。
public class AndroidViewModel extends ViewModel {
@SuppressLint("StaticFieldLeak")
private Application mApplication;
public AndroidViewModel(@NonNull Application application) {
mApplication = application;
}
/**
* Return the application.
*/
@NonNull
public <T extends Application> T getApplication() {
//noinspection unchecked
return (T) mApplication;
}
}
复制代码
ViewModel 使用demo
看下源码中的一个例子
public class UserActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.user_activity_layout);
final UserModel viewModel = ViewModelProviders.of(this).get(UserModel.class);
viewModel.userLiveData.observer(this, new Observer<User>() {
@Override
public void onChanged(@Nullable User data) {
// update ui.
}
});
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
viewModel.doAction();
}
});
}
}
复制代码
public class UserModel extends ViewModel {
public final LiveData<User> userLiveData = new LiveData<>();
public UserModel() {
// trigger user load.
}
void doAction() {
// depending on the action, do necessary business logic calls and update the
// userLiveData.
}
}
复制代码
源码分析
上面已经看过了ViewModel
的源码,很简单,就是一个抽象类,还有一个onCleared()方法
接下来看下获取ViewModel
的代码
ViewModelProviders.of(this).get(UserModel.class)
ViewModelProviders
ViewModelProviders
类提供了4个静态工厂方法 of()
创建新的 ViewModelProvider
对象。
ViewModelProviders.of(Fragment)
ViewModelProviders.of(FragmentActivity)
ViewModelProviders.of(Fragment, Factory)
ViewModelProviders.of(FragmentActivity, Factory)
复制代码
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
Application application = checkApplication(checkActivity(fragment));
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(fragment.getViewModelStore(), factory);
}
复制代码
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
复制代码
ViewModelProvider
ViewModelProvider
负责提供 ViewModel
对象
public class ViewModelProvider {
public interface Factory {
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
private final Factory mFactory;
private final ViewModelStore mViewModelStore;
//...省略其他无关代码
}
复制代码
Factory
接口定义了一个创建 ViewModel
的接口 create()
,ViewModelProvider
在需要时调用该方法新建 ViewModel
对象。
Android 已经内置了2个 Factory
实现类,分别是:
AndroidViewModelFactory
实现类,可以创建ViewModel
和AndroidViewModel
子类对象。NewInstanceFactory
类,只可以创建ViewModel
子类对象。
它们的实现都是通过反射机制调用 ViewModel
子类的构造方法创建对象。
public static class NewInstanceFactory implements Factory {
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection TryWithIdenticalCatches
try {
return modelClass.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
}
复制代码
AndroidViewModelFactory
继承 NewInstanceFactory
类,是个单例,支持创建 AndroidViewModel
子类对象。
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private static AndroidViewModelFactory sInstance;
/**
* Retrieve a singleton instance of AndroidViewModelFactory.
*
* @param application an application to pass in {@link AndroidViewModel}
* @return A valid {@link AndroidViewModelFactory}
*/
@NonNull
public static AndroidViewModelFactory getInstance(@NonNull Application application) {
if (sInstance == null) {
sInstance = new AndroidViewModelFactory(application);
}
return sInstance;
}
private Application mApplication;
/**
* Creates a {@code AndroidViewModelFactory}
*
* @param application an application to pass in {@link AndroidViewModel}
*/
public AndroidViewModelFactory(@NonNull Application application) {
mApplication = application;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
//noinspection TryWithIdenticalCatches
try {
return modelClass.getConstructor(Application.class).newInstance(mApplication);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
return super.create(modelClass);
}
}
复制代码
ViewModelStore
再看下实例化ViewModelProvider
中还需要的ViewModelStore
对象
ViewModelStore
类中维护一个 Map<String, ViewModel>
对象存储已创建的 ViewModel
对象,并提供 put()
和 get()
方法。
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);
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
复制代码
而这个ViewModelStore
通过之前的代码知道,是通过传入的Fragment
/FragmentActivity
中拿,这2个类都实现了ViewModelStoreOwner
接口,返回当前 UI 作用域里的 ViewModelStore
对象。
ViewModelStoreOwner
public interface ViewModelStoreOwner {
/**
* Returns owned {@link ViewModelStore}
*
* @return a {@code ViewModelStore}
*/
@NonNull
ViewModelStore getViewModelStore();
}
复制代码
实现如下:
androidx.fragment.app.Fragment#getViewModelStore
@Override
public ViewModelStore getViewModelStore() {
if (getContext() == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
return mViewModelStore;
}
复制代码
androidx.fragment.app.FragmentActivity#getViewModelStore
private ViewModelStore mViewModelStore;
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.");
}
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
复制代码
获取ViewModel
final UserModel viewModel = ViewModelProviders.of(this).get(UserModel.class);
复制代码
ViewModelProviders.of(this)
上面已经分析过了,这个方法返回一个ViewModelProvider
实例,然后通过get()
方法获取ViewModel
@MainThread
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);
}
复制代码
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
复制代码
如果在 ViewModelStore
里不存在,则使用 Factory
创建一个新的对象并存放到 ViewModelStore
里。
以上便是 ViewModel
创建和获取的主要过程
通过 ViewModelProviders
创建 ViewModelProvider
对象,调用该对象的 get()
方法获取 ViewModel
对象。 当 ViewModelStore
里不存在想要的对象,ViewModelProvider
会使用 Factory
新建一个对象并存放到 ViewModelStore
里。
注:老版本的实现源码分析参见Android Jetpack之ViewModel
ViewModel 总结
Configuration Changes 存活原理
总结里还有一段
销毁过程
androidx.fragment.app.FragmentActivity#onDestroy
@Override
protected void onDestroy() {
super.onDestroy();
if (mViewModelStore != null && !isChangingConfigurations()) {
mViewModelStore.clear();
}
mFragments.dispatchDestroy();
}
复制代码
androidx.fragment.app.Fragment#onDestroy
@CallSuper
public void onDestroy() {
mCalled = true;
FragmentActivity activity = getActivity();
boolean isChangingConfigurations = activity != null && activity.isChangingConfigurations();
if (mViewModelStore != null && !isChangingConfigurations) {
mViewModelStore.clear();
}
}
复制代码
先判断是否有发生 Configuration Changes,如果没有则会调用 ViewModelStore
的 clear()
方法,再一一调用每一个 ViewModel
的 onCleared()
方法。
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
复制代码
生命周期
ViewModel
对象被限定为在获取自己时传入 ViewModelProvider
的 Lifecycle
。在这个 Lifecycle
永久死去之前,ViewModel
都将留存在内存中:比如 activity 的 finish,或者 fragment 的 detach。
下图展示了一个 activity 在旋转和结束的过程中所经历的各种生命周期状态,以及与其关联的 ViewModel
的生命周期。而同样的基本状态也适用于 fragment 的生命周期。
一般而言,您可以在系统第一次调用某个 activity 对象的 onCreate()
方法时请求特定的 ViewModel
。系统可能会在整个 activity 的生命中多次调用 onCreate()
方法,例如当设备的屏幕旋转时。ViewModel
从您第一次请求它开始存在,直到 activity 被结束并销毁。
用途
-
在 Android 中,
Activity
和Fragment
这类 UI 组件会被系统销毁或重建,未特殊处理的 UI 数据将会丢失。以往处理这类问题时,会使用onSaveInstanceState()
保存 UI 数据,在onCreate()
方法里恢复 UI 数据(通常还要保证其正确的序列化),但是数据的大小和类型有限制(比如:List、Bitmap…)当发生 Configuration Changes 时,可利用
ViewModel
恢复数据 -
Activity/Fragment经常需要进行一些异步操作。一旦涉及到异步,我们都明白这里存在内存泄漏的可能性。因此我们保证Activity/Fragment在销毁后及时清理异步操作,以避免潜在的内存泄漏。
ViewModel
并没有自动帮我们解决这个问题,而是通过onCleared()
交给我们业务自己重写去处理。当
ViewModel
所在的 UI 组件被真正销毁时,它的onCleared()
方法会被调用,可以覆盖该方法清理资源。 -
Fragment可以通过宿主Activity共享ViewModel来处理通信问题。
在
FragmentActivity
中ViewModelStore
提供的ViewModel
可以存活至FragmentActivity
销毁。 因此不同的Fragment
实例,可以直接通过传入FragmentActivity
,拿到同样的ViewModel
实例,进而实现数据通讯。
其他
生命周期管理库 (Lifecycles) 由三个组件构成,包括 Lifecycle
、LiveData
和 ViewModel
。它可以用来解决常见的生命周期问题,同时使您的应用程序易于测试且可维护。
推荐开发者同时使用 ViewModel
和另一个生命周期组件 LiveData
来实现响应式的 UI 界面。
注意事项:不要在
ViewModel
中引用 View,使用LiveData
来通知界面更新;不要混淆ViewModel
和 onSaveInstanceState 方法的用途。