请珍惜劳动者的汗水,一分耕耘一分收获,转载请注明出处,谢谢!
初衷:如今热更新越来越火,各大厂也陆续开源自己的热更新框架。目前主流的热更新大概有以下一些,未统计到的望给予补充。
正好年底了有点时间,也总结一下。
1、Tinker 微信 2、QZone QQ空间 3、阿里 AndFix 4、美团 Robust 5、Nuwa 俗称女娲 。。。。。。
下面就本人用到的Tinker做个分析,以及具体的实现步骤。
傻瓜式SDK Tinker进入参考文档:http://tinkerpatch.com/Docs/intro
当然还要一种是源码直接接入,自己管理后台服务,稍微复杂麻烦点,可参考github上Tinker的官方介绍:https://github.com/Tencent/tinker
具体的原理和差异算法大家可以去参考下官方文档
差异算法结构图:
对比以上各优势果断选择了Tinker,主要看中的是资源文件的替换,当然有一点不满意的就是无法及时生效,不过也不影响,还可以使用锁屏自动生效的方式,这一点还是挺好的!
2、集成步骤:
新建项目成功后配置build.gradle,有三处的gradle需要配置。由于文档并未完全标明,其中一个gradle的配置还是费了点精力^_^。最后还是再同事的一句话点醒了。
2.1 至于我们的Application可以使用我们平常项目中正常的方式,只需稍加盖上即可。
public class SampleApplication extends Application{ @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); TinkerPatch.init(TinkerPatchApplicationLike.getTinkerPatchApplicationLike()); TinkerPatch tinkerPatch = TinkerPatch.with(); tinkerPatch.fetchPatchUpdate(true); //向后台请求补丁升级配置;true,即每次调用都会真正的访问后台是否有更新。 } }
gradle结构:
都需要我们配置,其中tinkerpatch.gradle 需要手动建立,然后在 2 中进行引用
具体看下第1个 的内容:
buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.2.3' // TinkerPatch 插件 classpath "com.tinkerpatch.sdk:tinkerpatch-gradle-plugin:1.0.1" } } allprojects { repositories { jcenter() } } task clean(type: Delete) { delete rootProject.buildDir }
下面先看下 第3个 gradle的内容:
apply plugin: 'tinkerpatch-support' def bakPath = file("${buildDir}/basekApk/") def tempPath = "app-1.0.0-0113-10-26-03" /** * 对于插件各参数的详细解析请参考 * http://tinkerpatch.com/Docs/SDK */ tinkerpatchSupport { tinkerEnable = true appKey = "你要去申请的appkey" appVersion = "${APP_VERSIONNAME}" reflectApplication = true autoBackupApkPath = "${bakPath}" baseApkFile = "${bakPath}/${tempPath}/app-release.apk" //app-debug.apk baseProguardMappingFile = "${bakPath}/${tempPath}/app-release-mapping.txt" //app-debug-mapping.txt baseResourceRFile = "${bakPath}/${tempPath}/app-release-R.txt" //app-debug-R.txt } /** * 一般来说,我们无需对下面的参数做任何的修改 * 对于各参数的详细介绍请参考: * https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97 */ tinkerPatch { ignoreWarning = false useSign = true dex { dexMode = "jar" pattern = ["classes*.dex"] loader = [] } lib { pattern = ["lib/*/*.so"] } res { pattern = ["res/*", "r/*", "assets/*", "resources.arsc", "AndroidManifest.xml"] ignoreChange = [] largeModSize = 100 } packageConfig { } sevenZip { zipArtifact = "com.tencent.mm:SevenZip:1.1.10" // path = "/usr/local/bin/7za" } buildConfig { keepDexApply = false } }
很简单
继续第2点,也没啥好将的,就是需要将第3个gradle进行apply
apply from: 'tinkerpatch.gradle'
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.0.0'
testCompile 'junit:junit:4.12'
compile "com.android.support:multidex:1.0.1"
//若使用annotation需要单独引用,对于tinker的其他库都无需再引用
provided("com.tencent.tinker:tinker-android-anno:1.7.6") { changing = true }
compile("com.tinkerpatch.sdk:tinkerpatch-android-sdk:1.0.1"){ changing = true }
}
至此Tinker的集成已经搞定!!!下面就是打包,编译差异包的过程了。下面还是列下官方的实现步骤吧,依葫芦画瓢,so sasy !
参考:http://tinkerpatch.com/Docs/SDK
集成完毕后将项目切换到Project模式下,可以看到这样的结构
展开Gradle projects 编译打包,其中tinker下是编译产生差异包
buildscript {
repositories {
jcenter()
}
dependencies {
// TinkerPatch 插件
classpath "com.tinkerpatch.sdk:tinkerpatch-gradle-plugin:1.0.2"
}
}
注意,在这里SDK使用了fat打包的模式,我们不能再引入任何 Tinker 的相关依赖,否则会造成版本冲突。当前SDK是基于 tinker 1.7.6 内核开发的。
添加TinkerPatch SDK 库的 denpendencies 依赖, 可参考 Sample 中的 app/build.gradle:
dependencies {
//若使用annotation需要单独引用,对于tinker的其他库都无需再引用
provided("com.tencent.tinker:tinker-android-anno:1.7.6")
compile("com.tinkerpatch.sdk:tinkerpatch-android-sdk:1.0.2")
}
注意,若使用 annotation 自动生成 Application, 需要单独引入 Tinker的 tinker-android-anno 库。除此之外,我们无需再单独引入 tinker 的其他库。
为了简单方便,我们将 TinkerPatch 相关的配置都放于 tinkerpatch.gradle 中, 我们需要将其引入:
apply from: 'tinkerpatch.gradle'
打开引入的 tinkerpatch.gradle 文件,它的具体参数如下:
tinkerpatchSupport {
tinkerEnable = true
appKey = "f828475486f91936"
appVersion = "1.0.0"
autoBackupApkPath = "${bakPath}"
baseApkFile = "${bakPath}/${appName}/app-debug.apk"
baseProguardMappingFile = "${bakPath}/${appName}/app-debug-mapping.txt"
baseResourceRFile = "${bakPath}/${appName}/app-debug-R.txt"
}
它的具体含义如下:
参数 | 默认值 | 描述 |
---|---|---|
tinkerEnable | true | 是否开启 tinkerpatchSupport 插件功能。 |
appKey | "" | 在 TinkerPatch 平台 申请的 appkey, 例如 sample 中的 'f828475486f91936' |
appVersion | "" | 在 TinkerPatch 平台 输入的版本号, 例如 sample 中的 '1.0.0'。 注意,我们使用 appVersion 作为 TinkerId, 我们需要保证每个发布出去的基础安装包的 appVersion 都不一样。 |
reflectApplication |
false | 是否反射 Application 实现一键接入;一般来说,接入 Tinker 我们需要改造我们的 Application, 若这里为 true, 即我们无需对应用做任何改造即可接入。 |
autoBackupApkPath | "" | 将每次编译产生的 apk/mapping.txt/R.txt 归档存储的位置 |
baseApkFile | "" | 基准包的文件路径, 对应 tinker 插件中的 oldApk 参数 ;编译补丁包时,必需指定基准版本的 apk,默认值为空,则表示不是进行补丁包的编译。 |
baseProguardMappingFile | "" | 基准包的 Proguard mapping.txt 文件路径, 对应 tinker 插件 applyMapping 参数 ;在编译新的 apk 时候,我们希望通过保持基准 apk 的 proguard 混淆方式,从而减少补丁包的大小。这是强烈推荐的,编译补丁包时,我们推荐输入基准 apk 生成的 mapping.txt 文件。 |
baseResourceRFile | "" | 基准包的资源 R.txt 文件路径, 对应 tinker 插件 applyResourceMapping 参数 ;在编译新的apk时候,我们希望通基准 apk 的 R.txt 文件来保持 Resource Id 的分配,这样不仅可以减少补丁包的大小,同时也避免由于 Resource Id 改变导致 remote view 异常。 |
一般来说,我们无需修改引用 android 的编译配置,也不用修改 tinker 插件原来的配置。针对特殊需求,具体的参数含义可参考 Tinker 文档:Tinker 接入指南.
最后在我们的代码中,只需简单的初始化 TinkerPatch 的 SDK 即可,我们无需考虑 Tinker 是如何下载/合成/应用补丁包, 也无需引入各种各样 Tinker 的相关类。
若我们使用 reflectApplication 模式,我们无需为接入 Tinker 而改造我们的 Application 类。初始化 SDK 可参考 tinkerpatch-easy-sample 中的 SampleApplication 类.
public class SampleApplication extends Application {
...
public void attachBaseContext(Context base) {
TinkerPatch.init(TinkerPatchApplicationLike.getTinkerPatchApplicationLike());
}
...
我们将 Tinker 加载补丁过程的结果存放在 TinkerPatchApplicationLike 中。
若我们已经完成了应用的 Application 改造,即将 Application 的逻辑移动到 ApplicationLike类中。我们可以参考 tinkerpatch-sample 中的 SampleApplicationLike 类.
public class SampleApplicationLike extends DefaultApplicationLike {
...
public void onBaseContextAttached(Context base) {
TinkerPatch.init(this);
}
...
}
TinkerPatch 的使用步骤非常简单,一般来说可以参考以下几个步骤:
assembleRelease
task 构建基准包,即将要发布的版本;autoBackupApkPath
保存编译的产物 apk/mapping.txt/R.txt 文件;baseApkFile
/baseProguardMappingFile
/baseResourceRFile
参数中;tinkerPatchRelease
task 构建补丁包,补丁包将位于 build/outputs/tinkerPatch
中。搞定!
后面就是将差异包提交的Tinker的Web上了。
差异包的发布可根据需要选择条件,不再啰嗦了。大家可以自己去看下介绍,没什么难度。有不懂的可以直接评论,第一次写这么长的博客,自我感觉都很烂,但这是一个锻炼的过程,而且没有足够的时间来总结,只是碰巧今天又点时间就先写上来了。公司一直都忙忙忙啊!!!!!受不了了,再看看咱们的IOS多爽,3个月没做过项目,爽死了!不过谁叫我们是Android屌丝呢^_^,吐槽归吐槽,还是要把工作干好!最后期待18号年会能给我抽个大奖啊!!!
各位见笑了,有问题Call我,能解决的一定尽力,帮不到的一起努力!祝自己2017工作顺利,身体健康,家庭和睦!祝大家步步高升!