热补丁HotFix使用

感谢原作者!
项目地址:GitHub:https://github.com/dodola/HotFix

这里只介绍如何使用,以及使用中可能遇到的问题以及解决方案。下面介绍一个精简的案例。

一.制作补丁包

打包成功的文件结构如下

这里写图片描述

热补丁HotFix使用_第1张图片

到这一步之前有三个操作 BugClass.java > BugClass.class > path.jar > path_dex.jar。

第一步 BugClass.java > BugClass.class

Dos中执行javac BugClass.java即可。

第二步 打包com文件夹 > path.jar

jar cvf path.jar *

第三步 path.jar > path_dex.jar

dx --dex --output=path_dex.jar path.jar

不清楚什么原因,在第二步到第三步执行dx命令的时候一直报错:
热补丁HotFix使用_第2张图片

这里写图片描述

为此花费了很多时间,在一篇博客中找到一种方案可以解决这个问题,在执行javac命令的时候这样写:

javac -source 1.6 -target 1.6 BugClass.java

这样在第三步执行dx命令不会出错。(http://blog.csdn.net/chichoxian/article/details/8760456)

我不确定问题所在,这里就不瞎唠叨了。

jar和dx命令

jar - Java\jdk1.8.0_51\bin\jar.exe

dx - Android\sdk\build-tools\23.0.3\dx.bat

我直接设置在环境变量

%JAVA_HOME%\bin\jar.exe

D:\Users\Administrator\AppData\Local\Android\sdk\build-tools\23.0.3

上面只是方便测试,所以使用了javac的命令去编译java文件,可以不这样操作。直接修改当前项目中的BugClass类,比如将返回值改为return fix class,然后编译项目,这样也可以得到BugClass.class文件,然后打包为patch_dex.jar,而且不会出现上面的错误。因为是测试嘛,所以还得把BugClass还原成以前模样,假设是 return bug class。然后再编译,这样本地的BugClass.class为return bug class,patch_dex.jar中BugClass.class为return fix class。

二.替换有问题的class文件

public class HotfixApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        File dexPath = new File(getDir("dex", Context.MODE_PRIVATE), "path_dex.jar");
        Utils.prepareDex(this.getApplicationContext(), dexPath, "path_dex.jar");
        HotFix.patch(this, dexPath.getAbsolutePath(), "com.lxq.hotfix.BugClass");
        try {
            this.getClassLoader().loadClass("com.lxq.hotfix.BugClass");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

按官方给出的描述,我们要阻止相关类打上CLASS_ISPREVERIFIED标志,否则会报错(相关类的意思是,如果BugClass是错误的需要被替换的类,但是LoadBugClass又引用了BugClass,那么LoadBugClass就是相关类)
这里写图片描述

为什么会报错,可以看安卓App热补丁动态修复技术介绍该

简单点说就是效验dex的时候发现两个类不在同一个dex中,所以报错了,但是这个错误外面还有个if条件,如果该类打上CLASS_ISPREVERIFIED标志,才会执行dex效验。该方案的解决方式为往所有类的构造函数里面插入了一段代码,这段代码会引用另外一个dex中的类,这样就可以阻止相关类打上CLASS_ISPREVERIFIED标志了(具体可以阅读以上链接文章)。

但是,我现在想复现上面的错误,也就是按正常流程不插入代码,可以在案例中注释掉buildsrc项目PatchClass类中process方法里面的所有代码,看上图中的错误是否出现。

结论:
在Android 19 模拟器中会出现上图中的错误
在Android 23 模拟器中不会出现上图错误,并且能正确打上补丁,执行结果返回补丁类中的 return fix class。

猜测Android 23中dex效验是不是规则改变了。。。为了做到兼容,还是应该以Android 19为基准来测试。

另外我还遇到如下坑,导致耽误很很多时间

1.案例中的path_dex.jar,比如里面的文件是BugClass.class,这个class文件构造中也必须加上:

System.out.println(AntilazyLoad.class)

你可以修改app项目,比如BugClass中return fix class OK!,然后编译整个项目,然后找到BugClass.class文件,这个class文件构造中会加上这段代码,因为在app 的build.gradle中有一段配置代码:

task('processWithJavassist') << {
    String classPath = file('build/intermediates/classes/debug')//项目编译class所在目录
    dodola.patch.PatchClass.process(classPath, project(':hackdex').buildDir
            .absolutePath + '/intermediates/classes/debug')//第二个参数是hackdex的class所在目录

}

android{
   .......
    applicationVariants.all { variant ->
        variant.dex.dependsOn << processWithJavassist //在执行dx命令之前将代码打入到class中
    }
}

这样你现在拿到的BugClass.class就可以制作补丁了。为了测试补丁效果,你还得把当前项目中刚才改动BugClass的代码还原回去,然后Build-ReBuild Project重新编译class文件。

2.每次运行项目后,下意识的想去看下class文件的构造中是否添加了

System.out.println(AntilazyLoad.class)

因为只有添加这段代码,打补丁才会有效,我当时用的 guolin大神的博客中的gui工具,发现class文件的构造中没有添加如上代码,以为是groovy的问题,看网上搭建环境啥的,好麻烦,最后实在没辙,换了一个gui工具,尼玛居然可以看到了。我用的android studio 2.1.2,项目中的buildsrc是可以运行的,看网上说要搭建环境,不是很理解。

3.如图
这里写图片描述

这个错误是因为我的build.gradle版本是这样的

classpath 'com.android.tools.build:gradle:2.1.2'

应该改成:

classpath 'com.android.tools.build:gradle:1.3.0'

查了很多资料,还是无解。

你可能感兴趣的:(基础)