亲自实践Andfix 流程记录

来源:互联网 发布:苹果 淘宝 编辑:程序博客网 时间:2024/05/29 19:11

今天心血来潮,写一个最近在探究的热更新。网上有很多资料和教程,github也有阿里官方的介绍(官方库),但是每个人遇到的情况不同 ,可能会出现各种问题,这里我就以我的情况记录一下,以备自己和大家参考借鉴。

首先描述一下我的环境:

电脑:mac os x 10.11.3 

安卓开发工具:android studio 2.2.2。

测试手机:readmi3

android版本:5.1.1 MIUI 8.0.1

首先,我也是查阅了很多资料和blog,了解了热修复的概念和基本实现原理。我就简单介绍一下热修复,所谓热修复,就如同AS的instant run ,可以在不重新安装apk的情况下,更改代码,实现代码更新。实现方式有很多种,市面上以阿里巴巴andfix和腾讯Tinker的比较出名。我这里亲测了andfix,使用比较简单,效果也出来了,只不过有一定限制,只能修改(增删改)类中的方法,对资源文件及其他文件无法修改。不过我相信虽然有此限制,但是阿里的团队都在用,说明是好用的。至于如何能做到够用,我猜测是使用预测类和方法来做到热更新。

下面就开始介绍我的试验流程

1 下载官方的demo,直接提取其中的导入sample文件夹中的AndFixDemo至AS,我是以eclipse的类型导入的,然后就自动转成了AS工程结构。

2 引入andfix库,两种方式,第一种直接gradle引入,但是无法修改源码,会有个问题,之后会提到

dependencies {    compile 'com.alipay.euler:andfix:0.5.0@aar'}
第二种就是引入第三方库

这种方式分两步,1 引入Andfix库,这个官方给的不是很好,我就参考了别的大神给的,虽然

AndFix
类中有个小bug ,修复后如下:
Runtime.getRuntime().loadLibrary("andfix");

当然载入的是so库,2 就是要在jniLibs中添加这个so文件,我为了减小apk大小 或者是偷懒,只在armeabi中添加了libandfix.so

我最后选择了第二种方式,因为如果使用第一种,那么PatchManager 的

public void addPatch(String path) throws IOException {

默认就会只更新相同名称的apatch一次,也就不能很好地实现多次fix,当然,如果每次名称不一样,那就还是会加载两个文件。看了PatchManager源码知道:

private static final String DIR = "apatch";//补丁文件夹

mPatchDir = new File(mContext.getFilesDir(), DIR);
FileUtil.copyFile(src, dest);// copy to patch's directory
andfix会把add的patch文件都复制到mPatchDir中,但是如果名字相同就不会再次去重复add,也就是说如果要保证多次fix,需要保证每次add的apatch文件名不同。

所以我就参考大神的做法,把PatchManager中的addPatch方法修改成:

public void addPatch(String path) throws IOException {    File src = new File(path);    File dest = new File(mPatchDir, src.getName());    if (!src.exists()) {        throw new FileNotFoundException(path);    }    if (dest.exists()) {        Log.d(TAG, "patch [" + src.getName() + "] has be loaded.");        boolean deleteResult = dest.delete();        if (deleteResult)            Log.e(TAG, "patch [" + dest.getPath() + "] has be delete.");        else {            Log.e(TAG, "patch [" + dest.getPath() + "] delete error");            return;        }    }    FileUtil.copyFile(src, dest);// copy to patch's directory    Patch patch = addPatch(dest);    if (patch != null) {        loadPatch(patch);    }}
基本思路是:每次addPatch的文件如果存在,就会先删除这个文件,然后重新add进去,这样就保证了每次修复文件的名称可以一样。

当然还有一步,为了减少不必要存储,我们把

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");}
从sd卡中push或下载的文件删除,代码如下

// .apatch file pathmPatchManager.addPatch(patchFileString);Log.d(TAG, "apatch:" + patchFileString + " added.");//这里我加了个方法,复制加载补丁成功后,删除sdcard的补丁,避免每次进入程序都重新加载一次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");}

3 接下来就是在MainApplication中初始化andfix和loadPatch,addPatch.

public void onCreate() {    super.onCreate();    String patchFileString = Environment.getExternalStorageDirectory()            .getAbsolutePath() + APATCH_PATH;    // initialize    mPatchManager = new PatchManager(this);    mPatchManager.init(BuildConfig.VERSION_NAME);    Log.d(TAG, "inited.");    // load patch    mPatchManager.loadPatch();    Log.d(TAG, "apatch loaded.");    // add patch at runtime    try {        // .apatch file path        mPatchManager.addPatch(patchFileString);        Log.d(TAG, "apatch:" + patchFileString + " added.");        //这里我加了个方法,复制加载补丁成功后,删除sdcard的补丁,避免每次进入程序都重新加载一次        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);    }}
4 现在可以在MainActivity实现热修复操作了。为了直观,我没有使用log的方式观察结果,我用了toast方式,在oncreate方法中toast一个当前时间,然后生成签名apk,作为old.apk 安装在手机上,然后修改toast的时间为当前时间,再次生成签名apk,作为new.apk.统统放在了桌面。

然后我们要把两个apk的不同通过工具生成apatch文件,我们就需要工具了apkpatch,这是官方提供的。怎么用呢?

首先我把下载的文件夹解压放在了桌面,右键文件在此文件夹下打开终端,不知道这个的可以百度。然后输入

./apkpatch.sh -f /Users/guolinyao/Desktop/new.apk -t /Users/guolinyao/Desktop/old.apk -o /Users/guolinyao/Desktop/ -k /Users/guolinyao/Documents/高顿实习平台/Hishixi签名文件/hishixi.keystore -p hishixi -a haishixi -e hishixi

当然我们的路径是不一样的:关于写法 规则在这里:

usage: apkpatch -f <new> -t <old> -o <output> -k <keystore> -p <***> -a <alias> -e <***> -a,--alias <alias>     keystore entry alias. -e,--epassword <***>   keystore entry password. -f,--from <loc>        new Apk file path. -k,--keystore <loc>    keystore path. -n,--name <name>       patch name. -o,--out <dir>         output dir. -p,--kpassword <***>   keystore password. -t,--to <loc>          old Apk file path.
生成了patch文件

我直接改成了out.apatch因为在初始化时,addpatch方法传的也是这个文件名,要对应!!!


接下来

把out.apatch放入scared
adb push  out.apatch的文件路径 sdcard/ 这个也不能错!


可以通过adb命令adb shell >cd sdcard > ls 查看是否存在该文件


好了,现在再次打开刚安装的app,你就会发现toast的时间变了。


最常规的过程讲完了,现在开始讲讲几个小心得。首先,关于加固,我是加固应用的,同样可以使用andfix但是需要在加固前apk制作apatch文件;关于版本更新,andfix默认在版本更新时会删除所有apatch文件;关于混淆,官方说明了;关于patch文件如何分发,我是想在服务端给一个标志是否下载patch文件,每次进入app判断,大家有好的建议希望能够一起分享哦!


demo下载


1 0
原创粉丝点击