首先说下前奏,在实际开发中,由于某些不确定的因素,软件在发布之后,获取存在着一些bug,有时候我们发布的软件比较大,有的有好几十M,所以不可能再让我们的软件去更新,用户体验非常差,目前市面上的大互联网公司都有热修复的框架,实际原理都差不多,这里我们选取阿里的ANDFIX框架详细讲解他的实现过程。
原理
AndFix的原理就是方法的替换,把有bug的方法替换成补丁文件中的方法。
![](https://i-blog.csdnimg.cn/blog_migrate/670e93c85b4429d240601afbc7ebab0d.jpeg)
优点:
重大bug,需要紧急修复
可以下次迭代修复的bug
影响用户体验的行为
无需重启
缺点:
- 无法添加新类(内部类也不行)和新的字段、新的方法?自己试了方法可以
- 资源文件无法替换 试了下换原有的图片可以,但是新增的不行
- 不能修改xml布局文件 不能
- 加固后的包补丁无法使用,如果要加固,需要加固前的包来生成补丁,不过这样生成的补丁也很容易破解
- 不能对同一个方法修复两次,否则App根本跑不起来
- 对加载过的补丁文件要做名字修改 如果名字重叠 就不会再次加载
![](https://i-blog.csdnimg.cn/blog_migrate/53bd4a6cc3ea524689c0e717bdb4dc0b.png)
使用教程:
1. 添加依赖
compile 'com.alipay.euler:andfix:0.4.0@aar'
2.初始化PatchManager
import com.alipay.euler.andfix.patch.PatchManager; /* * @项目名: HotFixAlibaba * @包名: com.huhai.hotfixalibaba * @文件名: AndFixApplication * @创建者: Administrator * @创建时间: 2017/3/18 9:19 * @描述: TODO */ 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(); } }
不要忘记了在清单文件中添加(重要,否则无法初始化application)
android:name=".AndFixApplication"
3.我选择是使用点击button的方式进行热修复。
原始的mainactivity代码
package com.huhai.hotfixalibaba; import android.os.Bundle; import android.os.Environment; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.TextView; import android.widget.Toast; import java.io.File; import java.io.IOException; 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.setText("加载了补丁之前"); 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(); 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; File apatchPath = new File(patchFileStr); if (apatchPath.exists()){ //Toast.makeText(this, "打补文件存在--修复了", Toast.LENGTH_LONG).show(); try { AndFixApplication.mPatchManager.addPatch(patchFileStr); } catch (IOException e) { e.printStackTrace(); } }else { Toast.makeText(this, "打补文件不存在", Toast.LENGTH_LONG).show(); } } }
4.编写到这里,就可以把程序跑起来了,注意要使用签名文件:
记住生成.jks文件,还有签名的apk文件包,将签名的apk文件自己修改名称,便于版本管理。
我选择的是将文件名称改为 andfix_v1.0.apk ,并且得到了andfix.jks文件。
先不要管这两个文件的位置,你先得到这两个文件,把他留着后面用
5.修改你的代码,假设之前的activity中有bug,此时需要修改bug
我选择的是修改了button的文字,以及弹出吐司的内容
package com.huhai.hotfixalibaba; import android.os.Bundle; import android.os.Environment; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.TextView; import android.widget.Toast; import java.io.File; import java.io.IOException; 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.setText("加载了补丁之后"); 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(); //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; File apatchPath = new File(patchFileStr); if (apatchPath.exists()){ Toast.makeText(this, "打补文件存在--修复了", Toast.LENGTH_LONG).show(); try { AndFixApplication.mPatchManager.addPatch(patchFileStr); } catch (IOException e) { e.printStackTrace(); } }else { Toast.makeText(this, "打补文件不存在", Toast.LENGTH_LONG).show(); } } }
6.再修改的基础上重新进行打包生成apk
修改名称你自己喜欢的便于管理
我修改的是---andfix_v2.0.apk
把他留着
这个时候,你应该有了两个apk,一个签名文件。
7.使用官方提供的工具apkpatch生成.apatch补丁文件
下载地址:https://github.com/alibaba/AndFix
解压后得到如下文件夹
看到没,然后把你的两个 apk还有签名文件放到该目录下。
8.使用工具生成补丁包。
命令的参数介绍:
上图所示则表示补丁包生成完成
apkpatch.bat -f andfix_v2.0.apk -t andfix_v1.0.apk -o output -k AndFixKey.jks -p andfix -a andfix -e andfix
apkpatch.bat -f 新apk -t 旧apk -o 输出目录 -k app签名文件 -p 签名文件密码 -a 签名文件别名 -e 别名密码
- -f <new.apk> :新apk
- -t <old.apk> : 旧apk
- -o <output> : 输出目录(补丁文件的存放目录)
- -k <keystore>: 打包所用的keystore
- -p <password>: keystore的密码
- -a <alias>: keystore 用户别名
- -e <alias password>: keystore 用户别名密码
这里需要注意的是,你第一次解压的时候是没有output文件夹的
到了这里,上面的文件就是工具帮我们对比了旧的apk,以及新的apk,然后生成的补丁文件。
9.加载补丁,在实际开发中,我们需要向服务器去下载这个补丁,下载存放在文件夹中,然后再去加载,为了方便跟大家一样我也是直接把文件放在手机的根目录下,然后在activity中去加载这个补丁。
10.查看效果
没有打补丁之前的效果:
将补丁文件放sd卡根目录下,点击button热更新加载之后:
效果就是这样了,是不是很简单,照着做,你也可以的
备注:我使用的andrroid studio开发工具,手机是杂牌手机 5.0系统,实测ok