在《Android高性能高稳定性代码覆盖率方案技术实践》一文中,我们实现了高效稳定的代码覆盖率采集方案。不少同学对其背后原理非常感兴趣,甚至发私信来询问底层的理论支撑。为了方便大家了解其中原理,我们写下了这篇详细的解析文章,欢迎大家阅读探讨。
方案中使用了新建ClassLoader,复制目标ClassLoader的classTable字段并进行类加载状态查询的方式,如图:
为什么要采取这样的方式?下面我们来一探究竟。
我们都知道Android和Java的类加载机制非常相似,都是使用ClassLoader进行。在Java中,要采集类级别的代码覆盖率非常简单,直接反射调用ClassLoader的findLoadedClass方法就行,那为什么在Android中却那么困难呢?这得从Android独有的类加载优化机制说起。
我们先来看看Android类加载涉及的几个主要的ClassLoader,如下图:
其中,
BootClassLoader负责加载系统类PathClassLoader负责加载应用自定义类DexClassLoader一般用于动态加载应用安装包以外的类代码覆盖率应当关注的是应用自定义的类,所以我们重点看看PathClassLoader和DexClassLoader。
我们先来看一下ClassLoader是如何加载一个类的,核心代码都在它的loadClass方法中。如下:
简单总结一下:ClassLoader中的classTable字段,是指向与其关联的ClassTable的指针,ClassLoader的findLoadedClass方法,最终也是调用到它。
我们要查询PathClassLoader加载过哪些类,就需要拿到并访问它的ClassTable对象。因为classTable字段指向的就是ClassTable对象,虽然它是私有字段,但是通过反射拿到它并不难。真正的难点在于,ClassTable这个C++类,并非是Android SDK中的API,我们没办法直接使用它。
虽然可以通过硬编码和某些黑科技来间接使用它,但是复杂性是巨大的挑战,同时会带来很大的稳定性和兼容性风险,得不偿失,因此我们没有这样做。那么有没有其他的方式来访问它呢?
答案是肯定的。
其实ClassLoader中已经提供了访问了ClassTable的方法,即findLoadedClass。我们将PathClassLoader的classTable字段的值,复制给一个新的ClassLoader,就能利用ClassLoader的访问能力直接访问PathClassLoader的ClassTable。
同时,因为新建的ClassLoader即不是PathClassLoader。也不是DexClassLoader,所以调用findLoadedClass查询某个类的时候,即使这个类没有加载,也不会触发加载,会直接返回null。
上述两点的巧妙之处在于,完美绕开了系统对ClassTable访问的限制和不需要的类加载优化,寥寥几行代码就实现了对类加载状态的获取,简洁而不简单。
到这里,原理和方法都已经清晰了:利用新的ClassLoader绕开系统限制和多余的优化,间接访问目标ClassLoader中的类加载状态。有了这个结论,Android高性能高稳定性代码覆盖率方案也就应运而生。大家若有兴趣或还有想要了解的地方,欢迎一起探讨。
作者:钧羡
来源:微信公众号:高德技术
出处:https://mp.weixin.qq.com/s/TsN4SzzGKdJUbcF1RBTNCw
2024-06-20
2024-07-08
2024-05-22
坦克战争2D软件
24MB
下载提孜农场手游安卓最新版
216.98MB
下载时尚洋品店最新版apk
5.71MB
下载法医模拟器最新版
31.37M
下载罗德的冰淇淋工厂安卓版
26.48MB
下载办公室情侣最新版本
43.31M
下载警察维加斯抓捕模拟行动中文版
117MB
下载Supermarket Simulator手机版安卓
81.26M
下载水族馆杀手2024
80MB
下载农间爱耕最新
62.66M
下载