AndroidMultidex热修复CLASS_ISPREVERIFIED问题解决方案

本文介绍了在Android 4.4及更低版本因CLASS_ISPREVERIFIED导致的热修复问题,分析了问题原因,并提供了解决方案。通过创建hack.dex并使用ASM插桩技术,防止类被标记,从而解决兼容性问题。详细步骤包括创建hack module、使用gradle插桩等,提供了一个可行的Demo。
摘要由CSDN通过智能技术生成

上文Android热修复方案盘点 中,提到了4种比较出名的热修复方案。

  • 腾讯Qzone超级补丁的 multidex方案,

  • 腾讯Tinker的 dexdiff方案,

  • 阿里 andFix纯native方法指针重定向方案(已废弃,因为有了新的替代方案 sophix ),

  • 美团的 robustinstantRun方案。

然而,在android多版本的兼容上,这些热修复方案多多少少存在一些问题。本文思路来源为两篇官方技术博文: 《安卓App热补丁动态修复技术介绍》《 Android N混合编译与对热补丁影响解析》
可惜大佬发文一般人看不懂,所以我重新解读一下,更通俗易懂地展示这两个坑的解决方案.

正文大纲

  • CLASS_ISPREVERIFIED兼容问题

  • AndroidN混合编译兼容问题

正文

CLASS_ISPREVERIFIED 兼容问题

Demo地址:https://github.com/18598925736/HotUpdateDemo/tree/4.4crashsolution

问题描述

一句话描述问题:

在apk安装的时候,Dalvik虚拟机如果发现 一个类A它所引用的其他类,和它自己都处于同一个dex文件内部,那么类A就会被打上一个 CLASS_ISPREVERIFIED 标记,从而提高性能。那么按照这个思路,如果类A引用了一个有bug的类Util,然后我们用multidex热修复方案给他推了一个patch.dex,然后重启修复,这个类已经被打上了标记,但是重启app之后,它所引用的类Util 此时和它又不处于一个dex内(新的Util类在patch.dex内)。此时,起了一个冲突,既打上了标记,又发现不处于一个dex内的引用类,程序就会报错。

CLASS_ISPREVERIFIED 分4个单词 class , is , pre verified , 是否 被预先 校验

此问题只会出现在Dalvik虚拟机之下(4.4 sdk19 以下默认使用dalvik,5.0 sdk 21 以后便默认使用art虚拟机),art不会有类似问题。所以可以认为此问题只出现在5.0以下(不含5.0)的机器上。

问题演示

我使用的是上一篇文章的 Android Muitldex热更新修复方案原理的demo 下载之后,直接运行在SDK 19 android4.4的模拟器上。这是一个已经加入了补丁包fix.dex的demo工程。当你直接运行,会发现程序崩溃,报错如下:image

大概意思就是 有一个类的引用预先校验了,但是没有找到预想中的实现。这就是由于 被打上了 CLASS_ISPREVERIFIED标记之后又执行了补丁修复,造成冲突。

解决方案

既然问题的根源在于 引用Util的A类被打上了 CLASS_ISPREVIRIFIED标记,那么有没有办法让这些类不被打上标记呢?

思考:

:如何防止我们源代码中所有的类被打上 CLASS_ISPPREVERIFIED标记?

答: 理论上,一个android工程中所有的java类(除了Application之外)都有可能需要热修复。如果让这些类都去引用一个另一个dex文件之下的class,就能防止在dex解析的时候被打
CLASS_ISPPREVERIFIED标记。但是这样有一个弊端,就是
CLASS_ISPPREVERIFIED带来的性能提升将会消失。但是既然出现bug,要解决,总要付出一点代价。代价且容后再说。

行动

1.创建一个hack module,其中创建一个空白java类 AntilazyLoad。编译它,得到 AntilazyLoad.class 然后用dx命令,将它打包成hack.dex

image

具体的命令为: dx--dex--output=hack.dex./com/zhou/hack/Antilazyload.class

dx命令的位置为下图所示,注意加入到系统环境变量pathimage

2.使用gradle插桩的方式,干涉gradle打包流程,在生成 javac命令之后,在 dx命令之前,在所有我们编写的所有class里面的构造函数内部,加上 AntilazyLoad 的直接使用( 反射引用是不行的)。 这一句话的信息量有点大,分步解释:

  • gradle插桩 类比为 用 gson,fastjson这类第三方框架来修改 json文件。我们也可以利用 特定的手段来自由修改 class文件。这类技术框架有 ASM,AspectJ,Javassist等。由于我们 androidStudiogradle来构建项目,所以,还需要我们自定义 gradle插件,来在合适的时机 使用 ASM 这种技术框架来在 class文件中修改字节码内容。

  • javac命令之后 dx命令之前 gradle执行项目构建,是通过一个一个的 task来进行。比如 将java文件用 javac命令编译为 class,任务名字叫做: :app:compileDebugJavaWithJavac

image

我们进行插桩的时机,便是上图中 javac之后, dx之前。另外,任何一个Task,都有 input元素和 output元素,以及可以设置 doFirst闭包,表示执行任务之前先执行一段逻辑,设置 doLast,表示执行任务执行之后再执行一段逻辑。
image

Demo完全解读

上面的解决方案,只是大略提及方案思路,真实去执行方案的时候会涉及到非常多的小细节,我认为有必要将细节中比较重要的部分逐一分步详解。

项目结构

hack module image

这个Module的作用,仅仅是生成一个普通的java类的class文件,然后用class 通过dx命令生成hack.dex(名字随意,只不过约定俗称用的hack)文件而已。没有别的。得到 hack.dex之后,它的使命就完成了。生成dex的方法上文已详述。

buildSrc moduleimage

这个Module只是一个普通的javaModule,但是,它是androidStudio中比较特殊的一个名字,当你在空白项目中创建一个buildSrc目录之后,执行同步,as就会为你自动生成如图所示的module结构。因为,这个名字是gradle插件特有的。image

HotfixPlugin.java 作为 gradle插件的核心类,其关键代码如下:


project.afterEvaluate(new  Action<Project>()  {
   
     @Override
       public  void execute(Project project)  {
   
       //找到额外属性
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值