Tinker 常見問題

Tinker 官網

Tinker GitHub 主頁

Tinker 常見問題

大概目錄:
* Tinker 編譯相關問題?

* Tinker 庫中有什么類是不能修改的?

* 什么類需要放在主 dex 中?

* 我應該使用哪個作為補丁包下發,如何做多次修復?

* 如何對 Library 文件作補丁?

* 如何對資源文件作補丁,為什么有時候會提示大量沒有改變的圖片發生變更?

* Tinker 中的 dex 配置 'raw' 與 'jar' 模式應該如何選擇?

* 如何兼容多渠道包?

* tinker 是否兼容加固?

* Google Play 版本是否可以有 Tinker 相關代碼?

* tinker 與 instant run 的兼容問題?

* 每次編譯我應該保留哪些文件,如何兼容 AndResGuard?

* tinkerId 應該如何選擇?

* 如何使生成的補丁包更小?

* 關于使用的 ClassLoader 問題?

* 什么時候調用 installTinker?

* Proguard 5.2.1 applymapping 出現 Warning?

* TinkerPatch 補丁管理后臺與 Tinker 的關系?

* Tinker 的最佳實踐?

一、Tinker 編譯相關問題?

編譯過程相關的 issue 請先查看是否是以下情況:

  1. 無法打開 sample 工程: 請使用單獨的 IDE 窗口打開 tinker-sample-android 工程;

  2. tinkerId is not set:是因為沒有正確地配置 IDE 的 git 路徑,若不是通過 clone 方式下載 tinker,需要本地手動 commit 一次。這里你也可以使用其他字符作為 tinkerId

  3. 對于編譯與補丁時發生的異常,請到 Tinker 自定義擴展 中查看具體錯誤碼的原因。并通過 "Tinker." 過濾 Tinker 相關的日志提交到 issue 中;

  4. 若自定義 TinkerResultService,請務必將新的 Service 添加到 Manifest 中;

  5. 權限問題;請務必已經將讀取 sdk 權限添加到 AndroidManifest.xml 中,并且已允許權限運行;

  6. 若使用 DefaultLifeCycle 注解生成 Application,需要將原來 Application 的實現移動到 ApplicationLike 中,并將原來的 Application 類刪掉;

  7. 關于 Application 的改造這一塊大家比較疑惑,這塊請認真閱讀自定義 Application 類,大部分的 app 應該都能在半小時內完成改造。

  8. 如果出現 Class ref in pre-verified class resolved to unexpected implementation 異常,請確認以下幾點:Application中傳入 ApplicationLike 的參數時是否采用字符串而不是 Class.getName 方式;新的 Application 是否已經加入到 dex loader pattern 中;額外添加到 dex loader pattern 中類的引用類也需要加載到 loader pattern 中。

二、Tinker 庫中有什么類是不能修改的?

Tinker 庫中不能修改的類一共有 26 個,即 com.tencent.tinker.loader.* 類。加上你的 Appliction 類,只有 26 個類是無法通過 Tinker 來修改的。即使類似 Tinker.java 等管理類,也是可以通過 Tinker 本身來修改。

注意,在 1.7.6 版本之前,我們需要手動將不能修改的類添加到 tinkerPatch.dex.loader pattern 中。對于 1.7.6 以后的版本會自動生成。

但是若使用 newApk 或者命令行編譯,需要手動添加 Application 類與 loader 類

三、什么類需要放在主 dex 中?

Tinker 并不干涉你分包與多 dex 的加載邏輯,但是你需要確保以下幾點:

  1. com.tencent.tinker.loader.* 類,你的 Application 類需要在主 dex,并且已經在 dex.loader 中配置;

  2. 若你自定義了 TinkerLoader 類,你需要將 TinkerLoader 的自定義類,以及它用的到類也放在主 dex,并且已經在 dex.loader 中配置;

  3. ApplicationLike 的繼承類也需要放在主 dex 中,但是它無須在 dex.loader 中配置,因為它是可以使用 Tinker 修改的類。最后,如果你需要在加載其他 dex 之前加載 Tinker 的管理類,你也可以將 com.tencent.tinker.* 都加入到主 dex。

  4. 你的 ApplicationLike 實現類的直接引用類以及在調用 Multidex install 之前加載的類也都需要放到主 dex 中。

