从LeakCanary看ViewModel生命周期监控

763 阅读3分钟

前面两篇文章中已经了解了LeakCanary中Service和Fragment生命周期监控的实现,那么ViewModel生命周期监控又是怎么实现的呢?

同样的,要了解ViewModel生命周期监控,我们首先应该清楚在代码结构中ViewModel是如何存储获取的,什么时候会销毁ViewModel对象,这也是面试中比较常见的问题。

ViewModel的分类

在Android官方文档中搜索ViewModel,可以看到下图:

24-5-1

Google官方先是推出android.arch.lifecycle:viewmodel,随后正式引入AndroidX版本,由于android.arch.lifecycle实现比较久远,这里我们重点讨论androidx中的ViewModel生命周期监控。

ViewModel原理简介

在ViewModel机制中,主要包含ViewModelStore,ViewModelStoreOwner和ViewModelProvider这两个角色,其中ViewModelStoreOwner接口由持有ViewModelStore的类实现,ViewModelStore主要用于存储ViewModel对象,ViewModelProvider是一个工具类,用于构造ViewModel对象,同时在构造时会将构造的对象添加到ViewModelStore中。

ViewModel中的类持有关系

viewMode class

如上图所示,开发者通过ViewModelProvider获取ViewModel实例对象,该对象通过Factory构造,构造完成后添加到ViewModelStore中,ViewModelStore被ViewModelStoreOwner持有。

AndroidX中的ViewModel实现

ViewModel in AndroidX.drawio

如上图所示,为androidx中ViewModel相关的实现,图示比较清楚,配合代码观看即可,不做赘述。

ViewModel的销毁

前面看到ViewModel是存储在ViewModelStore中,那么其销毁自然是在ViewModelStore中处理,ViewModelStore代码如下:

image-20230812222904696

可以看到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() {
             @Override
             public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
                 // 如果Activity是androidx中的ComponentActivity,
                 // 则向该Activity中添加ViewModel
                 if (activity instanceof ComponentActivity) {
                     ViewModelProvider viewModelProvider = new ViewModelProvider((ViewModelStoreOwner) activity, new ViewModelProvider.Factory() {
                         @NonNull
                         @Override
                         public <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);
                 }
             }
 ​
             @Override
             public void onActivityStarted(@NonNull Activity activity) {
 ​
             }
 ​
             @Override
             public void onActivityResumed(@NonNull Activity activity) {
 ​
             }
 ​
             @Override
             public void onActivityPaused(@NonNull Activity activity) {
 ​
             }
 ​
             @Override
             public void onActivityStopped(@NonNull Activity activity) {
 ​
             }
 ​
             @Override
             public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
 ​
             }
 ​
             @Override
             public void onActivityDestroyed(@NonNull Activity activity) {
 ​
             }
         });
     }
 ​
     class ViewModelClearedWatcher extends ViewModel {
         private ViewModelStoreOwner mViewModelStoreOwner;
 ​
         public ViewModelClearedWatcher(ViewModelStoreOwner viewModelStoreOwner) {
             this.mViewModelStoreOwner = viewModelStoreOwner;
         }
 ​
         @Override
         protected void onCleared() {
             Log.d(TAG,"view model has been cleared!");
             super.onCleared();
         }
     }
 }

运行可以看到确实监听到clear方法了,日志如下:

image-20230813000016582

获取所有即将销毁的ViewModel信息

前面已了解过ViewModel存储在ViewModelStore的mMap对象中,这也就意味着我们可以通过反射获取Map对象并遍历来获取即将被销毁的所有ViewModel信息,代码如下:

 class ViewModelClearedWatcher extends ViewModel {
     private ViewModelStoreOwner mViewModelStoreOwner;
 ​
     public ViewModelClearedWatcher(ViewModelStoreOwner viewModelStoreOwner) {
         this.mViewModelStoreOwner = viewModelStoreOwner;
     }
 ​
     @Override
     protected 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;
         }
 ​
     }
 }

运行结果如下:

image-20230813001757434

至此我们就完成了androidx包中Activity关联的ViewModel销毁的监听,至于androidx包下Fragment关联的ViewModel的监听以及arch包下ViewModel的监听,思路是一样的,大家可以探索尝试下,在此不做赘述。