从LeakCanary看ViewModel生命周期监控

news2025/1/12 23:17:35

前面两篇文章中已经了解了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的监听,思路是一样的,大家可以探索尝试下,在此不做赘述。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/897334.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【零基础自用】理解python为什么要用虚拟环境

不知道学过MATLAB或者R的小伙伴刚刚接触python的时候会不会被各种python版本&#xff0c;包版本&#xff0c;虚拟环境之类的搞的头晕眼花。 问题一 包版本 先来假设&#xff0c;我们自己开发了一个包MyPackage 1.0&#xff0c;里面包含一个模块叫PreTrained&#xff0c;然后去…

Python爬虫(十三)_案例:使用XPath的爬虫

本篇是使用XPath的案例 案例&#xff1a;使用XPath的爬虫 现在我们用XPath来做一个简单的爬虫&#xff0c;我们尝试爬取某个贴吧里的所有帖子且将该帖子里每个楼层发布的图片下载到本地。 #-*- coding:utf-8 -*- #tieba_xpath.py"""作用&#xff1a;本案例使用…

【AGC】Publishing api怎么上传绿色认证审核材料

【问题描述】 华为应用市场会对绿色应用标上特有的绿色标识&#xff0c;代表其通过华为终端开放实验室DevEco云测平台的兼容性、稳定性、安全、功耗和性能的检测和认证&#xff0c;是应用高品质的象征。想要自己的应用认证为绿色应用就需要在发布应用时提供绿色认证审核材料&a…

Go语言基础之基本数据类型

Go语言中有丰富的数据类型&#xff0c;除了基本的整型、浮点型、布尔型、字符串外&#xff0c;还有数组、切片、结构体、函数、map、通道&#xff08;channel&#xff09;等。Go 语言的基本类型和其他语言大同小异。 基本数据类型 整型 整型分为以下两个大类&#xff1a; 按…

echarts 关于折线统计图常用的属性设置--超详细(附加源码)

文章目录 折线统计图设置x轴字体大小及字体颜色设置y轴字体大小及字体颜色设置背景颜色及设置折线颜色设置折线效果图显示阴影折线图位置及标签位置设置鼠标悬浮折线弹出窗口显示对应的数据设置自动横向滚动 总结 大家好&#xff01;近期我会分享几篇关于echarts方面的技术点&a…

easy-es 使用

1、pom中引入依赖 <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.14.0</version></dependency><dependency><groupId>org.…

CentOS ens160 显示disconnected

使用nmcli device查看网卡状态&#xff0c;显示如图&#xff1a; 检查宿主机系统VMware DHCP Sevice和VMware NAT Sevice服务是否正常运行。 右键点击我的电脑管理按钮&#xff0c;打开计算机管理点击服务

C语言实例_异或校验算法

一、异或校验算法 异或校验算法&#xff08;XOR校验&#xff09;是一种简单的校验算法&#xff0c;用于检测数据在传输或存储过程中是否发生了错误。通过将数据中的所有比特位相异或&#xff0c;生成一个校验码&#xff0c;然后将该校验码与接收到的数据进行比较&#xff0c;以…

如何大幅提高遥感影像分辨率(Python+MATLAB)

前言: 算法:NSCT算法(非下采样变换) 数据:Landsat8 OLI 遥感图像数据 编程平台:MATLAB+Python 论文参考:毛克.一种快速的全色和多光谱图像融合算法[J].测绘科学,2016,41(01):151-153+98.DOI:10.16251/j.cnki.1009-2307.2016.01.028. 左图:未进行融合的多光谱真彩色合…

ChatGPT逐句逐句地解释代码并分析复杂度的提示词prompt

前提安装chrome 插件 AI Prompt Genius&#xff0c; 请参考 3 个 ChatGPT 插件您需要立即下载 你是首席软件工程师。请解释这段代码&#xff1a;{{code}} 添加注释并重写代码&#xff0c;用注释解释每一行代码的作用。最后分析复杂度。快捷键 / 选择 Explain Code 输入代码提…

内网隧道代理技术(十七)之 NPS的使用

NPS的介绍和使用 NPS介绍 nps是一款轻量级、高性能、功能强大的内网穿透代理服务器。目前支持tcp、udp流量转发,可支持任何tcp、udp上层协议(访问内网网站、本地支付接口调试、ssh访问、远程桌面,内网dns解析等等……),此外还支持内网http代理、内网socks5代理、p2p等,…

RFID技术助力汽车零配件装配产线,提升效率与准确性

随着科技的不断发展&#xff0c;越来越多的自动化设备被应用到汽车零配件装配产线中。其中&#xff0c;射频识别&#xff08;Radio Frequency Identification&#xff0c;简称RFID&#xff09;技术凭借其独特的优势&#xff0c;已经成为了这一领域的重要技术之一。本文将介绍RF…

Cpp基础Ⅰ之编译、链接

1 C是如何工作的 工具&#xff1a;Visual Studio 1.1 预处理语句 在.cpp源文件中&#xff0c;所有#字符开头的语句为预处理语句 例如在下面的 Hello World 程序中 #include<iostream>int main() {std::cout <"Hello World!"<std::endl;std::cin.get…

宝塔部署Java+Vue前后端分离项目经验总结

前言 之前部署服务器都是在Linux环境下自己一点一点安装软件&#xff0c;听说用宝塔傻瓜式部署更快&#xff0c;这次浅浅尝试了一把。 确实简单&#xff01; 1、 买服务器 咋买服务器略&#xff0c;记得服务器装系统就装 Cent OS 7系列即可&#xff0c;我装的7.6。 2、创建…

私密数据采集:隧道爬虫IP技术的保密性能力探究

作为一名专业的爬虫程序员&#xff0c;今天要和大家分享一个关键的技术&#xff0c;它能够为私密数据采集提供保密性能力——隧道爬虫IP技术。如果你在进行敏感数据采集任务时需要保护数据的私密性&#xff0c;那么这项技术将是你的守护神。 在进行私密数据采集任务时&#xff…

曲面(弧面、柱面)展平(拉直)瓶子标签识别ocr

瓶子或者柱面在做字符识别的时候由于变形&#xff0c;识别效果是很不好的 或者是检测瓶子表面缺陷的时候效果也没有展平的好 下面介绍两个项目&#xff0c;关于曲面&#xff08;弧面、柱面&#xff09;展平&#xff08;拉直&#xff09; 项目一&#xff1a;通过识别曲面的6个点…

报名开启 | HarmonyOS第一课“营”在暑期系列直播

<HarmonyOS第一课>2023年再次启航&#xff01; 特邀HarmonyOS布道师云集华为开发者联盟直播间 聚焦HarmonyOS 4版本新特性 邀您一同学习赢好礼&#xff01; 你准备好了吗&#xff1f; ↓↓↓预约报名↓↓↓ 点击关注了解更多资讯&#xff0c;报名学习

[C++]笔记-制作自己的静态库

一.静态库的创建 在项目属性c/c里面,选用无预编译头,创建头文件与cpp文件,需要注意release模式下还是debug模式,在用库时候要与该模式相匹配,库的函数实现是外界无法看到的,最后在要使用的项目里面导入.h文件和.lib文件 二.使用一个循环给二维数组赋值 行数 : 第几个元素 / …

一文带你了解CMS收集器:并发低停顿收集器

一、工作流程 CMS&#xff08;Concurrent Mark Sweep&#xff09;收集器是一种以获取最短回收停顿时间为目标的收集器。互联网网站或者基于B/S系统&#xff08;B/S系统是指Browser/Server系统&#xff0c;也就是基于浏览器和服务器的系统架构&#xff09;的服务端应用通常会关…