注意:Tinker 會自動生成需要放在主 dex 的 keep 規則。在 1.7.6 版本之前,你需要手動將生成規則拷貝到自己的 multiDexKeepProguard 文件中。例如 Sample 中的 multiDexKeepProguard file("keep_in_main_dex.txt")。在 1.7.6 版本之后,這里會通過腳本自動處理,無須手動填寫。

另外,如果 minsdkverion >= 21multiDexEnabled 會被忽略。我們可以在 build/intermediates/multi-dex 查找最終的 keep 規則以及結果。

四、我應該使用哪個作為補丁包下發,如何做多次修復?

patch_signed_7zip.apk 是已簽名并且經過 7z 壓縮的補丁包,但是你最好重命名一下,不要讓它以 .apk 結尾,這是因為有些運營商會挾持以 .apk 結尾的資源。

另外一點,我們在發起補丁請求時,需要先將補丁包先拷貝到 dataDir 中。因為在 sdcard 中,補丁包是極其容易被清理軟件刪除。這里可以參考 UpgradePatchRetry.java 的實現。

對于補丁包的版本問題,我們可以在 packageConfig 中增加,例如 sample 中的

packageConfig {
    /**
     * patch version via packageConfig
     */
     configField("patchVersion", "1.0")
}

Tinker 支持對同一基準版本做多次補丁修復,在生成補丁時,oldApk 依然是已經發布出去的那個版本。即補丁版本二的 oldApk 不能是補丁版本一,它應該依然是用戶手機上已經安裝的基準版本。

五、如何對 Library 文件作補丁?

當前官方并沒有直接將補丁的 lib 路徑添加到 DexPathList 中,理論上這樣可以做到程序完全沒有感知的對 Library 文件作補丁。這里主要是因為在多 abi 的情況下,某些機器獲取的并不準確。當前對 Library 文件作補丁可參考 Tinker API 概覽,tinker 1.7.7 版本中也提供了一鍵反射的方案給大家選擇。

大家可以根據自己的項目需要選擇合適的方案,事實上,無論是對 Library 還是 Application,官方都是采用盡量少去反射的策略,這也是為了提高 Tinker 框架的兼容性。上線前,我們應當嚴格測試補丁是否正確加載了修改后的 So 庫。

六、如何對資源文件作補丁,為什么有時候會提示大量沒有改變的圖片發生變更?

Tinker 采用全量合成方式實現資源替換,這里有以下幾點是使用者需要明確的:

  1. remoteView 是無法修改,例如 transition 動畫,notification icon 以及桌面圖標;

  2. 對于資源文件的更新(尤其是 assets),需要注意代碼中是否采用直接讀取 sourceApk 路徑方式讀取,這樣方式是無法更新的;
    Tinker 只會將滿足 res pattern 的資源放在最后的合成補丁資源包中。一般為了減少合成資源大小,官方不建議輸入 classes.dexlib 文件的 pattern

  3. 若一個文件 :assets/classes.dex,它既滿足 dex pattern,又滿足 res pattern。Tinker 只會處理 dex pattern,然后在合成資源包會忽略 assets/classes.dex 的變更。library 也是如此。

  4. 只要資源發生變成的前提下 Tinker 才會合成新的資源包,這一定程度會增加占 Rom 體積,請在考慮后使用。

注意:若出現資源變更,我們需要使用 applyResourceMapping 方式編譯,這樣不僅可以減少補丁包大小,同時防止 remote view id 變更造成的異常情況。最后我們應該查看編譯過程中生成的 resources_out.zip 是否滿足我們的要求。

