Android热修复相关

背景和原理

当app发布之后如果出现了紧急的线上bug,整个公司都会为此忙的焦头烂额,而bug的原因可能仅仅是传错了参数,或者写错一行代码,而且修复后的app又得重新上架,直到用户更新后bug才会被修正。热修复技术的出现就能很大程度上缓解这种情况,修复后不需要重新上架,用户也不需要重新下载安装。

github上的热修复框架如nuwa,HotFix原理都是依据安卓App热补丁动态修复技术介绍和Android dex分包方案 这两篇文章。 热修复原理是基于Android的分包方案的,那么什么是Android的分包方案呢,Android2.3之前执行dexopt的内存只有5M,每个dex的方法数不能超过65535,当app功能复杂,类和方法特别多的时候就会在编译时报错。Android dex分包方案中的描述:

解决办法是将编译好的class文件打成两个dex的包,运行时注入ClassLoader。如何注入呢,先看一下Android的ClassLoader体系。

Android热修复相关_第1张图片

可以看到实现类有两个DexClassLoader和PathClassLoader,其中PathClassLoader是用来Android用来加载Android系统类和应用的加载器,DexClassLoader用来加载.dex和.jar中的class.dex文件。看一下BaseDexClassLoader加载类的方法,从pathList里根据类名找,找不到就class not found。pathList是BaseDexClassLoader中的一个对象,它包含一个dexElements集合,找类就是听过遍历这个集合,拿到dexFile去找类。

一个ClassLoader可以包含多个dex文件,每个dex文件是一个Element,多个dex文件排列成一个有序的数组dexElements,当找类的时候,会按顺序遍历dex文件,然后从当前遍历的dex文件中找类,如果找类则返回,如果找不到从下一个dex文件继续查找。相关代码如下:

 

这样我们将有bug的类打成patch.jar然后插入到dexElements的最前边位置,findclass的时候就会从我们的patch.jar 中开始找,找到类之后就返回,有问题的类就被补丁包中的替换掉了。之后还有一个CLASS_ISPREVERIFIED的问题,详细可以看安卓App热补丁动态修复技术介绍主要是说,如果类和其引用类如果不在同一个dex包里就会报错,校检出这个错的前提是引用类被打上了CLASS_ISPREVERIFIED标识,这个标识是什么时候被打上的呢,在虚拟机启动的时候如果一个类的private,static,或者构造方法直接引用到的类都在同一个dex包里就会给当前类打上这个标识,所以要阻止这个表示被打上,我们要让类引用一个外部dex包里的类,往所有的类的构造函数中插入一段

 

 

所以我们还需要一个hack.dex,写个空的类AntilazyLoad打成dex包,而且必须先加载这个包,否则引用这个类的地方就会class not fount,加载方法就和之前的一样啦,要注意Application作为应用的入口不能插入这段代码。(因为载入hack.dex的代码是在Application中onCreate中执行的,如果在Application的构造函数里面插入了这段代码,那么就是在hack.dex加载之前就使用该类,该类一次找不到,会被永远的打上找不到的标志)。

热修复框架nuwa的使用

demo位置https://github.com/nononopro/nuwaExample

配置

1.在project的biuld.gradle中加入

classpath 'cn.jiajixin.nuwa:gradle:1.2.2'
之后我们的文件类似于这样
 repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.2.1'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
        classpath 'cn.jiajixin.nuwa:gradle:1.2.2'
    }

2.在app的build.gradle添加

compile 'cn.jiajixin.nuwa:nuwa:1.0.0'
apply plugin: "cn.jiajixin.nuwa"

然后构建,注意如果构建过程中出现错误 

Error:Cannot get property 'taskDependencies' on null object 
是因为gradle版本是1.5,将其回退到1.2.1

3.在项目的BaseApplication中添加

  protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        Nuwa.init(this);   
    }
到此基本配置完成,这个过程可能会遇到一些问题我们后边再说。

使用

使用很简单Nuwa.loadPatch(this,patchFile) 需要加载的地方加载你的补丁包patch.jar,通常在app启动的时候。这里我放在BaseApplication里。

 

补丁包的制作

以我的demo为例,MyApplication编译运行然后进入C:\MyProject\MyApplication\app\build\outputs`将文件夹下的nuwa文件夹拷贝到一个目录下,我们这里拷贝到C:\nuwa,现在将项目拷贝一份放入其它目录修改其中的类,相当于改bug的过程,修改完成后进入项目根目录如图 

Android热修复相关_第2张图片

在这里打开命令窗口输入 gradlew clean nuwaDebugPatch -P NuwaDir=C:/nuwa

会在当前工程的app\build\outputs\nuwa\debug目录下生成补丁包patch.jar 
将补丁包copy进手机adb push patch.jar /sdcard/,这个补丁包正常应该放在服务器,下载得到。

重启app,可以看到补丁包被正确加载

使用过程中遇到的一些问题汇总

1.刚才说过的Error:Cannot get property 'taskDependencies' on null object 
gradle版本1.5会报这个错,回退至1.2.1解决

2.运行报错

Error:Execution failed for task ':app:nuwaClassBeforeDexDebug'. > 1

解决方法将debug模式也添加混淆 
`buildTypes { 
debug { 
minifyEnabled true 
proguardFiles getDefaultProguardFile(‘proguard-Android.txt’), ‘proguard-rules.pro’ 

release { 
minifyEnabled true 
proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’ 
}

3.打包时作者给的命令是./gradlew clean nuwaQihooDebugPatch -P NuwaDir=/Users/jason/Documents/nuwa后边的NuwaDir我们知道了就是我们拷贝出来的nuwa文件夹的路径,nuwaQihooDebugPatch 注意这个参数其中的qihoo是根据渠道生成的,我们自己如果不指定渠道没这个文件夹自然会找不到路径,我这里没有设置所以是这样gradlew clean nuwaDebugPatch -P NuwaDir=C:/nuwa

4.Could not find class ‘cn.jiajixin.nuwa.Hack’, referenced from method a.a.a.a.b.解决:proguard-rules.pro中加入-keep class cn.jiajixin.nuwa.** { *; }防止被混淆

5.E/nuwa: /storage/emulated/0/patch.jar is null如果你的读取路径都没问题看一下权限是否打开  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

6.Java.lang.NoClassDefFoundError: cn.jiajixin.nuwa.Hack 如果报这个错那么将你的报错的BaseApliication在app的gradle中如下配置,nuwa{ }与android{}平级 ,这样会让BaseApplication不做注入操作,因为这时候还没有加载hack.dex所以找不到
nuwa { 
excludeClass = ['BaseApplication.class'] 
}

参考链接

https://github.com/jasonross/Nuwa/issues 
https://github.com/jasonross/Nuwa 
https://zhuanlan.zhihu.com/p/20308548

想看自动打包的源码讲解的话看看这个博主的文章Android 热修复Nuwa的原理及Gradle插件源码解析

你可能感兴趣的:(Android热修复相关)