android热补丁初探----HotFixDemo
1.先来个代码结构
2.热补丁的文章有好多了,我这里就不赘述了。
来两个链接
QQ空间 的链接
这还有另一片相关的文章
http://blog.csdn.net/lmj623565791/article/details/49883661
理论的学习还是要很多的,不然各种搞不懂,不知道都是什么东西,大概有哪些步骤也是不清楚的。
hotfix github地址:
https://github.com/dodola/HotFix
3.这里大概总结下有哪些主要的步骤:
1.android编译生成apk后,apk解压后会有一个classes.dex 文件,android系统在加载这个dex文件时,是可以加载多个dex文件的。
2.我们可以再编译一个dex文件传到用户正在使用的app中,
3.加载到APP指定的目录,/data/data/包名/app_文件夹名/
4.然后将这个dex也加载到系统中,执行最新的dex中的class代码,把最原始的类覆盖掉。
4.其中比较关键的技术点
1.加载dex的时候,加载了多个是有加载顺序的,需要把后传上去的补丁包先去查找其中的类。
如: 原始dex 中的 Bug.class
补丁dex 中的 Bug.class
这两个需要设置成加载补丁包中Bug.class先加载
2.android在编译应用的时候,默认是只加载原始dex的,如果dex中有对应的类Bug.class 就会在这个类上打个标志CLASS_ISPREVERIFIED,
Bug.class 被打上这个标志后,这个类就不会在去找其他的dex中的Bug.class了(编译系统优化产生的这个标志)。
怎么样才能不打这个标志呢?
hotfix中采用的做法是:在类加载的时候,在类的构造方法中加载另外一个dex文件。就是demo中的hackdex_dex.jar,这个实际是个dex文件。
这个类在应用初始化的时候Application中执行加载这个dex.,在对应的其他所有类中的构造方法中都加载这个中间dex其中的一个类即可,不用做事情。
就是为了消除这个CLASS_ISPREVERIFIED标志,这样就不打标了。
注意:打补丁这里,有三个dex文件,
第一个是原始apk中带的classes.dex文件,
第二个是这个解除打标用的dex文件,这个dex文件中,有一个空的类的就ok。
第三个就是补丁的dex文件。
此步骤的实现相对麻烦些:
要在gradle中处理一些东西,即在编译apk的时候要将所有要打补丁的类的构造方法中打上一句加载第二个dex文件的话
例如:System.out.print(SecondDexClass.class);
注意:这句话不能在系统初始化的自定义Application中执行。(为什么这里不说了,去其他的博客看一下吧,要有理论支撑)
所以这要对gradle进行编程,hotfix demo中已经有写了,可以参考。
3.就是从服务器或者手机sd卡的某个位置获得补丁dex文件,然后执行加载。
理论就大概介绍到这里。
这个是代码地址:
http://download.csdn.net/detail/methods2011/9444827
下面对代码进行简单的介绍
1.首先要有bug类,打印的bug class000就是需要补丁 改变的
public class BugClass { public String bug() { return "bug class000"; } }2.然后系统初始化的Application,
这里进行加载了第二个dex文件
dodola.hackdex.AntilazyLoad
这个类为第二个dex中的唯一的一个空类
public class HotfixApplication extends Application { @Override public void onCreate() { super.onCreate(); File dexPath = new File(getDir("dex", Context.MODE_PRIVATE), "hackdex_dex.jar"); Utils.prepareDex(this.getApplicationContext(), dexPath, "hackdex_dex.jar"); HotFix.patch(this, dexPath.getAbsolutePath(), "dodola.hackdex.AntilazyLoad"); try { this.getClassLoader().loadClass("dodola.hackdex.AntilazyLoad"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
3.这个是在Activity中执行的代码片段
//noinspection SimplifiableIfStatement if (id == R.id.action_settings) { //准备补丁,从assert里拷贝到dex里 File dexPath = new File(getDir("dex", Context.MODE_PRIVATE), "modify_dex.jar"); Utils.prepareDex(this.getApplicationContext(), dexPath, "modify_dex.jar"); // DexInjector.inject(dexPath.getAbsolutePath(), defaultDexOptPath, "dodola.hotfix // .BugClass"); HotFix.patch(this, dexPath.getAbsolutePath(), "com.method.hotfixdemo_01.BugClass"); return true; } if (id == R.id.action_test) { LoadBugClass bugClass = new LoadBugClass(); Toast.makeText(this, "测试调用方法:" + bugClass.getBugString(), Toast.LENGTH_SHORT).show(); return true; }
modify_dex.jar是加载的补丁dex文件其中只有一个bugClass文件,其中反编译的
如果补丁成功的话:会toast 这个。。。
4.我把另外两个dex(非原始apk带的)放到了assets中。
5.有一个groovy模块,用于gradle编译补丁类用的。
build.gradle中的设置是这样的
apply plugin: 'groovy' repositories { mavenCentral() } dependencies { compile gradleApi() compile localGroovy() compile 'org.javassist:javassist:3.18.+' }
注意最上面一行的这个:apply plugin: 'groovy'
这个下来大致就是可以跑的有bug的app了。
然后是补丁的打包的过程。
打完包的名称是:
modify_dex.jar
实际是一个dex文件
1:将BugClass类修复下
然后找到对应编译后的BugClass.class,要反编译下看看这个文件更新了没,
之后新建一个文件夹,将这个类对应的包名都弄对,然后将这个BugClass.class文件放入其中。
modify.jar就是 用命令行执行 jar -cvf modify.jar *
modify_dex.jar 使用android\sdk\build-tools\23.0.2\dx.bat 文件进行编译的
dx --dex --output=modify_dex.jar modify.jar
之后这个dex就可以最为补丁送到正在使用的app中了,代码自行补脑了。
然后就是代码中的执行这个:
File dexPath = new File(getDir("dex", Context.MODE_PRIVATE), "modify_dex.jar"); Utils.prepareDex(this.getApplicationContext(), dexPath, "modify_dex.jar"); // DexInjector.inject(dexPath.getAbsolutePath(), defaultDexOptPath, "dodola.hotfix // .BugClass"); HotFix.patch(this, dexPath.getAbsolutePath(), "com.method.hotfixdemo_01.BugClass");然后这个bug就被解决了。
ok!