AndFix
全称Android hot-fix,是alibaba的Android热修复框架,支持Android 2.3到6.0的版本,支持arm与X86系统架构,支持Dalvik和ART Runtime。
原理
AndFix的原理就是方法的替换,把有bug的方法替换成补丁文件中的方法。
原理-替换方法.png使用教程
1. 添加依赖和混淆
compile 'com.alipay.euler:andfix:0.4.0@aar'
}```
**混淆:**
-keep class * extends java.lang.annotation.Annotation
-keepclasseswithmembernames class * {
native <methods>;
}
**2. 在Application.onCreate()中初始化PatchManager**
public class AndFixApplication extends Application {
public static PatchManager mPatchManager;
@Override
public void onCreate() {
super.onCreate();
// 初始化patch管理类
mPatchManager = new PatchManager(this);
// 初始化patch版本
mPatchManager.init("1.0");
// String appVersion = getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
// mPatchManager.init(appVersion);
// 加载已经添加到PatchManager中的patch
mPatchManager.loadPatch();
}
}
**3. 找个地方加载补丁,我们这里为了演示,点击按钮进行加载补丁**
findViewById(R.id.main_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
update();
}
});
private static final String APATCH_PATH = "/fix.apatch"; // 补丁文件名
private void update() {
String patchFileStr = Environment.getExternalStorageDirectory().getAbsolutePath() + APATCH_PATH;
try {
AndFixApplication.mPatchManager.addPatch(patchFileStr);
} catch (IOException e) {
e.printStackTrace();
}
}
PatchManager的addPatch方法加载新补丁,项目中可以在下载补丁文件之后调用,这里为了演示就把补丁文件放在本地的SD卡中了,代码中patchFileStr就是补丁存放的位置,**.apatch**就是生成补丁文件的后缀名,fix就是补丁文件的名字,这里我们将其名字写死。
我们看一下打补丁之前MainActivity的所有代码
public class MainActivity extends AppCompatActivity {
private static final String APATCH_PATH = "/fix.apatch"; // 补丁文件名
private TextView mainTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainTv = (TextView) findViewById(R.id.main_tv);
mainTv.setText("点击Toast");
mainTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showToast();
}
});
findViewById(R.id.main_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
update();
}
});
}
private void showToast() {
Toast.makeText(this, "打补丁之前", Toast.LENGTH_LONG).show();
}
/**
* 动态更新,加载补丁文件
* @author zehua_chen
* create at 2016/8/3 14:35
*/
private void update() {
String patchFileStr = Environment.getExternalStorageDirectory().getAbsolutePath() + APATCH_PATH;
try {
AndFixApplication.mPatchManager.addPatch(patchFileStr);
} catch (IOException e) {
e.printStackTrace();
}
}
}
**4. 我们将以上应用打包,我们命名为andfix_v1.0**
![andfix_v1.0.png](http:https://img.haomeiwen.com/i1689990/fea1230a4b800532.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
**5. 修改MainActivity中的代码,打包成andfix_v2.0**
private void showToast() {
Toast.makeText(this, "打补丁之后", Toast.LENGTH_LONG).show();
}
mainTv.setText("加载了补丁之后");
上面代码可知,我们只修改TextView的文字和TextView点击之后的Toast内容,然后打包
![andfix_v2.0.png](http:https://img.haomeiwen.com/i1689990/6375e8976f9aba23.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
**6. 使用官方提供的工具[apkpatch](https://github.com/alibaba/AndFix/blob/master/tools/apkpatch-1.0.3.zip)生成.apatch补丁文件**
点击上面的链接下载apkpatch之后解压
![apkpatch.png](http:https://img.haomeiwen.com/i1689990/d694879c1ef977f8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
将两个apk文件和该app的签名文件放入到该目录中
![加入签名文件和apk.png](http:https://img.haomeiwen.com/i1689990/9217393c8a6016e1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
使用命令生成.apatch补丁文件,先cmd进入该目录
![进人apkpatch目录.png](http:https://img.haomeiwen.com/i1689990/86d824a35b3a5f2d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
使用命令生成补丁
![生成补丁文件.png](http:https://img.haomeiwen.com/i1689990/4ea5d937652ad95f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
上图所示则表示补丁包生成完成
<u>apkpatch.bat -f andfix_v2.0.apk -t andfix_v1.0.apk -o output -k AndFixKey.jks -p andfix -a andfix -e andfix</u>
<u>apkpatch.bat -f 新apk -t 旧apk -o 输出目录 -k app签名文件 -p 签名文件密码 -a 签名文件别名 -e 别名密码</u>
* -f <new.apk> :新apk
* -t <old.apk> : 旧apk
* -o <output> : 输出目录(补丁文件的存放目录)
* -k <keystore>: 打包所用的keystore
* -p <password>: keystore的密码
* -a <alias>: keystore 用户别名
* -e <alias password>: keystore 用户别名密码
生成的output文件:
![生成的output文件.png](http:https://img.haomeiwen.com/i1689990/388d2802c9ec436a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![apatch补丁文件.png](http:https://img.haomeiwen.com/i1689990/ebf6c69cc76890e5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
将生成的.apatch补丁文件改成代码中写死的fix.apatch
![改名成fix.apatch.png](http:https://img.haomeiwen.com/i1689990/8d2aeead99d107d1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
**7.运行andfix_v1.0查看修复之前的效果,把补丁文件push到sd卡目录下, 点击Button更新按钮加载补丁文件,再看其效果,到这里我们就完成了AndFix的热修复了**
![打开File Explorer.png](http:https://img.haomeiwen.com/i1689990/1c2eb8b8edbe4209.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![push fix.apatch文件到sd卡中.png](http:https://img.haomeiwen.com/i1689990/c9be1be105ffa32c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
* 运行andfix_v1.0没有点击动态更新按钮,点击TextView
![打补丁之前.jpg](http:https://img.haomeiwen.com/i1689990/cd3785eaec76e2f9.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
* 点击动态更新Button之后再Toast,我们发现Tost的内容变了,但是TextView的内容没换
![动态更新再Toast.jpg](http:https://img.haomeiwen.com/i1689990/f38474939eeb74c5.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
* 退出之后我们重新进入该应用就会发现TextView的内容也变化了。
![打补丁之后,退出再重新进入.jpg](http:https://img.haomeiwen.com/i1689990/5c5880c9d9525a77.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
网友评论
坑太多了啊
明明我放在sd根目录下的fix.apatch是有16329k
java.lang.NullPointerException: Attempt to invoke virtual method 'void com.alipay.euler.andfix.patch.PatchManager.addPatch(java.lang.String)' on a null object reference
at com.yejun.andfix.MainActivity.update(MainActivity.java:58)
at com.yejun.andfix.MainActivity.access$100(MainActivity.java:12)
at com.yejun.andfix.MainActivity$2.onClick(MainActivity.java:37)
at android.view.View.performClick(View.java:4785)
at android.view.View$PerformClick.run(View.java:19888)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5276)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:911)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:706)
12-23 11:43:29.051 1363-1380/? E/ActivityManager: Invalid thumbnail dimensions: 0x0
Process: com.cainiaotv.hotfix, PID: 3332
java.lang.NoSuchMethodError: Landroid/animation/Animator;.addListener
at com.cainiaotv.hotfix.MainActivity.showToast(MainActivity.java)
at com.cainiaotv.hotfix.MainActivity.access$000(MainActivity.java:16)
at com.cainiaotv.hotfix.MainActivity$1.onClick(MainActivity.java:46)
at android.view.View.performClick(View.java:4747)
at android.view.View$PerformClick.run(View.java:19454)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:146)
at android.app.ActivityThread.main(ActivityThread.java:5752)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1291)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1107)
at dalvik.system.NativeStart.main(Native Method)
大神 打补丁之后 点击toast 就报错 怎么回事啊
想问下一,你用下来有遇到什么坑吗?
-keep class * extends java.lang.annotation.Annotation
-keepclasseswithmembernames class * {
native <methods>;
}