详解 Android 热更新升级如何突破底层结构差异?

附上前面两篇关于Android 热更新的文章《Android热修复升级原理和实践》、《Android热修复升级、兼容性问题的根源》。

知道了native替换方式兼容性问题的原因,我们是否有办法寻求一种新的方式,不依赖于ROM底层方法结构的实现而达到替换效果呢?

我们发现,这样native层面替换思路,其实就是替换ArtMethod的所有成员。那么,我们并不需要构造出ArtMethod具体的各个成员字段,只要把ArtMethod的作为整体进行替换,这样不就可以了吗?

也就是把原先这样的逐一替换。

ROM底层方法结构的实现

变成了这样的整体替换。

ArtMethod

因此Andfix这一系列繁琐的替换:

Andfix

其实可以浓缩为:

memcpy 方法的使用

就是这样,一句话就能取代上面一堆代码,这正是我们深入理解替换机制的本质之后研发出的新替换方案。

刚才提到过,不同的手机厂商都可以对底层的ArtMethod进行任意修改,但即使他们把ArtMethod改得六亲不认,只要我像这样把整个ArtMethod结构体完整替换了,就能够把所有旧方法成员自动对应地换成新方法的成员。

但这其中最关键的地方,在于sizeof(ArtMethod)。如果size计算有偏差,导致部分成员没有被替换,或者替换区域超出了边界,都会导致严重的问题。

对于ROM开发者而言,是在art源代码里面,所以一个简单的sizeof(ArtMethod)就行了,因为这是在编译期就可以决定的。

但我们是上层开发者,app会被下发给各式各样的Android设备,所以我们是需要在运行时动态地得到app所运行设备上面的底层ArtMethod大小的,这就没那么简单了。

想要忽略ArtMethod的具体结构成员直接取得其size的精确值,我们还是需要从虚拟机的源码入手,从底层的数据结构及排列特点探寻答案。

在art里面,初始化一个类的时候会给这个类的所有方法分配空间,我们可以看到这个分配空间的地方:

ArtMethod的具体结构成员

类的方法有direct方法和virtual方法。direct方法包含static方法和所有不可继承的对象方法。而virtual方法就是所有可以继承的对象方法了。

AllocArtMethodArray函数分配了他们的方法所在区域。

AllocArtMethodArray函数

可以看到,ptr是这个方法数组的指针,而方法是一个接一个紧密地new出来排列在这个方法数组中的。这时只是分配出空间,还没填入真正的ArtMethod的各个成员值,不过这并不影响我们观察ArtMethod的空间结构。

ArtMethod的空间结构

正是这里给了我们启示,ArtMethod们是紧密排列的,所以一个ArtMethod的大小,不就是相邻两个方法所对应的ArtMethod的起始地址的差值吗?

正是如此。我们就从这个排列特点入手,自己构造一个类,以一种巧妙的方式获取到这个差值。

NativeStructsModel ArtMethod

由于f1和f2都是static方法,所以都属于direct ArtMethod Array。由于NativeStructsModel类中只存在这两个方法,因此它们肯定是相邻的。

那么我们就可以在JNI层取得它们地址的差值:

direct ArtMethod Array

然后,就以这个methSize作为sizeof(ArtMethod),代入之前的代码。

memcpy 和 sizeof(ArtMethod)

问题就迎刃而解了。

值得一提的是,由于忽略了底层ArtMethod结构的差异,对于所有的Android版本都不再需要区分,而统一以memcpy实现即可,代码量大大减少。即使以后的Android版本不断修改ArtMethod的成员,只要保证ArtMethod数组仍是以线性结构排列,就能直接适用于将来的Android 8.0、9.0等新版本,无需再针对新的系统版本进行适配了。事实也证明确实如此,当我们拿到Google刚发不久的Android O(8.0)开发者预览版的系统时,hotfix demo直接就能顺利地加载补丁跑起来了,我们并没有做任何适配工作,鲁棒性极好。

详解 Android 热更新升级如何突破底层结构差异?

: » 详解 Android 热更新升级如何突破底层结构差异?

原创文章,作者:3628473679,如若转载,请注明出处:https://blog.ytso.com/251598.html

(0)
上一篇 2022年5月3日
下一篇 2022年5月3日

相关推荐

发表回复

登录后才能评论