您的位置:首页 > 移动开发 > Android开发

Android热补丁动态修复技术(完结篇):自动生成打包带签名的补丁,重构项目

2016-05-05 12:41 736 查看

一、关于前面四篇博文

Android热补丁动态修复技术(一):从Dex分包原理到热补丁

Android热补丁动态修复技术(二):实战!CLASS_ISPREVERIFIED问题!

Android热补丁动态修复技术(三)—— 使用Javassist注入字节码,完成热补丁框架雏形(可使用)

Android热补丁动态修复技术(四):自动化生成补丁——解决混淆问题

前两篇博文主要是介绍热补丁修复技术的一些原理和实现方案。

而后面两篇博文主要是介绍如何使用代码实现整个热补丁框架,但是框架写的真的很糟糕,很多多余的操作。而这很大一部分原因是使用了transform,在混淆的时候transform并不好用。

以下是我在github上重构好的热补丁框架,求star (。・`ω´・)

https://github.com/AItsuki/HotFix

1. 支持混淆

2. 自动生成带签名的补丁包

3. 加载补丁包时会进行签名校验



图中的patch文件夹就是自动生成的补丁包保存目录了,里面有打成jar包之前的class,如果patch.jar打包失败,还能继续手动打包。

更详细的介绍和使用方式请移步到github,再说一次:求star (。・`ω´・)

二、框架的实现思路

在第四篇博文中,我们发现在混淆的情况下,transform使用起来真的很反人类,因为transform只能在混淆之前对class进行操作,无法将transform添加到混淆之后。

所以以下思路,我放弃了使用transform,而是直接在dextransform这个任务的dofirst中进行操作。

在重构项目之前,我先记录下了这些思路和流程,然后根据这个流程来实现热补丁框架,效率真的快了很多。

2.1 定义热补丁框架的使用方式

release签名打包作为发布版本,每次release打包都会重新生成hash.txt和mapping.txt(开启混淆的情况下才有mapping)

每次debug运行的时候(直接运行项目或者buildapk),都会通过校验hash.txt和mapping.txt生成已签名补丁包。

直接将补丁包放到sdcard中即可完成热修复

加载补丁的时候需要进行签名校验,防止恶意代码注入

2.2 代码流程

抛弃transform,使用纯hook的方式实现。

主要hook的task有这几个:

transformClassesWithDexForRelease

transformClassesWithDexForDebug

transformClassesAndResourcesWithProguardForRelease

transformClassesAndResourcesWithProguardForDebug

不混淆的情况:

transformClassesWithDexForRelease

dofirst —— 遍历输入文件,生成md5保存好(hash.txt),然后注入代码

transformClassesWithDexForDebug

dofirst —— 遍历输入文件,生成md5,和hash对比,将改变过的类复制到补丁文件夹,然后注入代码

混淆的情况:

transformClassesAndResourcesWithProguardForRelease

dolast —— 遍历输出文件,生成md5保存好(hash.txt),然后注入代码,将mapping保存好

transformClassesAndResourcesWithProguardForDebug(需要使用applymapping)

dolast —— 遍历输出文件,生成md5,和hash对比,将改变过的类复制到补丁文件夹,然后注入代码

开启混淆后task的执行顺序是proguard –> dex

因为dex永远是在最后面执行,所以注入代码和生成补丁这些操作都只需要hook dex就可以了

但是开启混淆的时候,dex dofirst需要做的事情还是有点不同的,我们可以通过一个变量来控制 def minify = false

hook proguard,在proguardTransform执行的时候复制minify = true

这样就可以控制混淆和不混淆两种情况了。

2.3 实际遇到的问题

1、 不clean项目,第二次运行release打包不会注入代码

这是因为gradle的增量式构建,up-to-date,task不执行

解决方式:

dexRelease.outputs.upToDateWhen {false} 让task一直都执行

http://stackoverflow.com/questions/7289874/resetting-the-up-to-date-property-of-gradle-tasks

2、如果有使用到自定义控件,在xml的preView窗口会报空指针异常

这是因为自定义控件已经被注入了代码,而预览窗口的时候并没有加载hack.jar,找不到AntilazyLoad.class,所以报空指针。

解决方式:

使用pluginExtention,在build.gradle中配置变量,控制在debug模式下是否注入代码。

如图,这里添加了两个Extention



3、如何applymapping

applymapping的作用是复用上一次的混淆规则。

所以我们需要将release生成的mapping.txt应用到debug的混淆上,否则可能无法正确的生成补丁。

解决方式:

第一种:

手动配置debug的混淆文件

第二种:

1. 在gradle 1.5以下时,可以直接task.applyMapping(File file)的方式在代码中动态添加

2. 在gradle1.5以上时,因为proguard的transform是一个特殊的task,所以并不能直接applyMapping,需要做一些强转。

(proguardDebug即transformClassesAndResourcesWithProguardForDebug)



4、开启混淆后的Release签名打包,如果debug模式不开启混淆的话,会将所有类都打包成补丁。

这是因为,如果debug模式不开启混淆,那么就会拿不混淆的代码和Release已经混淆的代码进行校验,md5肯定不一致,所以会将所有类打包成补丁包。

解决方式:

暂时没有好办法,老老实实开启混淆吧。Debug是否开启混淆要和Release保持一致



5、如何签名补丁

补丁的签名主要用到的是jdk的工具,jarsigner.exe。使用代码调用命令行即可

6、如何进行签名校验

首先,debug安装的app不需要进行校验,这是检测当前app是否是debug签名的方法。

/article/1559591.html

然后,这是校验补丁包和app签名是否一致

/article/2934829.html

7、android 6.0无法从sdcard加载补丁包

运行时权限机制的问题,可以将补丁包放到app私有空间加载。

8、 androidStudio 2.0以上用到了instantRun,这是否会对debug自动生成补丁包产生影响。

这个问题我还没有测试,如果真的有影响的话也有很简单的解决方式,直接使用签名打包debug也可以生成补丁包。



三、参考项目

https://github.com/jasonross/Nuwa

https://github.com/bunnyblue/DroidFix

https://github.com/Livyli/AndHotFix

主要是第三个,签名校验的思路来源于它

四、写在后面

终于算是完成了热补丁框架了,其中过程真的累人啊!

整个框架的实现思路比较清晰简单,代码量也不超过1000行,很适合正在学习这个技术的朋友们。

求star,求star,第一个上传到github的项目求star (。・`ω´・)

https://github.com/AItsuki/HotFix
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: