android动态设置冷启动图片拉伸变形,Android热修复技术(三)-----代码修复之冷启动类加载原理...

Android热修复技术(三)-----代码修复之冷启动类加载原理

Android热修复技术(三)-----代码修复之冷启动类加载原理

前一篇博客阐述了代码修复之底层替换原理,这篇文章主要讨论代码修复之冷启动加载原理。

因为底层替换方案的根本原理是基于native层的方法的替换,具有很大的局限性,不能新增减少类的方法和字段的数目,而冷加载的类加载方案可以突破这些的限制,因此这个方案也是非常重要的。

其他的冷启动方案

QQ空间

QQ空间的热修复方案是利用插桩实现,也就是引入一个单独的无关帮助类放到一个单独的dex中,原来的dex的所有类的构造函数都引用这个类,侵入dex打包流程,最后加载补丁dex得到dexFile对象作为参数构建一个Element对象插入到dex-Elements数组的最前面*但是插桩在Dalvik会给类加载带来很严重的影响,在Art下类地址写死,导致必须包含父类引用,最后补丁包很大。*

Tinker

提供dex差量包,整体替换dex的方案。差量的方式给出patch.dex,然后将patch.dex与应用的classes.dex合并成一个完整的dex,完整dex加载得到的dexFile对象作为参数构建一个Element对象然后整体替换掉旧的dex-Elements数组。****dex合并内存消耗在vm heap上,容易OOM,最后导致dex合并失败。

sophix(Art方案)

把补丁dex命名为classes.dex,原来的apk中的dex依次命名为classes(2,3,4,….).dex,然后一起打包为一个压缩文件。dexfile.loadDex得到DexFile对象,最后把该DexFile对象整个替换旧的dexElements数组。

d700c973a44be1df8c4a16a9b46047f8.png

注意点:

DexFile.loadDex尝试把一个dex文件解析并加载到native内存中,在加载到native内存之前,如果dex不存在对应的odex,那么Dalvik会执行dexopt,Art下会执行dexoat,最后都是得到一个优化后的odex,最后虚拟机执行的不是dex而是odex。现在如果dex足够的大,dexopt/dexoat就会非常的耗时间,在sophix的Art下由于补丁dex和apk中合并生成的一个完整的补丁压缩包,所以导致dexoat非常耗时,所以如果优化后的odex文件没有生成或者生成的不是一个完整的odex文件,那么loadDex就不能在应用启动的时候进行,因为会阻塞loadDex线程,一般就是主线程。这怎么办呢?

解决方案:

把loadDex当做一个事务处理,如果中途被打断,就删除odex文件,重启的时候发现存在odex文件,loadDex完之后,反射注入/替换dexElements数组,实现patch。如果不存在odex文件,就重启另一个子线程loadDex,重启之后再生效。

sophix(Dalvik方案)

关于QQ空间dex插入方案

这种方案都会遇到Dalvik虚拟机下类的pre-verify问题。也就是说如果一个类中直接引用到的所有非系统类都和这个类在同一个dex中的话,那么这个类就会被打上CLASS_ISPREVERIFIED。

解决方案:

QQ空间:在每个类中插入一个来自其他dex的hack.class,由此打破pre-verify问题。显然这个方案为了hack添加了一些臃肿的代码,不优雅其实tinker的方案不错,但是实现比较复杂,性能消耗严重。

全量DEX方案。

合成完整的dex,思路就是把原来的dex和补丁的dex重新合并成一个dex。但是sophix提出一种新的思路。在原来的基线包中的dex里面去掉补丁也有的class,这样补丁+去除了补丁类的基线包,就等于新的app中的所有类。这和android原生的multi-dex方案比较类似,他是把一个apk中用到的所有类拆分到classes.dex,classes2.dex。。。。。。之中,每个dex都只包含了一部分的类的定义,但单个dex也是可以加载的。

问题:如何删除原来dex中的class?

为了最大可能的减少offset的修改,我们只需要移除class定义的入口,对于class内容不进行删除。我们发现一个dex类的定义是在pheader->classDefsOff偏移处开始的,一个接一个线性排列的,一个dex一共有pheader->classDefSize个类的定义。因此我们可以一个个遍历所有的DexClassDef,如果发现了这个类名包含在我们的补丁中,就将其删除。

3aaa64f26a0a0f0b9adfdc6ccbc28d39.png

sophix对于Application的处理

Application是整个程序的入口,因此,在进入到替换的完整dex之前,一定会通过Application的代码,因此,Application必然是加载在老的dex里的。

1、如果Application使用了新dex里的类,由于不在同一个dex中,如果Application被打上了pre-verified标识,就会报异常。

这里的解法是在jni层直接将这个标识擦除。Tinker的方案是让用户在AndroidManfest.xml声明中将Application换成TinkerApplication,这样就接管了原来的Application,但是显然有侵入性。

2、如果Application没有被pre-verified,虚拟机就会在初始化的时候扫描这个类的全部代码,使得这个类的代码都要进行dvmoptResolveClass操作,它会在Resolve的时候对使用的类进行初始化,而这个逻辑在Application类初始化的时候,此时补丁还没有被加载,所以会加载到原来的dex的类,这些已经加载的类如果用到了新的dex中的类,并且又是pre-verified就会报错。

可是有没有办法把补丁加载提前到dvmoptResolveClass之前,怎么办???

解法:

让Application用到的所有非系统类都和Application位于同一个dex中,就可以保证标签被打上,接下来就可以清除操作了。

Android热修复技术(三)-----代码修复之冷启动类加载原理相关教程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值