这篇只是记录tinker的集成sdk
现在市场上的热修复工具或者框架有很多很多,比较出名的有阿里的 AndFix、美团的 Robust 以及 QZone 的超级补丁方案。这个方案各有利弊,但是今天我们不说这几个方案,我们来聊一聊另一个补丁方案——Tinker。首先我们上一张图:
Tinker热补丁方案·不仅支持类、So以及资源的替换,它还是2.X-7.X的全平台支持。
一:集成sdk
第一步 添加 gradle 插件依赖
在项目的Gradle添加依赖
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.2'
// TinkerPatch 插件
classpath "com.tinkerpatch.sdk:tinkerpatch-gradle-plugin:1.1.8"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
//注意:gradle3.0版本以上的后有不兼容的问题应该,会一直报错,改成2.3版本的没有问题
第二步 集成 TinkerPatch SDK
在app中的gradle添加denpendencies 依赖,注意:这两个gradle不是同一个gradle,上面的那个build gradle 是整个项目的,下面这个build gradle是在app里面的,注意区分
dependencies { // 若使用annotation需要单独引用,对于tinker的其他库都无需再引用 provided("com.tinkerpatch.tinker:tinker-android-anno:1.8.0") compile("com.tinkerpatch.sdk:tinkerpatch-android-sdk:1.1.8") }
为了简单方便,我们将 TinkerPatch 相关的配置都放于 tinkerpatch.gradle 中, 我们需要将其引入:
1 apply from: 'tinkerpatch.gradle'
第三步 配置 tinkerpatchSupport 参数
好了,接下来我们就在app目录下创建这个tinkerpatch.gradle,如图所示的目录
打开tinkerpatch.gradle,将 TinkerPatch 相关的配置都放于tinkerpatch.gradle中。
1 apply plugin: 'tinkerpatch-support' 2 3 /** 4 * TODO: 请按自己的需求修改为适应自己工程的参数 5 */ 6 7 //基包路径 8 def bakPath = file("${buildDir}/bakApk/") 9 //基包文件夹名(打补丁包的时候,需要修改) 10 def baseInfo = "app-1.0.0-0220-09-08-09" 11 //版本名称 12 def variantName = "debug" 13 14 /** 15 * 对于插件各参数的详细解析请参考 16 * 17 */ 18 tinkerpatchSupport { 19 //可以在debug的时候关闭 tinkerPatch 20 tinkerEnable = true 21 //是否使用一键接入功能 默认为false 是否反射 Application 实现一键接入; 22 // 一般来说,接入 Tinker 我们需要改造我们的 Application, 若这里为 true, 即我们无需对应用做任何改造即可接入。 23 reflectApplication = true 24 //将每次编译产生的 apk/mapping.txt/R.txt 归档存储的位置 25 autoBackupApkPath = "${bakPath}" 26 appKey = "1a56e0adbf67926d"// 注意!!! 需要修改成你的appkey 27 28 /** 注意: 若发布新的全量包, appVersion一定要更新 **/ 29 appVersion = "1.0.2" 30 31 def pathPrefix = "${bakPath}/${baseInfo}/${variantName}/" 32 def name = "${project.name}-${variantName}" 33 /** 34 * 基准包的文件路径, 对应 tinker 插件中的 oldApk 参数;编译补丁包时, 35 * 必需指定基准版本的 apk,默认值为空,则表示不是进行补丁包的编译 36 */ 37 baseApkFile = "${pathPrefix}/${name}.apk" 38 39 /** 40 * 基准包的 Proguard mapping.txt 文件路径, 对应 tinker 插件 applyMapping 参数;在编译新的 apk 时候, 41 * 我们希望通过保持基准 apk 的 proguard 混淆方式, 42 * 从而减少补丁包的大小。这是强烈推荐的,编译补丁包时,我们推荐输入基准 apk 生成的 mapping.txt 文件。 43 */ 44 baseProguardMappingFile = "${pathPrefix}/${name}-mapping.txt" 45 /** 46 * 基准包的资源 R.txt 文件路径, 对应 tinker 插件 applyResourceMapping 参数;在编译新的apk时候, 47 * 我们希望通基准 apk 的 R.txt 文件来保持 Resource Id 的分配,这样不仅可以减少补丁包的大小, 48 * 同时也避免由于 Resource Id 改变导致 remote view 异常 49 */ 50 baseResourceRFile = "${pathPrefix}/${name}-R.txt" 51 /** 52 * 若有编译多flavors需求, 可以参照: https://github.com/TinkerPatch/tinkerpatch-flavors-sample 53 * 注意: 除非你不同的flavor代码是不一样的,不然建议采用zip comment或者文件方式生成渠道信息(相关工具:walle 或者 packer-ng) 54 **/ 55 } 56 57 /** 58 * 用于用户在代码中判断tinkerPatch是否被使能 59 */ 60 android { 61 defaultConfig { 62 buildConfigField "boolean", "TINKER_ENABLE", "${tinkerpatchSupport.tinkerEnable}" 63 } 64 } 65 /** 66 * 一般来说,我们无需对下面的参数做任何的修改 67 * 对于各参数的详细介绍请参考: 68 * https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97 69 */ 70 tinkerPatch { 71 ignoreWarning = false 72 useSign = true //是否需要签名,打正式包如果这里是true,则要配置签名,否则会编译不过去 73 dex { 74 dexMode = "jar" 75 pattern = ["classes*.dex"] 76 loader = [] 77 } 78 lib { 79 pattern = ["lib/*/*.so"] 80 } 81 82 res { 83 pattern = ["res/*", "r/*", "assets/*", "resources.arsc", "AndroidManifest.xml"] 84 ignoreChange = [] 85 largeModSize = 100 86 } 87 packageConfig { 88 } 89 sevenZip { 90 zipArtifact = "com.tencent.mm:SevenZip:1.1.10" 91 // path = "/usr/local/bin/7za" 92 } 93 buildConfig { 94 keepDexApply = false 95 } 96 }
这里还需要注意一个地方,就是appKey,在我们登录tinker的官网,并且添加一个app版本以后,都会生成一个appkey,我们要把自己的appkey填到上面的配置中,附上一张图
第四步 初始化 TinkerPatch SDK
官方给了我们两种方式来初始化TinkerPatch SDK,第一种是reflectApplication = true 的情况,另一种reflectApplication = false的情况,今天我们说reflectApplication = true这种情况,另一种情况,大家可以到官网中看看
简单来说一下这两种情况的区别啊,当reflectApplication = true这种情况是不需要更改我们项目的Application类,而reflectApplication = false的情况是需要改动Application这个类。
初始化TinkerPatch SDK
1 package zhangxiaoqiang.com.myapplication; 2 3 /** 4 * author: Administrator 5 * created on: 2019/2/19 16:57 6 * description: 7 */ 8 9 import android.app.Application; 10 11 import com.tencent.tinker.loader.app.ApplicationLike; 12 import com.tinkerpatch.sdk.TinkerPatch; 13 import com.tinkerpatch.sdk.loader.TinkerPatchApplicationLike; 14 15 16 public class tinkerApplication extends Application { 17 18 private ApplicationLike tinkerApplicationLike; 19 @Override 20 public void onCreate() { 21 super.onCreate(); 22 23 if (BuildConfig.TINKER_ENABLE) { 24 // 我们可以从这里获得Tinker加载过程的信息 25 tinkerApplicationLike = TinkerPatchApplicationLike.getTinkerPatchApplicationLike(); 26 27 // 初始化TinkerPatch SDK, 更多配置可参照API章节中的,初始化SDK 28 TinkerPatch.init(tinkerApplicationLike) 29 .reflectPatchLibrary() 30 .setPatchRollbackOnScreenOff(true) 31 .setPatchRestartOnSrceenOff(true); 32 33 // 每隔3个小时去访问后台时候有更新,通过handler实现轮训的效果 34 new FetchPatchHandler().fetchPatchWithInterval(3); 35 } 36 } 37 }
1 package zhangxiaoqiang.com.myapplication; 2 3 /** 4 * author: Administrator 5 * created on: 2019/2/19 14:44 6 * description: 7 */ 8 9 10 import android.os.Handler; 11 import android.os.Message; 12 13 import com.tinkerpatch.sdk.TinkerPatch; 14 15 16 17 public class FetchPatchHandler extends Handler { 18 19 public static final long HOUR_INTERVAL = 3600 * 1000; 20 private long checkInterval; 21 22 23 /** 24 * 通过handler, 达到按照时间间隔轮训的效果 25 */ 26 public void fetchPatchWithInterval(int hour) { 27 //设置TinkerPatch的时间间隔 28 TinkerPatch.with().setFetchPatchIntervalByHours(hour); 29 checkInterval = hour * HOUR_INTERVAL; 30 //立刻尝试去访问,检查是否有更新 31 sendEmptyMessage(0); 32 } 33 34 35 @Override 36 public void handleMessage(Message msg) { 37 super.handleMessage(msg); 38 //这里使用false即可 39 TinkerPatch.with().fetchPatchUpdate(false); 40 //每隔一段时间都去访问后台, 增加10分钟的buffer时间 41 sendEmptyMessageDelayed(0, checkInterval + 10 * 60 * 1000); 42 } 43 }
1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="zhangxiaoqiang.com.myapplication"> 4 5 <uses-permission android:name="android.permission.INTERNET"/> 6 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 7 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> 8 9 <application 10 android:allowBackup="true" 11 android:icon="@mipmap/ic_launcher" 12 android:label="@string/app_name" 13 android:roundIcon="@mipmap/ic_launcher_round" 14 android:supportsRtl="true" 15 android:name=".tinkerApplication" 16 android:theme="@style/AppTheme"> 17 <activity android:name=".MainActivity"> 18 <intent-filter> 19 <action android:name="android.intent.action.MAIN" /> 20 21 <category android:name="android.intent.category.LAUNCHER" /> 22 </intent-filter> 23 </activity> 24 </application> 25 26 </manifest>
好了,到这里,集成Tinker的代码基本上已经完成了,现在运行看看如图
双击assembleDebug就可以运行项目了,结果如图:
然后在app-->build下有个app-debug.apk的包,我们将这个包安装到手机上,就相当于是我们本地的应用,在里面我加了一句话,如图
接着我们打有补丁的包,打补丁的包之前,我们的改两个地方,在tinkerpatch.gradle中找到如图中的代码,我们需要将baseInfo,和variantName改成和自己的一直如图:
改完以后,我们就可以打补丁的包了,如图
将baseInfo的值改为 右图中1所对应的值,variantName的值改为右图2对应的值。改完以后,我们就可以打补丁的包了,如图
然后在build --> outputs --> tinkerPatch 中就可以找到我们的补丁的包了,如图
最后将我们的补丁包上传到tinker官网,就可以使用了