概述:
热修复其实很简单,通俗理解就找到有bug的apk和无bug的apk的差异生成一个.apatch(按照AndFix使用)结尾的文件,通过预先固定的通道从网上下载无bug的代码替换有bug的代码,从而实现bug的修复,最关键的是用户体验好,如果按照正常的流程操作的话需要开发人员修复完bug后打包经过测试人员测试后,上传到多个应用市场;通过热修复的方法就省去了很大的人力物力成本。
1 热修复的原理
Android的类加载器有两种:PathClassLoader和DexClassLoader,两者的父类是BaseDexClassLoader,BaseDexClassLoader的父类是ClassLoader
其中PathDexLoader用来加载系统类和应用类;
DexClassLoader用来加载一些jar、apk、dex文件,其实jar和apk文件实际上加载的都是dex文件
热修复原理:ClassLoader会遍历一个由dex文件组成的数组,然后加载其中的dex文件,我们会把正确的dex(修复过的类所在的dex)文件插入数组的前面,当加载器加载到好 的类文件时候就不会加载有bug的类了,就实现了热修复
2 热修复例子
本案例使用的是阿里的开源热修复框架AndFix
1 首先建立一个App类继承自Application
public class App extends Application {
private static final String TAG = "App";
//生成的apatch文件名
public static final String APATCH_PATCH = "jiang.apatch";
private PatchManager mPatchManager;
@Override
public void onCreate() {
super.onCreate();
//初始化
mPatchManager = new PatchManager(this);
String appversion=null;
try {
//这个地方要是用此方法获取versionName
appversion= getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
Log.i("aaa",""+appversion);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
mPatchManager.init(appversion);
//加载apatch
mPatchManager.loadPatch();
//apatch文件的目录
String patchFileString = Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator+APATCH_PATCH;
Log.i("aaa","App:"+patchFileString);
File apatchPatch = new File(patchFileString);
if (apatchPatch.exists()){
Log.i(TAG, "补丁文件存在");
try {
mPatchManager.addPatch(patchFileString);
} catch (IOException e) {
Log.i(TAG, "打补丁出错了");
e.printStackTrace();
}
}else {
Log.i(TAG, "补丁文件不存在");
}
}
}
这里只是模拟,其实真正的是会通过固定的网络通道下载.apatch文件,然后进行更新
2 下面是模拟修复前和修复后的代码
修复前:
public class MainActivity extends AppCompatActivity {
public Button mBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtn = (Button) findViewById(btn);
String patchFileString = Environment.getExternalStorageDirectory().getAbsolutePath()+ File.separator+APATCH_PATCH;
Log.i("aaa",""+"MainActivity"+patchFileString);
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "出现bug了", Toast.LENGTH_SHORT).show();
}
});
}
}
修复后:
public class MainActivity extends AppCompatActivity {
public Button mBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtn = (Button) findViewById(btn);
String patchFileString = Environment.getExternalStorageDirectory().getAbsolutePath()+ File.separator+APATCH_PATCH;
Log.i("aaa",""+"MainActivity"+patchFileString);
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "修复bug了", Toast.LENGTH_SHORT).show();
}
});
}
}
分别打包成apk文件:bug.apk和nobug.apk(注意要对apk文件签名)
3 然后需要使用到一个生成补丁的工具apkapatch
文件目录如下
需要将签名文件放到当前文件中,使用命令
apkpatch.bat -f nobug.apk -t bug.apk -o jiang -k mzbanner.jks -p 123456 -a w -e 123456
其中参数的意义是
-f代表修复好的apk -t代表有问题的apk -k代表签名文件 -p代表签名文件的密码 -a代表别名 -e代表别名密码
使用cmd命令打开后运行就会生成一个jiang的文件夹 里面就会有一个.apatch文件生成
我们利用反编译可以看看里面的内容
可以看到类名用_CF结尾,方法名用@MethodReplace注释,通过这种方式就可以找到需要替换的类和方法
4 最后看看结果吧
运行bug.apk的界面
当把jiang.apatch文件放入指定的SD目录后,重新打开app就会发现神奇一幕
以上就是热修复的简单案例,欢迎指正