Android Tinker热修复集成解析

首先解释下tinker是什么,官方如是解释:Tinker是微信官方的Android热补丁解决方案,它支持动态下发代码、So库以及资源,让应用能够在不需要重新安装的情况下实现更新。当然,你也可以使用Tinker来更新你的插件。看到这,应该知道它的厉害了吧,支持动态下发代码、So库以及资源,第二它已经正式运用于有7、8亿用户的微信上了;比起阿里的andifx只能用作方法替换,这太强大了;因此作为app的开发,我们应该了解怎么使用tinker。

由于tinker的基本集成官方也有说明,https://github.com/Tencent/tinker,因此基本的配置方面我就不说明了,重点说明下大家需要注意的地方。

1,module的gradle依赖后面带了 chang true如下:

provided('com.tencent.tinker:tinker-android-anno:1.7.7') { changing true }
compile('com.tencent.tinker:tinker-android-lib:1.7.7') { changing true }
2,tinker插件照抄官方sample便可

if (buildWithTinker()) {
    apply plugin: 'com.tencent.tinker.patch'

    tinkerPatch {
3,调试的时候如果 tinkerEnabled = true,而你的项目又没有使用git版本控制,记得修改此处,如下。否则编译不通过

def gitSha() {
    try {
//        String gitRev = 'git rev-parse --short HEAD'.execute(null, project.rootDir).text.trim()
        String gitRev = 100// 此参数变化,tinker会删除app下载过的patch
4,为了便于发布补丁包,ext位置处建议修改如我。这样每次打补丁包就不用麻烦去修改tinkerOldApkPath、tinkerApplyMappingPath、tinkerApplyResourcePath、tinkerBuildFlavorDirectory 这些变量了。

def bakName="app-debug-0401-16-44-51"//每次编译了新apk,注意记得修改此处
ext {
    //for some reason, you may want to ignore tinkerBuild, such as instant run debug build?
    tinkerEnabled = true

    //for normal build
    //old apk file to build patch apk
    tinkerOldApkPath = "${bakPath}/${bakName}.apk"
    //proguard mapping file to build patch apk
    tinkerApplyMappingPath = "${bakPath}/${bakName}-mapping.txt"
    //resource R.txt to build patch apk, must input if there is resource changed
    tinkerApplyResourcePath = "${bakPath}/${bakName}-R.txt"

    //only use for build all flavor, if not, just ignore this field
    tinkerBuildFlavorDirectory = "${bakPath}/${bakName}"
}

5,baseApk和相对应patch apk的gitRev要一致,tinker才会加载成功。每次发布新的版本(不是patch),记得修改gitRev的值,一般加1即可,新版本安装后tinker加载的时候就会把旧版本的patch删除


ok,到此配置方面的说明结束,下面上本文重点。

为了统计补丁下发的应用情况,我们需要做统计。看了下tinkerPatch的后台,感觉做的还听好的,试用挺好,就是免费版的使用次数有限,而付费版的公司又不愿意拿钱购买,因此打算自己实现下tinker的统计功能。

1,patch下载次数统计,这个因人而已,我的做法是通过downloadManager下载,下载成功后请求patch下载成功接口,更新下载成功数。

2,patch应用成功统计,自定义一个类MyTinkerResultService,以此统计patch加载成功数

package com.tinkertest;

import com.tencent.tinker.lib.service.DefaultTinkerResultService;
import com.tencent.tinker.lib.service.PatchResult;
import com.tencent.tinker.lib.util.TinkerLog;

/**
 * 补丁加载成功服务回调
 * 切记在manifest文件注册此service,否则会报找不到service
 * Created by 黄海 on 2017/4/1.
 */
public class MyTinkerResultService extends DefaultTinkerResultService {
    private static final String TAG = "Tinker.MyTinkerResultService";

    @Override
    public void onPatchResult(PatchResult result) {
        if (result != null) {
            TinkerLog.i(TAG, "补丁加载成功 patchInfo=" + result);
            /**
             * 补丁加载成功,note!此处不代表应用成功
             *此处做的事情发送请求告诉服务器补丁加载成功(if need)
             */
        }
        super.onPatchResult(result);
    }
}
判断服务器是否有最新补丁要加载,自定义一个类MyLoadReporter,

package com.tinkertest;

import android.content.Context;

import com.tencent.tinker.lib.reporter.DefaultLoadReporter;
import com.tencent.tinker.lib.tinker.Tinker;
import com.tencent.tinker.lib.util.TinkerLog;
import com.tencent.tinker.loader.shareutil.ShareConstants;

import java.io.File;

/**
 * 补丁应用统计
 * Created by 黄海 on 2017/4/1.
 */

public class MyLoadReporter extends DefaultLoadReporter {
    private static final String TAG = "Tinker.MyLoadReporter";
    private Context context;

    public MyLoadReporter(Context context) {
        super(context);
        this.context = context;
    }

    @Override
    public void onLoadResult(File patchDirectory, int loadCode, long cost) {
        if (loadCode == ShareConstants.ERROR_LOAD_OK) {
            /**
             * currentVersion 就是应用成功的的patch file 的文件MD5
             * 根据loadCode统计补丁应用情况:如果加载成功,则根据currentVersion与服务器最新的patch文件
             * md5做对比,以此判断是否有最新patch需要下载;如果加载失败(一般此版本没有发布过补丁),则
             * 请求服务器此版本是否有补丁,有则下载,无则不理会
             */
            String currentVersion = Tinker.with(context).getTinkerLoadResultIfPresent().currentVersion;
            TinkerLog.i(TAG, "当前补丁信息:md5=" + currentVersion);
        }
        super.onLoadResult(patchDirectory, loadCode, cost);
    }
}
然后在你的DefaultApplicationLike子类的onCreate方法把上面定义的两个类带入初始化tinker,

TinkerInstaller.install(this, new MyLoadReporter(getApplication()), new DefaultPatchReporter(getApplication()), new DefaultPatchListener(getApplication())
        , MyTinkerResultService.class, new UpgradePatch());
ok,这样就完成了tinker补丁的应用统计了,整体感觉下来难度中等。


下面附上本文的项目源码

https://github.com/haipinghuang/TinkerWithServer

再附上一个功能类似tinkerPatch服务端的代码,https://github.com/haipinghuang/tinkerServer

开发IDE是idea,采用spring+springmvc+mybatis+maven+mysql实现,数据库结构文件在webApp/static文件夹


欢迎大家star,提意见!!!



猜你喜欢

转载自blog.csdn.net/u014763302/article/details/68958321