原理
java文件打包成apk后其实就是apk里的一个个dex文件。
当一个apk出现多个dex文件后,需要把dex文件塞入到app的classloader之中。如果class.dex和class1.dex中有一个类是重复的,系统其实并不会报异常,依然正常运行。那系统会选择加载哪个类呢?它会选择排在前面dex的类。
让我们来看看类加载的代码:
当找类的时候,会按顺序遍历dex文件,然后从当前遍历的dex文件中找类,如果找到类则返回,如果找不到从下一个dex文件继续查找。
在此基础上,构想了热修复的方案:把有问题的类打包到一个dex(patch.dex)中去,然后把该dex插入到Element的最前面。
如下图:
但是存在一个问题:当我们修改某个类,然后打包成dex,插入到classloader,当加载类的时候,出现了异常。
为什么出现异常?
ModuleManager引用了QzoneActivityManager,但是发现这这两个类所在的dex不在一起,其中:
- ModuleManager在classes.dex中
- QzoneActivityManager在patch.dex中
结果发生了错误。
那么如何让不在同一个dex中的两个类,即使存在引用关系,也不崩溃呢?
不进行dex校验~
怎么才会不做dex校验呢?就是不要被打上CLASS_ISPREVERIFIED标志。
具体实现:就是在所有类的构造函数中插入一段代码,具体如下
if (ClassVerifier.PREVENT_VERIFY) {
System.out.println(AntilazyLoad.class);
}
只要类A没有被打上CLASS_ISPREVERIFIED标志,那么即使Antilazyload这个类在不同的dex中,也不会出现问题。
在虚拟机启动的时候,当verify选项被打开的时候,如果static方法、private方法、构造函数等,其中的直接引用(第一层关系)到的类都在同一个dex文件中,那么该类就会被打上CLASS_ISPREVERIFIED标志。
那么,我们要做的就是,阻止该类打上CLASS_ISPREVERIFIED的标志。
注意下,是阻止引用者的类,也就是说,假设你的app里面有个类叫做LoadBugClass,再其内部引用了BugClass。发布过程中发现BugClass有编写错误,那么想要发布一个新的BugClass类,那么你就要阻止LoadBugClass这个类打上CLASS_ISPREVERIFIED的标志。
让LoadBugClass在构造方法中,去引用别的dex文件,比如:hack.dex中的某个类即可。
Application作为应用的入口不能插入这段代码(构造函数)。
因为hack.dex是在onCreate中执行的,如果插入上述代码,会出现找不到Antilazyload。
QQ空间使用的是字节码插入代码的方式,并不是源代码插入,借助了库javaassist进行字节码插入。
QQ空间如何打补丁包
1.当正式版本发布之后,会生成一个缓存文件,缓存文件中有所有class的md5以及mapping混淆文件
2.在后续版本使用了apply-mapping选项,正式版本的mapping文件,md5文件和上个版本进行比较,这样就可以把不同的class打包成dex了。
插件化是什么
插件化一般就是提供一个apk(插件)文件,然后在程序中load该apk,那么如何加载apk中的类呢?其实就是通过这个DexClassLoader
总结一句话
看了很多,发现兼容性并不太好。所以转战了~~~~重新看Tinker
参考文章
http://blog.csdn.net/lmj623565791/article/details/49883661
https://mp.weixin.qq.com/s?__biz=MzI1MTA1MzM2Nw==&mid=400118620&idx=1&sn=b4fdd5055731290eef12ad0d17f39d4a&scene=1&srcid=1106Imu9ZgwybID13e7y2nEi#wechat_redirect
http://www.cnblogs.com/chenxibobo/p/6076459.html