有時候會發現大量明明沒有改變的 png 發現變更,解壓發現的確兩次編譯這些 png 的 md5 不一致。經分析,aapt 在其中一次編譯將 png 優化成 8-bit,另外一次卻沒有,從而導致 png 改變了。如果你們 app 出現了這種情況,官方建議關閉 aaptpng 的優化:

aaptOptions{
    cruncherEnabled false
}

若你對安裝包大小非常 care,可以提前使用命令行工具將所有圖片手動優化一次。我們也可以選擇一些有損壓縮工具,獲得更大的壓縮效果。

如果你確認 png 并沒有修改,你可以在 tinker 的配置使用 ignoreChange 來忽略所有 png 文件的修改。

res {
    ignoreChange = ["*.png"]
}

七、Tinker 中的 dex 配置 'raw' 與 'jar' 模式應該如何選擇?

它們應該說各有優劣勢,大概應該有以下幾條原則:

  1. 如果你的 minSdkVersion 小于 14,那你務必要選擇 'jar' 模式;

  2. 以一個 10M 的 dex 為例,它壓縮成 jar 大約為 4M,即 'jar' 模式能節省 6M 的 ROM 空間。

  3. 對于 'jar' 模式,我們需要驗證壓縮包流中 dex 的 md5,這會更耗時,在小米 2S 上數據大約為 'raw' 模式 126 ms,'jar' 模式為 246 ms

因為在合成過程中 Tinker 已經校驗了各個文件的 Md5,并將它們存放在 /data/data/.. 目錄中。默認每次加載時 Tinker 并不會去校驗 tinker 文件的 Md5,但是你也可通過開啟 loadVerifyFlag 強制每次加載時校驗,但是這會帶來一定的時間損耗。

簡單來說,'jar' 模式更省空間,但是運行時校驗的耗時大約為 'raw' 模式的兩倍。如果你沒有打開運行時校驗,推薦使用 'jar' 模式。

八、如何兼容多渠道包?

關于渠道包的問題,若使用 flavor 編譯渠道包,會導致不同的渠道包由于 BuildConfig 變化導致 classes.dex 差異。這里建議的方式有:

  1. 將渠道信息寫在 AndroidManifest.xml 或文件中,例如 channel.ini

  2. 將渠道信息寫在 apk 文件的 zip comment 中,這種是建議方式,例如可以使用項目 packer-ng-plugin 或者可使用 V2 Schemewalle

  3. 若不同渠道存在功能上的差異,建議將差異部分放于單獨的 dex 或采用相同代碼不同配置方式實現;

事實上,tinker 也支持多 flavor 直接編譯多個補丁包,具體可參考多 Flavor 打包

九、tinker 是否兼容加固?

tinker 1.7.8 可以通過 isProtectedApp 開啟加固支持,這種模式僅僅可以使用在加固應用中。

加固廠商 測試
騰訊云·樂固 Tested
愛加密 Tested
梆梆加固 Tested
360加固 Tested,需要 5 月 8 日以后加固的版本
其他 請自行測試,只要滿足下面規則的都可以支持

這里是否支持加固,需要加固廠商明確以下兩點:

  1. 不能提前導入類;

  2. art 平臺若要編譯 oat 文件,需要將內聯取消。

十、Google Play 版本是否可以有 Tinker 相關代碼?

由于 Google play 的使用者協議,對于 GP 渠道我們不能使用 Tinker 動態更新代碼,這里會存在應用被下架的風險。但是在 Google play 版本,我們依然可以存在 Tinker 的相關代碼,但是我們需要屏蔽補丁的網絡請求與合成相關操作。

十一、tinker 與 instant run 的兼容問題?

