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]
    }
}

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



    ...

    
    
    


最后,我们在上一篇文章的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、没有使用组件化思想的时候,模块都是很杂乱的,模块之间的耦合度很高:

Tinker使用进阶_第1张图片
image.png

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

Tinker使用进阶_第2张图片
image.png

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

Tinker使用进阶_第3张图片
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的原理,将在下一篇文章中继续...相信看完下一篇文章,回过头来看这篇文章,就一目了然了。

你可能感兴趣的:(Tinker使用进阶)