Tinker使用进阶

Tinker多渠道打包支持

这里我们先在Gradle引入多渠道打包的支持:

//多渠道脚本支持
productFlavors {
    googleplayer {
        manifestPlaceholders = [UMENG_CHANNEL_VALUE: "googleplayer"]
    }

    baidu {
        manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"]
    }

    productFlavors.all {
        flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
    }
}

相关的渠道信息在清单文件中进行配置:

<application
    android:name=".App"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

    ...

    <!-- 友盟统计相关meta-data -->
    <meta-data
        android:name="UMENG_APPKEY"
        android:value="57bfe3cbe0f55a0b2e0025b8" />
    <meta-data
        android:name="UMENG_CHANNEL"
        android:value="${UMENG_CHANNEL_VALUE}" />

</application>

最后,我们在上一篇文章的App的Gradle文件的tinkerPatch块的最后按照官方Demo添加多渠道打包的支持(核心逻辑就是不同渠道的APK、Patch的拷贝,拷贝到指定的目录方便我们拾取):

//多渠道打包支持
project.afterEvaluate {
    if (hasFlavors) {
        task(tinkerPatchAllFlavorRelease) {
            group = 'tinker'
            def originOldPath = getTinkerBuildFlavorDirectory()
            for (String flavor : flavors) {
                def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Release")
                dependsOn tinkerTask
                def preAssembleTask = tasks.getByName("process${flavor.capitalize()}ReleaseManifest")
                preAssembleTask.doFirst {
                    String flavorName = preAssembleTask.name.substring(7, 8).toLowerCase() + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 15)
                    project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release.apk"
                    project.tinkerPatch.buildConfig.applyMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release-mapping.txt"
                    project.tinkerPatch.buildConfig.applyResourceMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release-R.txt"
                }
            }
        }

        task(tinkerPatchAllFlavorDebug) {
            group = 'tinker'
            def originOldPath = getTinkerBuildFlavorDirectory()
            for (String flavor : flavors) {
                def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Debug")
                dependsOn tinkerTask
                def preAssembleTask = tasks.getByName("process${flavor.capitalize()}DebugManifest")
                preAssembleTask.doFirst {
                    String flavorName = preAssembleTask.name.substring(7, 8).toLowerCase() + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 13)
                    project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug.apk"
                    project.tinkerPatch.buildConfig.applyMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug-mapping.txt"
                    project.tinkerPatch.buildConfig.applyResourceMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug-R.txt"
                }

            }
        }
    }
}

最后,在配置基准包(也就是old.apk)的信息的时候,直接写基准包所在的路径即可:

ext {
    tinkerEnable = true
    tinkerID = "1.0"
    tinkerOldApkPath = "${bakPath}/app-1111-15-29-27"
    tinkerApplyMappingPath = "${bakPath}/app-1111-15-29-27"
    tinkerApplyResourcePath = "${bakPath}/app-1111-15-29-27"
    tinkerBuildFlavorDirectory = "${bakPath}/app-1111-15-29-27"
}

Gradle同步成功之后,就可以使用如下的命令生成Patch文件:

./gradlew tinkerPatchAllRelease

或者  

./gradlew tinkerPatch渠道名字Release

Tips:需要注意的事,向服务器请求Patch的时候,就需要带上渠道名。

Tinker自定义行为

如果要对Tinker的行为进行自定义,例如修改默认行为的重启、增加数据埋点统计、文件MD5检测等,就需要调用这个install方法。

sCustomPatchListener = new CustomPatchListener(getApplication());
TinkerInstaller.install(
        mApplicationLike,
        new DefaultLoadReporter(getApplication()),
        new DefaultPatchReporter(getApplication()),
        sCustomPatchListener,
        CustomResultService.class,
        new UpgradePatch()
);

这个install方法,除了传入ApplicationLike以外,还需要其他参数,例如:

  • 补丁加载的时候的报告者
  • 补丁应用的时候的报告者
  • 与补丁加载的逻辑有关的UpgradePatch对象

上述一般使用Tinker默认的即可,下面我们介绍一些我们常用的:

一般我们会自定义一个PatchListener,监听Patch的加载过程,在里面我们进行Patch文件的MD5检验:

public class CustomPatchListener extends DefaultPatchListener {

    private String currentMD5;

    public void setCurrentMD5(String md5Value) {
        this.currentMD5 = md5Value;
    }

    public CustomPatchListener(Context context) {
        super(context);
    }

    @Override
    protected int patchCheck(String path, String patchMd5) {
        //patch文件ms5较验
        if (!Utils.isFileMD5Matched(path, currentMD5)) {
            return ShareConstants.ERROR_PATCH_DISABLE;
        }
        return super.patchCheck(path, patchMd5);
    }

}

然后,我们也会自定义一个ResultService改变Patch安装之后的行为:防止Tinker在加载完补丁之后,直接把APP的进程重启:

/**
 * 本类的作用:决定在patch安装完以后的后续操作,默认实现是杀进程
 */
public class CustomResultService extends DefaultTinkerResultService {
    private static final String TAG = "Tinker.SampleResultService";

    //返回patch文件的最终安装结果
    @Override
    public void onPatchResult(PatchResult result) {
        if (result == null) {
            TinkerLog.e(TAG, "DefaultTinkerResultService received null result!!!!");
            return;
        }
        TinkerLog.i(TAG, "DefaultTinkerResultService received a result:%s ", result.toString());

        //first, we want to kill the recover process
        TinkerServiceInternals.killTinkerPatchServiceProcess(getApplicationContext());

        // if success and newPatch, it is nice to delete the raw file, and restart at once
        // only main process can load an upgrade patch!
        if (result.isSuccess) {
            deleteRawPatchFile(new File(result.rawPatchFilePath));
        }
    }
}

需要注意的是,CustomResultService跟一般的Service一样,必须要在清单中配置。

Tinker组件化

组件化的概念是:将一个大的软件系统按照分离关注点的形式,拆分成多个独立的组件,已达到降低模块之间耦合的目的。组件化的思想最早用于服务器端开发。

下面介绍组件化的过程。

1、没有使用组件化思想的时候,模块都是很杂乱的,模块之间的耦合度很高:

image.png

2、在单工程之下实现组件化的框架图如下所示,各个层次之间十分明确:

image.png

3、在单工程之下实现组件化的基础上,进一步抽取模块,实现多个工程(Module)的组件化:

image.png

组件化的优点比较多:

  1. 每个组件都有自己独立的版本,可以独立的编译、安装、测试,最大程度地达到互不干扰
  2. 团队的分工更加明确,小团队专注于自己的业务模块
  3. 提高代码的可复用性,提高团队的效率

具体代码实现的话,跟之前的AndFix组件化一样,都是自定义一个FixService,在闪屏页开启这个FixService。FixService的实现按照AndFix的流程进行封装即可:

AndFix组件化封装.png

Tinker的注意事项

最后,谈一下Tinker的注意事项:

  1. Tinker不支持加固,但是后续版本估计会支持
  2. Tinker支持修改资源文件,但是不支持修改清单文件
  3. TInker不支持新增四大组件、Transition动画、桌面图标等
  4. Tinker不支持部分三星Android21的机型

关于Tinker的原理,将在下一篇文章中继续...相信看完下一篇文章,回过头来看这篇文章,就一目了然了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355

推荐阅读更多精彩内容