五、CLASS_ISPREVERIFIED
运行一下Demo,报以下错误。(AndroidStudio 2.0 可能不会报错,需要打包的时候才会出现错误,这是Instant run 导致的)
![](https://img.kancloud.cn/8b/5d/8b5deb7fbaeecf430ffc6b7c20a44a0b_1056x393.jpg)
dexElements 的length = 2,看来我们的patch_dex 已经成功添加进去了。
但是从黄色框框和黄色框上面那一段log 提示中可以看出,MainActivity 引用了Cat,但是发现他们在不同的Dex 中。
看到这里可能就会问:
为什么之前那么多项目都采用分包方案,但是却不会出现这个错误呢?
我在这里总结了一个过程,想知道详细分析过程的请看QQ 空间开发团队的原文。
在apk 安装的时候,虚拟机会将dex 优化成odex 后才拿去执行。在这个过程中会对所有class 一个校验。
校验方式:假设A 该类在它的static 方法,private 方法,构造函数,override方法中直接引用到B 类。如果A 类和B 类在同一个dex 中,那么A 类就会被打上CLASS_ISPREVERIFIED 标记
。
被打上这个标记的类不能引用其他dex 中的类,否则就会报图中的错误在我们的Demo 中,MainActivity 和Cat 本身是在同一个dex 中的,所以MainActivity 被打上了CLASS_ISPREVERIFIED。而我们修复bug 的时候却引用了另外一个dex 的Cat.class,所以这里就报错了
。
而普通分包方案则不会出现这个错误,因为引用和被引用的两个类一开始就不在同一个dex 中,所以校验的时候并不会被打上CLASS_ISPREVERIFIED
。
补充一下第二条:A 类如果还引用了一个C 类,而C 类在其他dex 中,那么A类并不会被打上标记。换句话说,只要在static 方法,构造方法,private 方法,override 方法中直接引用了其他dex 中的类,那么这个类就不会被打上
CLASS_ISPREVERIFIED 标记。
5.1 解决方案
根据上面的第六条,我们只要让所有类都引用其他dex 中的某个类就可以了。
下面是QQ 控件给出的解决方案
![](https://img.kancloud.cn/e6/36/e636c4c70294cd2352510e7cd804b8cd_664x320.jpg)
在所有类的构造函数中插入这行代码System.out.println(AntilazyLoad.class);这样当安装apk 的时候,classes.dex 内的类都会引用一个在不相同dex 中的AntilazyLoad 类,这样就防止了类被打上CLASS_ISPREVERIFIED 的标志了,只要没被打上这个标志的类都可以进行打补丁操作。
hack.dex 在应用启动的时候就要先加载出来,不然AntilazyLoad 类会被标记为不存在,即使后面再加载hack.dex,AntilazyLoad 类还是会提示不存在。该类只要一次找不到,那么就会永远被标上找不到的标记了。
我们一般在Application 中执行dex 的注入操作,所以在Application 的构造中不能加上`System.out.println(AntilazyLoad.class);`这行代码,因为此时hack.dex 还没有加载进来,AntilazyLoad 并不存在。
之所以选择构造函数是因为他不增加方法数,一个类即使没有显式的构造函数,也会有一个隐式的默认构造函数。
5.2 插入代码的难点
1.首先在源码中手动插入不太可行,hack.dex 此时并没有加载进来,AntilazyLoad.class 并不存在,编译不通过。
2.所以我们需要在源码编译成字节码之后,在字节码中进行插入操作。对字节码进行操作的框架有很多,但是比较常用的则是ASM 和javaassist
3.但AndroidStudio 是使用Gradle 构建项目,编译-打包都是自动化的,我们怎么操作呢。
- 第一章 热修复设计
- 第一节、AOT/JIT & dexopt 与dex2oat
- 一、AOT/JIT
- 二、dexopt 与dex2oat
- 第二节、热修复设计之CLASS_ISPREVERIFIED 问题
- 一、前言
- 二、建立测试Demo
- 三、制作补丁
- 四、加载补丁
- 五、CLASS_ISPREVERIFIED
- 第三节、热修复设计之热修复原理
- 一、Android 热修复
- 二、Android 虚拟机和编译加载顺序
- 三、混合模式的理解
- 四、源码类到机器执行的文件过程
- 五、补丁包
- 六、类补丁生效原理
- 七、Davlik 虚拟机的限制
- 八、Davlik Class resolved by unexpected DEX: 限制和处理方式
- 九、类加载器的双亲委派加载机制
- 第四节、Tinker 的集成与使用(自动补丁包生成)
- 一、简述
- 二、Tinker 组件依赖
- 三、Tinker 的配置及任务
- 四、Tinker 封装与拓展
- 五、编写Application 的代理类
- 六、常用API
- 七、测试
- 八、细节
- 第二章 插件化设计
- 第一节、Class 文件与Dex 文件的结构解读
- 一、Class 文件
- 二、Dex 文件
- 三、Class 文件和Dex 文件对比
- 第二节、Android 资源加载机制详解
- 第三节、四大组件调用原理
- 第四节、so 文件加载机制
- 第五节、Android 系统服务实现原理
- 第三章 组件化框架设计
- 第一节、阿里巴巴开源路由框——ARouter 原理分析
- 第二节、APT 编译时期自动生成代码&动态类加载
- 第三节、Java SPI 机制
- 第四节、AOP&IOC
- 第五节、手写组件化架构
- 第四章 图片加载框架
- 第一节 图片加载框架选型
- 第二节 Glide 原理分析
- 第三节 手写图片加载框架实战
- 第五章 网络访问框架设计
- 第一节 网络通信必备基础
- 第二节 OkHttp 源码解读
- 第三节 Retrofit2 源码解析
- 第六章 RXJava响应式编程框架设计
- 第一节 RXJava之链式调用
- 第二节 RXJava之扩展的观察者模式
- 第三节 RXJava之事件变换设计
- 第四节 Scheduler 线程控制
- 第七章 IOC架构设计
- 第一节 依赖注入与控制反转
- 第二节 ButterKnife 原理上篇、中篇、下篇
- 第三节 IOC架构设计之Dagger2架构设计
- 第八章 Android架构组件 JetPack
- 第一节 LiveData的工作原理
- 第二节 Navigation 如何解决tabLayout 问题
- 第三节 ViewModel 如何感知View 生命周期及内核原理
- 第四节 Room 架构方式方法
- 第五节 dataBinding 为什么能够支持MVVM
- 第六节 WorkManager 内核揭秘
- 第七节 Lifecycles 生命周期