本篇文章主要介绍AndFix的基本使用,以实用为主,对于深层次的原在此不做过多解释。
AndFix 全称是Android hot-fix。是阿里开源的一个热补丁框架,允许APP在不重新发布版本的情况下修复线上的bug。支持Android 2.3 到 6.0,并且支持arm 与 X86系统架构的设备。完美支持Dalvik与ART的Runtime,补丁文件是以 .apatch 结尾的文件。它的实现原理概括一句话是:AndFix热补丁原理就是在 native 动态替换方法 java 层的代码,通过 native 层hook java 层的代码。
缺点:
- 无法添加新类(内部类也不行)和新的字段、新的方法?自己试了方法可以
- 资源文件无法替换 试了下换原有的图片可以,但是新增的不行
- 不能修改xml布局文件 不能
- 加固后的包补丁无法使用,如果要加固,需要加固前的包来生成补丁,不过这样生成的补丁也很容易破解
- 不能对同一个方法修复两次,否则App根本跑不起来
- 对加载过的补丁文件要做名字修改 如果名字重叠 就不会再次加载
优点:
- 因为是动态的,所以不需要重启应用就可以生效
- 支持ART与Dalvik
- 与multidex方案相比,性能会有所提升(Multi Dex需要修改所有class的class_ispreverified标志位,导致运行时性能有所损失)
- 支持新增加方法
- 支持在新增方法中新增局部变量
以上优缺点看一遍,你就会发现,这个框架是方法级别的修复,话不多说,直接上代码。我这个例子不是严格意义上 的修复,只是一个修改,来验证
AndFix方法的修复。
我在HomeActivity中给一个button按钮定义了一个点击事件,但没有具体的实现。
public class HomeActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
findViewById(R.id.btn).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
});
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
findViewById(R.id.btn).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
});
}
打包成one.apk 我们认为是一个有bug的包
然后我们给这个点击事件一个具体实现。就是跳到OneAcitvity
public class HomeActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
findViewById(R.id.btn).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(HomeActivity.this, OneActivity.class);
startActivity(intent);
}
});}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
findViewById(R.id.btn).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(HomeActivity.this, OneActivity.class);
startActivity(intent);
}
});}
打包为two.apk 我们认为他是个修复过的包。
接下来介绍一个工具apkPath (在我们demo中的tools文件夹中)它可以对比有bug 和没有bug的apk文件,得到需要的信息,把信息打包为apatch文件,也就是我们的
补丁文件。使用方式如下:
把之前生成的one.apk和two.apk,还有打包所使用的keystore文件放到apkpatch-1.0.3目录下
打开cmd,进入到apkpatch-1.0.3目录下,输入如下指令
apkpatch.bat -f two.apk -t one.apk -o out -k 111111 -p 111111 -a 111111 -e 111111
每个参数含义如下
-f 新版本的apk
-t 旧版本的apk
-o 输出apatch文件的文件夹,可以随意命名
-k 打包的keystore文件名
-p keystore的密码
-a keystore 用户别名
-e keystore 用户别名的密码
生成的文件夹内会有一个.apatch文件,这个就是我们需要加载的补丁文件。怎么加载这个补丁,进行修复呢?
看代码:
public class MyApp extends Application{
private static final String TAG = "euler";
private static final String APATCH_PATH = "/out.apatch";
private static final String DIR = "apatch";//补丁文件夹
/**
* patch manager
*/
private PatchManager mPatchManager;
@Override
public void onCreate() {
super.onCreate();
mPatchManager = new PatchManager(this);
mPatchManager.init("1.0");
mPatchManager.loadPatch();
try {
String patchFileString = Environment.getExternalStorageDirectory()
.getAbsolutePath() + APATCH_PATH;
mPatchManager.addPatch(patchFileString);
//复制且加载补丁成功后,删除下载的补丁
File f = new File(this.getFilesDir(), DIR + APATCH_PATH);
if (f.exists()) {
boolean result = new File(patchFileString).delete();
if (!result)
Log.e(TAG, patchFileString + " delete fail");
}
} catch (IOException e) {
Log.e(TAG, "", e);
}
}
}
private static final String TAG = "euler";
private static final String APATCH_PATH = "/out.apatch";
private static final String DIR = "apatch";//补丁文件夹
/**
* patch manager
*/
private PatchManager mPatchManager;
@Override
public void onCreate() {
super.onCreate();
mPatchManager = new PatchManager(this);
mPatchManager.init("1.0");
mPatchManager.loadPatch();
try {
String patchFileString = Environment.getExternalStorageDirectory()
.getAbsolutePath() + APATCH_PATH;
mPatchManager.addPatch(patchFileString);
//复制且加载补丁成功后,删除下载的补丁
File f = new File(this.getFilesDir(), DIR + APATCH_PATH);
if (f.exists()) {
boolean result = new File(patchFileString).delete();
if (!result)
Log.e(TAG, patchFileString + " delete fail");
}
} catch (IOException e) {
Log.e(TAG, "", e);
}
}
}
在项目的Application中对补丁加载器进行一个初始化,和补丁的加载。把我们上面生成的补丁文件更名为
out.apatch,放到手机内存卡中,运行程序就能修复我们的bug了。
运行图:有bug的apk点击没有响应
修复后的运行图:
我这个demo是在eclipse中做的,我会把基于eclipse的依赖一起奉上,as直接引用就行
dependencies {
compile 'com.alipay.euler:andfix:0.3.1@aar'
}
源码下载(包含补丁文件及AndFix依赖)