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

一、关于前面四篇博文

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 定义热补丁框架的使用方式

  1. release签名打包作为发布版本,每次release打包都会重新生成hash.txt和mapping.txt(开启混淆的情况下才有mapping)
  2. 每次debug运行的时候(直接运行项目或者buildapk),都会通过校验hash.txt和mapping.txt生成已签名补丁包。

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

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

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签名的方法。

http://blog.csdn.net/luohai859/article/details/44679085

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

http://blog.csdn.net/hudashi/article/details/8245105

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

时间: 2024-10-12 13:13:26

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

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

一.前言 在上一章中,我们使用javassist成功为项目注入了System.out.println(AntilazyLoad.class);这行代码,解决了class_ispreverified问题,可以正常使用了,但肯定还存在着很多未知的问题. 首先是javassist的问题 class中使用到的类,必须添加到classpool的classpath中,我在Demo中写了一个自定义控件,注入代码的时候报错,提示没有找到Context,然后我将android.jar整个添加到classpath之

App像Web一样发布新版本,安卓App热补丁动态修复技术介绍 转

背景 当一个App发布之后,突然发现了一个严重bug需要进行紧急修复,这时候公司各方就会忙得焦头烂额:重新打包App.测试.向各个应用市场和渠道换包.提示用户升级.用户下载.覆盖安装.有时候仅仅是为了修改了一行代码,也要付出巨大的成本进行换包和重新发布. 这时候就提出一个问题:有没有办法以补丁的方式动态修复紧急Bug,不再需要重新发布App,不再需要用户重新下载,覆盖安装? 解决方案 该方案基于的是android dex分包方案的,关于dex分包方案,网上有几篇解释了,所以这里就不再赘述,具体可

Android 热补丁动态修复框架小结

转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/49883661: 本文出自:[张鸿洋的博客] 一.概述 最新github上开源了很多热补丁动态修复框架,大致有: https://github.com/dodola/HotFix https://github.com/jasonross/Nuwa https://github.com/bunnyblue/DroidFix 上述三个框架呢,根据其描述,原理都来自:安卓App热补丁

仿百度壁纸client(六)——完结篇之Gallery画廊实现壁纸预览已经项目细节优化

仿百度壁纸client(六)--完结篇之Gallery画廊实现壁纸预览已经项目细节优化 百度壁纸系列 仿百度壁纸client(一)--主框架搭建,自己定义Tab + ViewPager + Fragment 仿百度壁纸client(二)--主页自己定义ViewPager广告定时轮播图 仿百度壁纸client(三)--首页单向,双向事件冲突处理,壁纸列表的实现 仿百度壁纸client(四)--自己定义上拉载入实现精选壁纸墙 仿百度壁纸client(五)--实现搜索动画GestureDetector

仿百度壁纸客户端(六)——完结篇之Gallery画廊实现壁纸预览已经项目细节优化

仿百度壁纸客户端(六)--完结篇之Gallery画廊实现壁纸预览已经项目细节优化 百度壁纸系列 仿百度壁纸客户端(一)--主框架搭建,自定义Tab + ViewPager + Fragment 仿百度壁纸客户端(二)--主页自定义ViewPager广告定时轮播图 仿百度壁纸客户端(三)--首页单向,双向事件冲突处理,壁纸列表的实现 仿百度壁纸客户端(四)--自定义上拉加载实现精选壁纸墙 仿百度壁纸客户端(五)--实现搜索动画GestureDetector手势识别,动态更新搜索关键字 仿百度壁纸客

Eclipse下配置Ant脚本 自己主动打包带签名的Android apk

尽管eclipse非常少用了,可是在古老的项目上还是会用到.一个麻烦事是打带签名包的时候.非常不方便.下边纪录下配置ant,自己主动打包带签名apk的过程,作为备忘.(PC环境为MAC) 1,第一步得安ant,下载相应安装包,解压后配置环境变量: export ANT_HOME="/Users/yanzi/work/apache-ant-1.9.4" export PATH=${PATH}:${ANT_HOME}/bin 通过which ant检查是否成功安装. 2,在项目文件夹下执行

Eclipse下配置Ant脚本 自动打包带签名的Android apk

虽然eclipse很少用了,但是在古老的项目上还是会用到.一个麻烦事是打带签名包的时候,非常不方便.下边纪录下配置ant,自动打包带签名apk的过程,作为备忘.(PC环境为MAC) 1,第一步得安ant,下载对应安装包,解压后配置环境变量: export ANT_HOME="/Users/yanzi/work/apache-ant-1.9.4" export PATH=${PATH}:${ANT_HOME}/bin 通过which ant检查是否安装成功. 2,在项目目录下运行:and

Windows系统下Ant自动编译打包并签名Android应用——实践篇

1.背景 项目进行到一定程度了,每次发布前,产品要求打包测试,都需要手动从eclipse中导出签名APK,很是麻烦.于是想起了之前探索过的Ant自动签名打包.查看自己之前写的博客Windows系统下Ant自动编译打包并签名Android应用--探索篇,边动手边复习.但是后面遇到了一大串的问题... 2.问题解决过程 1).libs库中的第三方包 如果项目只是引用了第三方jar包,只要将jar包放入项目libs目录下就可以了,ant编译的时候会自动把第三方jar包加入进去的,没有任何问题. 2).

Android热补丁动态修复

1.前言 由于公司项目中使用到热修复技术,之前对这块技术知之甚少,所以有时间去学习了解了一下. 2.学习资源 2.1 热修复介绍 还是鸿洋老师的精彩讲解,中间引用了Andorid dex分包方案和QQ空间的热修复的技术贴. 2.2 其他相关知识 pathclassloader和dexclassloader的区别如下:1.DexClassLoader和PathClassLoader都属于符合双亲委派模型的类加载器(因为它们没有重载loadClass方法).也就是说,它们在加载一个类之前,回去检查自