整合tinker
對於原有app整合tinker,還是比較簡單的,根據tinker上的wiki的指示操作即可。 具體步驟如下:
- 在專案的build.gradle中新增 tinker-patch-gradle-plugin 的依賴
buildscript {
dependencies {
classpath 'com.tencent.tinker:tinker-patch-gradle-plugin:1.7.9'
}
}
複製程式碼
- 然後在app的gradle檔案app/build.gradle,我們需要新增tinker的庫依賴以及apply tinker的gradle外掛.
dependencies {
//可選,用於生成application類
provided 'com.tencent.tinker:tinker-android-anno:1.7.9'
//tinker的核心庫
compile 'com.tencent.tinker:tinker-android-lib:1.7.9'
}
...
...
//apply tinker外掛
apply plugin: 'com.tencent.tinker.patch'
複製程式碼
- 還需要配置
dexOptions {
jumboMode = true
}
複製程式碼
tinker的最佳實踐,防止由於字串增多導致force-jumbol,導致更多的變更 4. 最後可以把tinker相關的程式碼挪動到tinkerpatch.gradle中 5. 改造SampleApplication,通過SampleApplicationLike代理SampleApplication的行為。具體可以看wiki和SampleApplication 相關的改動可以參考 TinkerDemo
加固支援
從1.7.8開始,tinker又支援加固了,只需要修改tinkerpatch.gradle中的這部分
buildConfig {
applyMapping = getApplyMappingPath()
applyResourceMapping = getApplyResourceMappingPath()
tinkerId = getTinkerIdValue()
keepDexApply = false
isProtectedApp = true //開啟加固
}
複製程式碼
整合patchsdk
patchsdk 使用的是 https://github.com/baidao/tinker-manager/tree/master/patchsdk 步驟如下
- 需要在app/build.gradle中新增
repositories {
jcenter()
}
dependencies {
...
compile 'com.dx168.patchsdk:patchsdk:1.1.3'
}
複製程式碼
- 使用ApplicationLike代理原來的Application
@SuppressWarnings("unused")
@DefaultLifeCycle(application = "com.dx168.patchsdk.sample.MyApplication",
flags = ShareConstants.TINKER_ENABLE_ALL,
loadVerifyFlag = false)
public class MyApplicationLike extends TinkerApplicationLike {
public MyApplicationLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent) {
super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
}
@Override
public void onCreate() {
super.onCreate();
String appId = "20170112162040035-6936";
String appSecret = "d978d00c0c1344959afa9d0a39d7dab3";
PatchManager.getInstance().init(getApplication(), "http://xxx.xxx.xxx/hotfix-apis/", appId, appSecret, new ActualPatchManager() {
@Override
public void cleanPatch(Context context) {
TinkerInstaller.cleanPatch(context);
}
@Override
public void patch(Context context, String patchPath) {
TinkerInstaller.onReceiveUpgradePatch(context, patchPath);
}
});
PatchManager.getInstance().register(new Listener() {
...
});
PatchManager.getInstance().setTag("your tag");
PatchManager.getInstance().setChannel("");
PatchManager.getInstance().queryAndPatch();
}
}
複製程式碼
- 搭建補丁後臺管理系統 參考Readme 搭建補丁後臺,不過這個後臺沒有push下發補丁的功能,只有後臺下發後,客戶端pull拉取補丁,進行補丁修復操作 如果你的app用的是bugly來作為異常上報和運營統計,那麼可以直接使用bugly提供的後臺,具體操作請參考這裡, 小巫同學錄制了一系列相關的視訊,參考視訊教程即可學會,地址在這裡 ,bugly支援push下發,比自己搭建後臺更加有優勢,同樣,也可以使用TinkerPatch補丁管理後臺,不過是收費的。
生成渠道包
對於渠道包,如果不是需要使用熱修復,那麼怎麼生成渠道包都可以的。 對於flavor編譯渠道包,會導致不同的渠道包由於BuildConfig變化導致classes.dex差異,這種方案是不可取的。 將渠道資訊寫在apk檔案的zip comment中,是非常推薦的,例如可以使用專案packer-ng-plugin或者可使用V2 Scheme的walle, 也包括最新出來的多渠道打包神器ApkChannelPackage,說一下區別,如果要使用熱修復的話,對於不需要加固的app,那麼生成渠道包,這三種方案都可以採用;對於要加固的app,只能採用ApkChannelPackage這種方案中的根據已有APK生成渠道包(如果有其他的方案,請記得告訴我)。這篇文章對多渠道打包工具對比做了詳細的區分。目前採用的也是ApkChannelPackage方案。
生成補丁
以TinkerDemo為例
- 執行./gradlew assembleRelease 生成apk
- 使用梆梆加固工具加固apk,並簽名,得到加固並且重簽名的app_protected_signed.apk
- 使用./gradlew reBuildChannel 生成渠道包
- 修改TinkerDemo中的若干程式碼
- ./gradlew tinkerPatchRelease 生成patch補丁apk(需要保證補丁包tinkerId跟基線版本tinkerId一致)(也就是說,打補丁的時候不要commit程式碼,tinkerId是根據git commit生成的)
- 如果搭建了補丁管理後臺的話,使用後臺上傳補丁包,進行修復,根據log,可以觀察到結果
- 如果沒有搭建補丁管理後臺的話,使用adb push app/build/outputs/tinkerPatch/release/patch_signed_7zip.apk /storage/sdcard0/(參考tinker的sample工程,以及tinker接入文件)
測試補丁包
根據生成補丁的若干步驟,來測試補丁包是否有效,在第三步生成渠道包後,安裝其中的一個渠道apk到手機上,然後呢,我這邊是通過補丁管理後臺上傳的補丁,然後客戶端pull補丁,根據log,可以清晰的看到補丁是否下載完成,是否有效。下載完補丁後,會進行dex合成。然後在後臺或者螢幕關閉後app會被殺死,重啟後補丁才會生效,這個時候我們才真正修復了問題。
踩過的坑
- gradle打release包需要開啟簽名(./gradlew assembleRelease),不然的話,打patch包的時候,提示出錯(./gradlew tinkerPatchRelease)
- 打補丁包的時候,得保證基線版本是上一次發版本的apk,而且如果用的是tinker的預設形式的話,tinkerId得跟上一次發版本的apk的tinkerId一致,也就是基於上一次發版本的tag開bugfix分支,但是記得不要commit程式碼,這樣就保證了tinkerId一致(可以考慮是用app的版本號VERSION_NAME來作為tinkerId,每一次的發版,肯定是會修改版本號的),如果是bugly整合的tinker,請參考bugly官方文件
- 加固包跟渠道包如何相容的問題,一開始不太懂,如果有加固的需求如何做?。 我們需要搞明白的是,加固包不是基線版本,使用tinker打補丁包,用的是基線版本,得到基線版本後,我們會選擇梆梆加固,樂固,愛加密這樣的平臺對基線版本進行加固,得到加固包後,還需要注意的是,不能夠用普通的渠道包生成方案來打渠道包。舉例來說吧,我司的app,採用的是梆梆加固,加固後,工具自動幫你做了生成渠道包的操作,但是這種生成的渠道包實際上是有問題的,會導致無法打補丁,無法修復,tinker熱修復不生效。這種生成的渠道包,不同渠道對應的dex的CRC都不一樣,我們得保證打出來的渠道包的dex都是一樣的,通過apksigner可以知道梆梆加固的簽名工具採用的是v1簽名方案,那麼我們可以考慮在APK檔案的註釋欄位,新增渠道資訊。這樣就能保證不同渠道的dex是一致的。
- ApkChannelPackage
存在一點小問題,還需要作者修復,v1簽名的判斷邏輯有問題,加固後的apk,會改變META-INF/XXX.SF的名稱,這塊需要修改,不然./gradle reBuildChannel執行後,無法生成渠道包,對於開發者而言可以自己搭建本地localMaven,來測試修改這個repo作者已經修復若干問題 - 還有一些其他的坑已經記不得了。。。