解析Android高性能高稳定性代码覆盖率方案的原理

时间:2024-03-14来源:兔子手游作者:佚名
前言

在《Android高性能高稳定性代码覆盖率方案技术实践》一文中,我们实现了高效稳定的代码覆盖率采集方案。不少同学对其背后原理非常感兴趣,甚至发私信来询问底层的理论支撑。为了方便大家了解其中原理,我们写下了这篇详细的解析文章,欢迎大家阅读探讨。


方案中使用了新建ClassLoader,复制目标ClassLoader的classTable字段并进行类加载状态查询的方式,如图:

解析Android高性能高稳定性代码覆盖率方案的原理


为什么要采取这样的方式?下面我们来一探究竟。



从ClassLoader入手

我们都知道Android和Java的类加载机制非常相似,都是使用ClassLoader进行。在Java中,要采集类级别的代码覆盖率非常简单,直接反射调用ClassLoader的findLoadedClass方法就行,那为什么在Android中却那么困难呢?这得从Android独有的类加载优化机制说起。


我们先来看看Android类加载涉及的几个主要的ClassLoader,如下图:

解析Android高性能高稳定性代码覆盖率方案的原理

其中,

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

相关阅读

更多+

游戏推荐

Copyright ©  2009-2024 m.rabbit52.com