前面两篇文章中已经了解了LeakCanary中Service和Fragment生命周期监控的实现,那么ViewModel生命周期监控又是怎么实现的呢?
同样的,要了解ViewModel生命周期监控,我们首先应该清楚在代码结构中ViewModel是如何存储获取的,什么时候会销毁ViewModel对象,这也是面试中比较常见的问题。
ViewModel的分类
在Android官方文档中搜索ViewModel,可以看到下图:
Google官方先是推出android.arch.lifecycle:viewmodel,随后正式引入AndroidX版本,由于android.arch.lifecycle实现比较久远,这里我们重点讨论androidx中的ViewModel生命周期监控。
ViewModel原理简介
在ViewModel机制中,主要包含ViewModelStore,ViewModelStoreOwner和ViewModelProvider这两个角色,其中ViewModelStoreOwner接口由持有ViewModelStore的类实现,ViewModelStore主要用于存储ViewModel对象,ViewModelProvider是一个工具类,用于构造ViewModel对象,同时在构造时会将构造的对象添加到ViewModelStore中。
ViewModel中的类持有关系
如上图所示,开发者通过ViewModelProvider获取ViewModel实例对象,该对象通过Factory构造,构造完成后添加到ViewModelStore中,ViewModelStore被ViewModelStoreOwner持有。
AndroidX中的ViewModel实现
如上图所示,为androidx中ViewModel相关的实现,图示比较清楚,配合代码观看即可,不做赘述。
ViewModel的销毁
前面看到ViewModel是存储在ViewModelStore中,那么其销毁自然是在ViewModelStore中处理,ViewModelStore代码如下:
可以看到clear方法就是用来清理ViewModelStore中存储的ViewModel对象的,在Activity onDestroy是会调用ViewModelStore的clear方法。
ViewModel销毁监控
从ViewModelStore clear方法的实现可以看出,其首先会遍历mMap中的ViewModel对象,调用每一个的clear方法,随后清空整个mMap,这也就意味着,我们可以通过向该ViewModelStore添加一个ViewModel来达到监控ViewModel销毁的目的,当ViewModel需要被销毁时,我们添加的ViewModel对象的clear方法会被调用,此时mMap还没有清空,我们可以通过遍历mMap来得到所有应该被清空的ViewModel对象信息。
向ViewModelStore添加ViewModel对象
向ViewModelStore添加我们自定义的ViewModelClearedWatcher,用于监控ViewModel的销毁,实现代码如下:
public class ViewModelWatcher {private static final String TAG = "ViewModelWatcher";private static volatile ViewModelWatcher mInstance;private ViewModelWatcher() {}public static ViewModelWatcher getInstance() {if (null == mInstance) {synchronized (ViewModelWatcher.class) {if (null == mInstance) {mInstance = new ViewModelWatcher();}}}return mInstance;}public void init(Application application) {application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {@Overridepublic void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {// 如果Activity是androidx中的ComponentActivity,// 则向该Activity中添加ViewModelif (activity instanceof ComponentActivity) {ViewModelProvider viewModelProvider = new ViewModelProvider((ViewModelStoreOwner) activity, new ViewModelProvider.Factory() {@NonNull@Overridepublic <T extends ViewModel> T create(@NonNull Class<T> modelClass) {if (modelClass.isAssignableFrom(ViewModelClearedWatcher.class)) {return (T) (new ViewModelClearedWatcher((ViewModelStoreOwner) activity));}return null;}});viewModelProvider.get(ViewModelClearedWatcher.class);}}@Overridepublic void onActivityStarted(@NonNull Activity activity) {}@Overridepublic void onActivityResumed(@NonNull Activity activity) {}@Overridepublic void onActivityPaused(@NonNull Activity activity) {}@Overridepublic void onActivityStopped(@NonNull Activity activity) {}@Overridepublic void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {}@Overridepublic void onActivityDestroyed(@NonNull Activity activity) {}});}class ViewModelClearedWatcher extends ViewModel {private ViewModelStoreOwner mViewModelStoreOwner;public ViewModelClearedWatcher(ViewModelStoreOwner viewModelStoreOwner) {this.mViewModelStoreOwner = viewModelStoreOwner;}@Overrideprotected void onCleared() {Log.d(TAG,"view model has been cleared!");super.onCleared();}}
}
运行可以看到确实监听到clear方法了,日志如下:
获取所有即将销毁的ViewModel信息
前面已了解过ViewModel存储在ViewModelStore的mMap对象中,这也就意味着我们可以通过反射获取Map对象并遍历来获取即将被销毁的所有ViewModel信息,代码如下:
class ViewModelClearedWatcher extends ViewModel {private ViewModelStoreOwner mViewModelStoreOwner;public ViewModelClearedWatcher(ViewModelStoreOwner viewModelStoreOwner) {this.mViewModelStoreOwner = viewModelStoreOwner;}@Overrideprotected void onCleared() {Log.d(TAG, "view model has been cleared!");Map<String, ViewModel> map = getMapFromViewModelStore();if (map != null && !map.isEmpty()) {for (ViewModel viewModel : map.values()) {Log.d(TAG, "viewModel been cleared:" + viewModel.toString());}}super.onCleared();}private Map<String, ViewModel> getMapFromViewModelStore() {try {Class<?> viewModelStoreClass =Class.forName(ViewModelStore.class.getName());Field field = viewModelStoreClass.getDeclaredField("mMap");field.setAccessible(true);return (Map<String, ViewModel>) field.get(mViewModelStoreOwner.getViewModelStore());} catch (NoSuchFieldException | ClassNotFoundException | IllegalAccessException e) {e.printStackTrace();return null;}}
}
运行结果如下:
至此我们就完成了androidx包中Activity关联的ViewModel销毁的监听,至于androidx包下Fragment关联的ViewModel的监听以及arch包下ViewModel的监听,思路是一样的,大家可以探索尝试下,在此不做赘述。