事實上,若編譯時都使用 assemble*tinkerinstant run 是可以兼容的。但是不少用戶基礎包與補丁包混用兩種模式導致補丁過大,所以 tinker 編譯時禁用 instant run,我們可以在設置中禁用 instant run 或使用 assemble 方式編譯。

大家日常 debug 時若想開啟 instant run 功能,可以將 tinker 暫時關閉:

ext {
    //for some reason, you may want to ignore tinkerBuild, such as instant run debug build?
    tinkerEnabled = false
}

十二、每次編譯我應該保留哪些文件,如何兼容 AndResGuard?

正如 sample 中 app/build.gradle,每個可能用到 Tinker 發布補丁的版本,需要在編譯后保存以下幾個文件:

  1. 編譯后生成的 apk 文件,即用來編譯補丁的基礎版本;

  2. 若使用 proguard 混淆,需要保持 mapping.txt 文件;

  3. 需要保留編譯時的 R.txt 文件;

  4. 若你同時使用了資源混淆組件 AndResGuard,你也需要將混淆資源的 resource_mapping.txt 保留下來,同時將 r/* 也添加到 res pattern 中。具體我們可以參考 build.gradle

微信通過將補丁編譯與 Jenkins 很好的結合起來,只需要點擊一個按鈕,即可方便的生成補丁包。也可以參考 tinkerpatch-andresguard-sample

十三、tinkerId 應該如何選擇?

tinkerId 是用來區分基準安裝包的,我們需要嚴格保證一個基準包的唯一性。在設計的初期,官方使用的是基準包的 CentralDirectoryCRC,但某些 APP 為了生成渠道包會對安裝包重新打包,導致不同的渠道包的 CentralDirectory 并不一致。

編譯補丁包時,Tinker 會自動讀取基準包 AndroidManifesttinkerId 作為 package_meta.txt 中的 TINKER_ID。將本次編譯傳入的 tinkerId,作為 package_meta.txt 中的 NEW_TINKER_ID。當前 NEW_TINKER_ID 并沒有被使用到,只是保留作為配置項。如果我們使用 git rev 作為 tinkerid,這時只要使用 git diff TINKER_ID NEW_TINKER_ID 即可獲得所有的代碼差異。

我們需要保證 tinkerId 一定是要唯一性的,這里推薦使用 git rev 或者 svn rev。如果我們升級了客戶端版本,但 tinkerId 與舊版本相同,會導致可能會加載舊版本的補丁。這里我們一定要注意,升級可客戶端版本,需要更新 tinkerId

十四、如何使生成的補丁包更小?

對于代碼來說,我們最好記住以下幾條規則:

  1. 編譯補丁包時,proguard 使用 applymapping 模式;

  2. 對于多 dex 的情況,保持原本的分包規則,盡量減少由于分包變化而帶來的變更。在生成補丁包過程中,對于 class 分包的變化將會輸出 Warning:Class Moved 日志,我們應該盡量減少這種變化;

  3. 大量靜態常量的改變與資源 R 文件的變更,這里推薦使用 applyResouceMapping 方式保持資源 ID。大量類分包的改變對補丁包的影響不大,但是對于合成的時間消耗與占 ROM 的體積影響更大。我們每次生成補丁后,都應該查看 TinkerPatch 輸出文件夾的日志;

  4. 其他的例如使用 force jumbo 模式以及使用 7zip 壓縮補丁包。

十五、關于使用的 ClassLoader 問題?

Tinker 沒有使用 parent classloader 方案,而是使用 Multidex 插入 dexPathList 方式,這里主要考慮到分平臺內部類可能存在校驗 classloader 的問題。

  1. SDK >= 24,即 Android N 版本,當補丁存在時,Tinker 將 PathClassloader 替換為 AndroidNClassLoader,但是它依然繼承與 PathClassLoader。我們依然可以像以往那樣對它進行類似 makeDexElements 的操作。;

  2. SDK < 14,Tinker 沒有對 classloader 做處理,這里需要注意補丁的 Dex 是插入在 dexElement 的前方。

十六、什么時候調用 installTinker?

首先我們推薦在最開始的時候就是執行 installTinker 操作,但是即使你不去 installTinker,也不會影響 Tinker 對代碼、So 與資源的加載。installTinker 只是做了以下幾件事件:

  1. 回調 LoadReporter,返回加載結果;

  2. 初始化各個自定義類與 Tinker 實例,可以調用 Tinker 相關 API,發起升級補丁以及處理相關的回調。

事實上,微信只在主進程與 :patch 進程執行 installTinker 操作。其他進程只要不處理回調結果,不發起補丁請求即可。在 SampleUncaughtExceptionHandler 中,為了防止 Crash 并沒有執行 installTinker,全部使用的是 TinkerApplicationHelper 中的 API,詳細可以查看 Tinker API 概覽

十七、Proguard 5.2.1 applymapping 出現 Warning?

這是因為 5.2.1 增加了內聯函數的行輸出信息導致,你可以使用以下幾種方法解決:

  1. 使用 5.1 版本 proguard

  2. 將內聯函數的優化關掉;

  3. 自己對 mapping 文件去除內聯函數的行信息。

如果使用 4.X 版本的 Proguard 強烈建議升級到 5.1 版本。可以先下載 5.1 的 Proguard, 然后通過以下方式指定:

 classpath files('proguard-5.1.jar')

若使用 gradle 編譯,與 multiDexKeepProguard 不同,我們無需將生成的 tinker_proguard.pro 拷貝到自己的配置中。另外一個方面,若 applymapping 過程出現沖突,我們可以采取以下幾個方法:

  1. 添加 ignoreWarning;需要注意的是如果某些類的確需要采用新的 mapping,這樣補丁后 App 會出問題,一般并不建議采用這種方式;

  2. 修改基準包的 mapping 文件;我們需要根據新的 mapping 文件,修正基準包的 mapping 文件。例如將 warning 項刪掉或者將新 mappingkeep 的項復寫到基準的 mapping 中。可以參考腳本 proguard_warning.pymerge_mapping.py

注意,如果想通過直接刪除舊 mapping 文件的沖突項,需要注意刪除類的內部類是否存在混淆沖突。

十八、TinkerPatch 補丁管理后臺與 Tinker 的關系?

TinkerPatch 平臺是第三方開發基于 CDN 分發的補丁管理后臺。它提供了補丁后臺托管,版本管理,一鍵傻瓜式接入等功能,讓我們可以無需修改任何代碼即可輕松接入Tinker。

我們可以根據自己的需要選擇接入,它是獨立于 Tinker 項目之外。

十九、Tinker 的最佳實踐?

為了使補丁的成功率更高,官方在 Sample 中還做了以下工作:

  1. 由于合成進程可能被各種原因殺死,使用 UpgradePatchRetry.java 來做重試功能,提高成功率;

  2. 防止補丁后程序無法啟動,使用 SampleUncaughtExceptionHandler.javacrash 啟動保護。這里更推薦的是進入安全模式,使用配置的方式強制清理或者升級補丁;

  3. 為了防止 BuildConfig 的改變導致大量類的變更,使用 BuildInfo.java 非 final 的變量來中轉。

  4. 為了加快補丁應用同時保持用戶體驗,SampleResultService.java 在應用退入后臺或手機滅屏時,才殺掉進程。你也可以在殺掉進程前,直接通過發送 broadcast 或 service intent 的方式盡快的重啟進程。

  5. jumboMode 打開,防止由于字符串增多導致 force-jumbol,導致更多的變更。

  6. 使用 zip comment 方式生成渠道包。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,431評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,637評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,555評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,900評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,629評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,976評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,976評論 3 448
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,139評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,686評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,411評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,641評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,129評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,820評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,233評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,567評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,362評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,604評論 2 380

推薦閱讀更多精彩內容