Tinker 熱更新的使用
較為具體的技術實現過程
Step 1: 新增 gradle 依賴
- 在專案的 build.gradle 中新增 tinker-patch-gradle-plugin的依賴
buildscript { dependencies { // TinkerPatch 外掛 classpath "com.tinkerpatch.sdk:tinkerpatch-gradle-plugin:1.2.2" } } 複製程式碼
- 然後在app的gradle檔案app/build.gradle,我們需要新增tinker的庫依賴以及apply tinker的gradle外掛
dependencies { // TinkerPatch SDK 依賴 compile 'com.tinkerpatch.sdk:tinkerpatch-android-sdk:1.9.1' } 複製程式碼
//apply tinker外掛 apply plugin: 'com.tencent.tinker.patch' 複製程式碼
Step 2:設定 TinkerPatch 相關的配置
- 為了簡單方便,我們將 TinkerPatch 的相關配置都發在 tinkerpatch.gradle 中,建立他 TinkerPatch.gradle 檔案之後,在 app.gradle 中使用該檔案(apply from: 'tinkerpatch.gradle'),TinkerPatch.gradle 的引數如下:
apply plugin: 'tinkerpatch-support' /** * TODO: 請按自己的需求修改為適應自己工程的引數 */ def bakPath = file("build/backupApk/") def baseInfo = "app-1.0.2-1120-17-20-01" def variantName = "release" /** * 對於外掛各引數的詳細解析請參考 * http://tinkerpatch.com/Docs/SDK */ tinkerpatchSupport { /** 可以在debug的時候關閉 tinkerPatch, isRelease() 可以判斷BuildType是否為Release **/ tinkerEnable = true // 是否反射實現一鍵接入 reflectApplication = true /** * 是否開啟加固模式,只能在APK將要進行加固時使用,否則會patch失敗。 * 如果只在某個渠道使用了加固,可使用多flavors配置 **/ protectedApp = true /** * 實驗功能 * 補丁是否支援新增 Activity (新增Activity的exported屬性必須為false) **/ supportComponent = true autoBackupApkPath = "${bakPath}" appKey = "d5d1c96215a04b5c" /** 注意: 若釋出新的全量包, appVersion一定要更新 **/ /** Tinker 使用 appVersion 作為 TinkerId, 我們需要保證每個釋出出去的基礎安裝包的 appVersion 都不一樣。 **/ appVersion = "1.0.2" def pathPrefix = "${bakPath}/${baseInfo}/${variantName}/" def name = "${project.name}-${variantName}" /** 基準包的檔案路徑, 對應 tinker 外掛中的 oldApk 引數;編譯補丁包時,必需指定基準版本的 apk,預設值為空,則表示不是進行補丁包的編譯。 */ baseApkFile = "${pathPrefix}/${name}.apk" baseProguardMappingFile = "${pathPrefix}/${name}-mapping.txt" baseResourceRFile = "${pathPrefix}/${name}-R.txt" /** * (可選)重新命名備份檔案的格式化字串,預設為'${appName}-${variantName}' * * Available vars: * 1. projectName * 2. appName * 3. packageName * 4. buildType * 5. versionName * 6. versionCode * 7. buildTime * 8. fileSHA1 * 9. flavorName * 10. variantName * * default value: '${appName}-${variantName}' * Note: plz use single-quotation wrapping this format string */ // 格式化命名備份檔案 這裡請使用單引號 backupFileNameFormat = '${appName}-${variantName}' /** *若有編譯多flavors需求, 可以參照: https://github.com/TinkerPatch/tinkerpatch-flavors-sample *注意: 除非你不同的flavor程式碼是不一樣的,不然建議採用zip comment或者檔案方式生成渠道資訊(相關工具:walle 或者 packer-ng) **/ } /** * 用於使用者在程式碼中判斷tinkerPatch是否被使能 */ android { defaultConfig { buildConfigField "boolean", "TINKER_ENABLE", "${tinkerpatchSupport.tinkerEnable}" } } /** * 一般來說,我們無需對下面的引數做任何的修改 * 對於各引數的詳細介紹請參考: * 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 } } import java.util.regex.Matcher import java.util.regex.Pattern /** * 如果只想在Release中開啟tinker,可以把tinkerEnable賦值為這個函式的return * @return 是否為release */ def isRelease() { Gradle gradle = getGradle() StringtskReqStr = gradle.getStartParameter().getTaskRequests().toString() Pattern pattern; if (tskReqStr.contains("assemble")) { println tskReqStr pattern = Pattern.compile("assemble(\\w*)(Release|Debug)") } else { pattern = Pattern.compile("generate(\\w*)(Release|Debug)") } Matcher matcher = pattern.matcher(tskReqStr) if (matcher.find()) { String task = matcher.group(0).toLowerCase() println("[BuildType] Current task: " + task) return task.contains("release") } else { println "[BuildType] NO MATCH FOUND" return true; } } 複製程式碼
- 重要引數
引數 | 作用 |
---|---|
tinkerEnable | 是否開啟熱更新 |
reflectApplication | 是否反射實現一鍵接入 |
supportComponent | 是否支援新增 activity |
protectedApp | 是否開啟應用加固(360 加固或者樂固) |
appKey | 在 TinkerPatch 平臺 申請的 appkey |
appVersion | 若釋出新的全量包, appVersion一定要更新 |
autoBackupApkPath | 自動備份檔案的路徑 |
step 3:reflectApplication = true
- 為了更方便的使用,我們選擇使用 reflectApplication = true 的模式,這種模式我們不用為了接入 tinker 而改造我們的 Application 類。
public class SampleApplication extends Application { ... @Override public void onCreate() { super.onCreate(); // 我們可以從這裡獲得Tinker載入過程的資訊 tinkerApplicationLike = TinkerPatchApplicationLike.getTinkerPatchApplicationLike(); // 初始化TinkerPatch SDK, 更多配置可參照API章節中的,初始化SDK TinkerPatch.init(tinkerApplicationLike) .reflectPatchLibrary() .setPatchRollbackOnScreenOff(true) .setPatchRestartOnSrceenOff(true) .setFetchPatchIntervalByHours(3); // 每隔3個小時(通過setFetchPatchIntervalByHours設定)去訪問後臺時候有更新,通過handler實現輪訓的效果 TinkerPatch.with().fetchPatchUpdateAndPollWithInterval(); } ... 複製程式碼
step 4:執行 assembleRelease task(生成基準包 -- 釋出到應用商店)
-
執行 assembleRelease task 構建基準包(請在釋出前確保更新tinkerpatchSupport中的appVersion),tinkerPatch會基於你填入的autoBackupApkPath自動備份基礎包資訊到相應的資料夾,包含:apk檔案、R.txt檔案和mapping.txt檔案 (注:mapping.txt是proguard的產物,如果你沒有開啟proguard則不會有這個檔案)
- 備份的路徑
- 備份的檔案
-
這個步驟是我們一般的情況下的打包操作,如我們釋出到應用商店的版本名為:1.0.0,那麼之後我們釋出補丁的話,我們都是要在這個 1.0.0 的版本之上生成補丁包的。

step 5:在 TinkerPatch 平臺中新增 app,並獲取 appkey,填入到 tinkerpatch.gradle 中對於的 appkey 引數中
- 新增 app

- 獲取 appkey

step 6:釋出到應用商店的版本出現 bug,想要釋出補丁包
-
執行 tinkerPatchRelease task 構建補丁包,補丁包將位於 build/outputs/tinkerPatch下,預設的情況下,補丁包為:patch_signed.apk
- 執行 tinkerPatchRelease
- 執行 tinkerPatchRelease 之後,生成的補丁包路徑