亲自实践Andfix 流程记录

今天心血来潮,写一个最近在探究的热更新。网上有很多资料和教程,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 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");
}

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下载


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值