Tinker熱修復整合總結

xingstarx發表於2018-01-18

整合tinker

對於原有app整合tinker,還是比較簡單的,根據tinker上的wiki的指示操作即可。 具體步驟如下:

  1. 在專案的build.gradle中新增 tinker-patch-gradle-plugin 的依賴
buildscript {
    dependencies {
        classpath 'com.tencent.tinker:tinker-patch-gradle-plugin:1.7.9'
    }
}
複製程式碼
  1. 然後在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'
複製程式碼
  1. 還需要配置
dexOptions {
        jumboMode = true
    }
複製程式碼

tinker的最佳實踐,防止由於字串增多導致force-jumbol,導致更多的變更 4. 最後可以把tinker相關的程式碼挪動到tinkerpatch.gradle中 5. 改造SampleApplication,通過SampleApplicationLike代理SampleApplication的行為。具體可以看wikiSampleApplication 相關的改動可以參考 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 步驟如下

  1. 需要在app/build.gradle中新增
repositories {
    jcenter()
}
dependencies {
    ...
    compile 'com.dx168.patchsdk:patchsdk:1.1.3'
}
複製程式碼
  1. 使用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();
     }
}
複製程式碼
  1. 搭建補丁後臺管理系統 參考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為例

  1. 執行./gradlew assembleRelease 生成apk
  2. 使用梆梆加固工具加固apk,並簽名,得到加固並且重簽名的app_protected_signed.apk
  3. 使用./gradlew reBuildChannel 生成渠道包
  4. 修改TinkerDemo中的若干程式碼
  5. ./gradlew tinkerPatchRelease 生成patch補丁apk(需要保證補丁包tinkerId跟基線版本tinkerId一致)(也就是說,打補丁的時候不要commit程式碼,tinkerId是根據git commit生成的)
  6. 如果搭建了補丁管理後臺的話,使用後臺上傳補丁包,進行修復,根據log,可以觀察到結果
  7. 如果沒有搭建補丁管理後臺的話,使用adb push app/build/outputs/tinkerPatch/release/patch_signed_7zip.apk /storage/sdcard0/(參考tinker的sample工程,以及tinker接入文件

測試補丁包

根據生成補丁的若干步驟,來測試補丁包是否有效,在第三步生成渠道包後,安裝其中的一個渠道apk到手機上,然後呢,我這邊是通過補丁管理後臺上傳的補丁,然後客戶端pull補丁,根據log,可以清晰的看到補丁是否下載完成,是否有效。下載完補丁後,會進行dex合成。然後在後臺或者螢幕關閉後app會被殺死,重啟後補丁才會生效,這個時候我們才真正修復了問題。

踩過的坑

  1. gradle打release包需要開啟簽名(./gradlew assembleRelease),不然的話,打patch包的時候,提示出錯(./gradlew tinkerPatchRelease)
  2. 打補丁包的時候,得保證基線版本是上一次發版本的apk,而且如果用的是tinker的預設形式的話,tinkerId得跟上一次發版本的apk的tinkerId一致,也就是基於上一次發版本的tag開bugfix分支,但是記得不要commit程式碼,這樣就保證了tinkerId一致(可以考慮是用app的版本號VERSION_NAME來作為tinkerId,每一次的發版,肯定是會修改版本號的),如果是bugly整合的tinker,請參考bugly官方文件
  3. 加固包跟渠道包如何相容的問題,一開始不太懂,如果有加固的需求如何做?。 我們需要搞明白的是,加固包不是基線版本,使用tinker打補丁包,用的是基線版本,得到基線版本後,我們會選擇梆梆加固,樂固,愛加密這樣的平臺對基線版本進行加固,得到加固包後,還需要注意的是,不能夠用普通的渠道包生成方案來打渠道包。舉例來說吧,我司的app,採用的是梆梆加固,加固後,工具自動幫你做了生成渠道包的操作,但是這種生成的渠道包實際上是有問題的,會導致無法打補丁,無法修復,tinker熱修復不生效。這種生成的渠道包,不同渠道對應的dex的CRC都不一樣,我們得保證打出來的渠道包的dex都是一樣的,通過apksigner可以知道梆梆加固的簽名工具採用的是v1簽名方案,那麼我們可以考慮在APK檔案的註釋欄位,新增渠道資訊。這樣就能保證不同渠道的dex是一致的。
  4. ApkChannelPackage 存在一點小問題,還需要作者修復,v1簽名的判斷邏輯有問題,加固後的apk,會改變META-INF/XXX.SF的名稱,這塊需要修改,不然./gradle reBuildChannel執行後,無法生成渠道包,對於開發者而言可以自己搭建本地localMaven,來測試修改這個repo 作者已經修復若干問題
  5. 還有一些其他的坑已經記不得了。。。

參考資料

Tinker常見問題 Android新一代多渠道打包神器 微信tinker補丁管理 TinkerDemo

相關文章