Tinker 接入指南
gradle接入
gradle是推薦的接入方式,在gradle插件tinker-patch-gradle-plugin
中我們幫你完成proguard、multiDex以及Manifest處理等工作。
添加gradle依賴
在項目的build.gradle中,添加tinker-patch-gradle-plugin
的依賴
buildscript {
dependencies {
classpath ('com.tencent.tinker:tinker-patch-gradle-plugin:1.7.5')
}
}
然後在app的gradle文件app/build.gradle,我們需要添加tinker的庫依賴以及apply tinker的gradle插件.
dependencies {
//可選,用於生成application類
provided('com.tencent.tinker:tinker-android-anno:1.7.5')
//tinker的核心庫
compile('com.tencent.tinker:tinker-android-lib:1.7.5')
}
...
...
//apply tinker插件
apply plugin: 'com.tencent.tinker.patch'
gradle參數詳解
我們將原apk包稱爲基準apk包,tinkerPatch直接使用基準apk包與新編譯出來的apk包做差異,得到最終的補丁包。gradle配置的參數詳細解釋如下:
參數 | 默認值 | 描述 |
---|---|---|
tinkerPatch |
全局信息相關的配置項 | |
oldApk | null | 基準apk包的路徑,必須輸入,否則會報錯。 |
ignoreWarning | false | 如果出現以下的情況,並且ignoreWarning爲false,我們將中斷編譯。因爲這些情況可能會導致編譯出來的patch包帶來風險: 1. minSdkVersion小於14,但是 dexMode 的值爲"raw";2. 新編譯的安裝包出現新增的四大組件(Activity, BroadcastReceiver...); 3. 定義在dex.loader用於加載補丁的類不在main dex中; 4. 定義在dex.loader用於加載補丁的類出現修改; 5. resources.arsc改變,但沒有使用applyResourceMapping編譯。 |
useSign | true | 在運行過程中,我們需要驗證基準apk包與補丁包的簽名是否一致,我們是否需要爲你簽名。 |
buildConfig |
編譯相關的配置項 | |
applyMapping | null | 可選參數;在編譯新的apk時候,我們希望通過保持舊apk的proguard混淆方式,從而減少補丁包的大小。這個只是推薦的,但設置applyMapping會影響任何的assemble編譯 。 |
applyResourceMapping | null | 可選參數;在編譯新的apk時候,我們希望通過舊apk的R.txt 文件保持ResId的分配,這樣不僅可以減少補丁包的大小 ,同時也避免由於ResId改變導致remote
view異常 。 |
tinkerId | null | 在運行過程中,我們需要驗證基準apk包的tinkerId是否等於補丁包的tinkerId。這個是決定補丁包能運行在哪些基準包上面,一般來說我們可以使用git版本號、versionName等等。 |
dex |
dex相關的配置項 | |
dexMode | jar | 只能是'raw'或者'jar'。 對於'raw'模式,我們將會保持輸入dex的格式。 對於'jar'模式,我們將會把輸入dex重新壓縮封裝到jar。如果你的minSdkVersion小於14,你必須選擇‘jar’模式,而且它更省存儲空間,但是驗證md5時比'raw'模式耗時()。 |
usePreGeneratedPatchDex | flase | 是否提前生成dex,而非合成的方式。這套方案即回退成Qzone的方案,對於需要使用加固或者多flavor打包(建議使用其他方式生成渠道包)的用戶 可使用。但是這套方案需要插樁,會造成Dalvik下性能損耗以及Art補丁包可能過大的問題,務必謹慎使用 。另外一方面,這種方案在Android
N之後可能會產生問題,建議過濾N之後的用戶。 |
pattern | [] | 需要處理dex路徑,支持*、?通配符,必須使用'/'分割。路徑是相對安裝包的,例如/assets/... |
loader | [] | 這一項非常重要,它定義了哪些類在加載補丁包的時候會用到。這些類是通過Tinker無法修改的類,也是一定要放在main dex的類。 這裏需要定義的類有: 1. 你自己定義的Application類; 2. Tinker庫中用於加載補丁包的部分類,即com.tencent.tinker.loader.*; 3. 如果你自定義了TinkerLoader,需要將它以及它引用的所有類也加入loader中; 4. 其他一些你不希望被更改的類,例如Sample中的BaseBuildInfo類。這裏需要注意的是,這些類的直接引用類也需要加入到loader中。或者你需要將這個類變成非preverify。 |
lib |
lib相關的配置項 | |
pattern | [] | 需要處理lib路徑,支持*、?通配符,必須使用'/'分割。與dex.pattern一致, 路徑是相對安裝包的,例如/assets/... |
res |
res相關的配置項 | |
pattern | [] | 需要處理res路徑,支持*、?通配符,必須使用'/'分割。與dex.pattern一致, 路徑是相對安裝包的,例如/assets/...,務必注意的是,只有滿足pattern的資源纔會放到合成後的資源包。 |
ignoreChange | [] | 支持*、?通配符,必須使用'/'分割。若滿足ignoreChange的pattern,在編譯時會忽略該文件的新增、刪除與修改。 最極端的情況,ignoreChange與上面的pattern一致,即會完全忽略所有資源的修改。 |
largeModSize | 100 | 對於修改的資源,如果大於largeModSize,我們將使用bsdiff算法。這可以降低補丁包的大小,但是會增加合成時的複雜度。默認大小爲100kb |
packageConfig |
用於生成補丁包中的'package_meta.txt'文件 | |
configField | TINKER_ID, NEW_TINKER_ID | configField("key", "value"), 默認我們自動從基準安裝包與新安裝包的Manifest中讀取tinkerId,並自動寫入configField。在這裏,你可以定義其他的信息,在運行時可以通過TinkerLoadResult.getPackageConfigByName得到相應的數值。但是建議直接通過修改代碼來實現,例如BuildConfig。 |
sevenZip |
7zip路徑配置項,執行前提是useSign爲true | |
zipArtifact | null | 例如"com.tencent.mm:SevenZip:1.1.10",將自動根據機器屬性獲得對應的7za運行文件,推薦使用。 |
path | 7za | 系統中的7za路徑,例如"/usr/local/bin/7za"。path設置會覆蓋zipArtifact,若都不設置,將直接使用7za去嘗試。 |
具體的參數設置事例可參考sample中的app/build.gradle。
tinkerPatch task詳解
直接使用task:tinkerPatchVariantName
(例如tinkerPatchDebug、tinkerPatchRelease)即可自動根據Variant選擇相應的編譯類型,同時它還貼心的爲我們完成以下幾個操作:
-
將TINKER_ID自動插入AndroidManifest的meta項,輸出路徑爲build/intermediates/tinker_intermediates/AndroidManifest.xml;
-
如果minifyEnabled爲true,將自動將Tinker的proguard規則添加到proguardFiles中,輸出路徑爲build/intermediates/tinker_intermediates/tinker_proguard.pro,
這裏你不需要將它們拷貝到自己的proguard配置文件中
; -
如果multiDexEnabled爲true,將自動生成Tinker需要放在主dex的keep規則,你
需要手動將生成規則拷貝到自己的multiDexKeepProguard文件中
。這是因爲它是一個單獨的文件,而proguardFiles是一個list。例如Sample中的multiDexKeepProguard file("keep_in_main_dex.txt")
。 -
把dexOptions的jumboMode打開。
輸出路徑爲:build/intermediates/tinker_intermediates/tinker_multidexkeep.pro。 後你可以在build/outputs/tinkerPatch
中找到輸出的文件。
多Flavor打包
有的時候我們希望通過flavor方式打包,在sample中提供了簡單的用法事例:
1.通過flavor編譯,這個時候我們可以看到bakApk路徑是一個按照flavor名稱區分的目錄;
2.將編譯目錄路徑填寫到sample中tinkerBuildFlavorDirectory
,其他的幾個字段不需要填寫,這裏會自動根據路徑拼接;
ext {
tinkerBuildFlavorDirectory = "${bakPath}/app-1014-13-35-12"
}
3.運行tinkerPatchAllFlavorDebug
或者tinkerPatchAllFlavorRelease
即可得到所有flavor的補丁包。
輸出文件詳解
在tinkerPatch輸出目錄build/outputs/tinkerPatch
中,我們關心的文件有:
文件名 | 描述 |
---|---|
patch_unsigned.apk | 沒有簽名的補丁包 |
patch_signed.apk | 簽名後的補丁包 |
patch_signed_7zip.apk | 簽名後並使用7zip壓縮的補丁包,也是我們通常使用的補丁包。但正式發佈的時候,最好不要以.apk 結尾,防止被運營商挾持。 |
log.txt | 在編譯補丁包過程的控制檯日誌 |
dex_log.txt | 在編譯補丁包過程關於dex的日誌 |
so_log.txt | 在編譯補丁包過程關於lib的日誌 |
tinker_result | 最終在補丁包的內容,包括diff的dex、lib以及assets下面的meta文件 |
resources_out.zip | 最終在手機上合成的全量資源apk,你可以在這裏查看是否有文件遺漏 |
resources_out_7z.zip | 根據7zip最終在手機上合成的全量資源apk |
tempPatchedDexes | 在Dalvik與Art平臺,最終在手機上合成的完整Dex,我們可以在這裏查看dex合成的產物。 |
每次編譯結束,我們都應該查看相關日誌,清楚最終在補丁包中的文件。尤其是dex的補丁文件,即使是1k的dex補丁文件,也會帶來合成時的時間損耗以及合成完整dex文件ROM空間體積這兩部分影響!
命令行接入
命令行工具tinker-patch-cli.jar
提供了基準包與新安裝包做差異,生成補丁包的功能。具體的命令參數如下:
java -jar tinker-patch-cli.jar -old old.apk -new new.apk -config tinker_config.xml -out output_path
參數與gradle基本一致,新增的sign參數,我們需要輸入簽名路徑與簽名信息。
與gradle不同的是,在編譯時我們需要將TINKER_ID插入到AndroidManifest.xml中。例如
<meta-data android:name="TINKER_ID" android:value="tinker_id_b168b32"/>
同時,我們需要自己保證proguard文件以及main dex類是正確的。具體配置可參考以下幾個文件:
- tinker_config.xml 實例
- tinker_proguard.pro proguard配置實例
- tinker_multidexkeep.pro main dex配置實例
如何快速獲得依賴包
使用tinker-git:buildSdk
任務即可在根目錄的buildSdk
文件夾中獲得所有需要的文件。
其中包括:
- build; 編譯時用到的工具,主要是tinker-patch-cli.jar以及一些可能用到的配置信息;
- android;需要放到手機端的依賴庫,其中
tinker-android-anno.jar
爲可選庫,只有用到Tinker的annotation的才需要引入。
使用步驟詳解
Sample的使用方法
Demo請參考tinker-sample-android, 它的使用方法如下:
-
調用
assembleDebug
編譯,我們會將編譯過的包保存在build/bakApk中。然後我們將它安裝到手機,點擊SHOW INFO
按鈕,可以看到補丁並沒有加載. -
修改代碼,例如將MainActivity中
I am on patch onCreate
的Log打開。然後我們需要修改build.gradle中的參數,將步驟一編譯保存的安裝包路徑拷貝到tinkerPatch
中的oldApk
參數中。 -
調用
tinkerPatchDebug
, 補丁包與相關日誌會保存在/build/outputs/tinkerPatch/
。然後我們將patch_signed_7zip.apk
推送到手機的sdcard中。adb push ./app/build/outputs/tinkerPatch/debug/patch_signed_7zip.apk /storage/sdcard0/
-
點擊
LOAD PATCH
按鈕, 如果看到patch success, please restart process
的toast,即可鎖屏或者點擊KILL SELF
按鈕 -
我們可以看到的確出現了
I am on patch onCreate
日誌,同時點擊SHOW INFO
按鈕,顯示補丁包的確已經加載成功了。
Release的使用方法
Tinker的使用方式如下,以gradle接入的release包爲例:
- 每次編譯或發包將安裝包與mapping文件備份;
- 若有補丁包的需要,按自身需要修改你的代碼、庫文件等;
- 將備份的基準安裝包與mapping文件輸入到tinkerPatch的配置中;
- 運行tinkerPatchRelease,即可自動編譯最新的安裝包,並與輸入基準包作差異,得到最終的補丁包。
調試源碼
tinker調試源碼非常簡單,大家需要在tinker的主工程運行tinker group中buildAndPublishTinkerToLocalMaven
任務即可。
此外由於localmaven無法傳遞依賴,需要在使用的地方再顯式引用以下庫:
compile("com.tencent.tinker:tinker-android-loader:${TINKER_VERSION}") { changing = true }
compile("com.tencent.tinker:aosp-dexutils:${TINKER_VERSION}") { changing = true }
compile("com.tencent.tinker:bsdiff-util:${TINKER_VERSION}") { changing = true }
compile("com.tencent.tinker:tinker-commons:${TINKER_VERSION}") { changing = true }
github/Tinker的默認分支爲master分支,幾個含義的含義分別是:
- master分支;最近一次release的穩定代碼,我們在master分支打tag;
- dev分支;開發分支,這裏會包含下一個版本的代碼,我們只能給dev分支提pr以及驗證部分已經修復的issue;
- hotfix分支;爲了修復tinker緊急bug的分支。
關於tinker分支管理、issue以及pr規範,請閱讀Tinker Contributing Guide。
TinkerPatch補丁管理後臺
www.tinkerpatch.com 是第三方開發基於CDN分發的補丁管理後臺。它提供了腳本後臺託管,版本管理,保證傳輸安全等功能,讓你無需搭建一個後臺,無需關心部署操作,只需引入一個 SDK 即可立即使用 Tinker。TinkerPatch平臺的SDK也是開源的,它的地址爲tinker_server_client。
總的來說,我們更推薦使用gradle作爲接入方式。然後我們繼續學習如何Tinker 自定義擴展。