這類文章已經(jīng)很多了寝衫,寫這個是為了記錄下自己的使用過程和在使用中遇到過的一些問題顷扩。
看過一句話,無論學(xué)習(xí)什么技術(shù)竞端,以官方文檔為主屎即,教程文章為輔。所以認(rèn)真看文檔事富。
Tinker
介紹下Tinker技俐,其實(shí)這些官方文檔都有,寫在這里也就是為了自己再看的時(shí)候方便统台。
Tinker是什么
Tinker是微信官方的Android熱補(bǔ)丁解決方案雕擂,它支持動態(tài)下發(fā)代碼、So庫以及資源贱勃,讓應(yīng)用能夠在不需要重新安裝的情況下實(shí)現(xiàn)更新井赌。也可以使用Tinker來更新插件谤逼。
主要包括以下幾個部分:
- gradle編譯插件:tinker-patch-gradle-plugin
- 核心sdk庫:tinker-android-lib
- 非gradle編譯用戶的命令行版本:tinker-patch-cli.jar
與其他方案比較
阿里的AndFix、美團(tuán)的Robust以及QZone的超級補(bǔ)丁方案
Tinker | QZone | AndFix | Robust | |
---|---|---|---|---|
類替換 | yes | yes | no | no |
SO替換 | yes | no | no | no |
資源替換 | yes | yes | no | no |
全平臺支持 | yes | yes | yes | yes |
即時(shí)生效 | no | no | yes | yes |
性能消耗 | 較小 | 較大 | 較小 | 較小 |
補(bǔ)丁包大小 | 較小 | 較大 | 一般 | 一般 |
開發(fā)透明 | yes | yes | no | no |
復(fù)雜度 | 較低 | 較低 | 復(fù)雜 | 復(fù)雜 |
gradle支持 | yes | no | no | no |
ROM體積 | 較大 | 較小 | 較小 | 較小 |
成功率 | 較高 | 較高 | 一般 | 最高 |
- AndFix作為native解決方案仇穗,首先面臨的是穩(wěn)定性與兼容性問題流部,更重要的是它無法實(shí)現(xiàn)類替換,它是需要大量額外的開發(fā)成本的纹坐;
- Robust兼容性與成功率較高枝冀,但是它與AndFix一樣,無法新增變量與類只能用做的bugFix方案耘子;
- Qzone方案可以做到發(fā)布產(chǎn)品功能果漾,但是它主要問題是插樁帶來Dalvik的性能問題,以及為了解決Art下內(nèi)存地址問題而導(dǎo)致補(bǔ)丁包急速增大的谷誓。
特別是在Android N之后绒障,由于混合編譯的inline策略修改,對于市面上的各種方案都不太容易解決捍歪。而Tinker熱補(bǔ)丁方案不僅支持類户辱、So以及資源的替換,它還是2.X-8.X(1.9.0以上支持8.X)的全平臺支持糙臼。利用Tinker我們不僅可以用做bugfix,甚至可以替代功能的發(fā)布焕妙。
不過這些是Tinker文檔的內(nèi)容,想具體了解其他方案還是到每個的官方文檔進(jìn)行了解AndFix弓摘、Robust。
已知問題
- Tinker不支持修改AndroidManifest.xml痕届,Tinker不支持新增四大組件(1.9.0支持新增非export的Activity)韧献;
- 由于Google Play的開發(fā)者條款限制,不建議在GP渠道動態(tài)更新代碼研叫;
- 在Android N上锤窑,補(bǔ)丁對應(yīng)用啟動時(shí)間有輕微的影響;
- 不支持部分三星android-21機(jī)型嚷炉,加載補(bǔ)丁時(shí)會主動拋出"TinkerRuntimeException:checkDexInstall failed"渊啰;
- 對于資源替換,不支持修改remoteView申屹。例如transition動畫绘证,notification icon以及桌面圖標(biāo)
開始接入
gradle接入
在項(xiàng)目的build.gradle文件中,添加tinker-patch-gradle-plugin的依賴
dependencies{
classpath "com.tencent.tinker:tinker-patch-gradle-plugin:x.x.x"
}
在app的build.gradle文件中哗讥,添加tinker的庫依賴以及apply tinker的gradle插件.
dependencies{
//可選嚷那,用于生成application類
provided('com.tencent.tinker:tinker-android-anno:x.x.x')
//tinker的核心庫
compile('com.tencent.tinker:tinker-android-lib:x.x.x')
}
...
...
//apply tinker插件
apply plugin: 'com.tencent.tinker.patch'
這三個依賴在添加的時(shí)候版本號必須保持一致。
配置gradle中的參數(shù)
我自己在build.gradle中參數(shù)的配置基本是按照demo中build.gradle配置的杆煞,或者也可以查看文檔中的參數(shù)說明魏宽,弄清楚每個參數(shù)代表什么以后腐泻,根據(jù)自己的需求進(jìn)行配置。
修改Application類
這里有兩種方法進(jìn)行修改队询,一種是將自己的Application類繼承TinkerApplication.Java派桩;第二種是使用Annotation來自動生成Application類。官方推薦是使用第二種方式蚌斩,Annotation自動生成Application類铆惑,既然已經(jīng)用了Tinker方案了,就按官方推薦的接入凳寺。文檔中自定義Application類鸭津。
要用Annotation來自動生成Application類的原因是:程序啟動時(shí)會加載默認(rèn)的Application類,這導(dǎo)致補(bǔ)丁包無法對其做修改肠缨。
具體實(shí)現(xiàn):
1逆趋、新建一個自己的ApplicationLike類,讓其繼承DefaultApplicationLike晒奕,并添加相關(guān)注釋
@DefaultLifeCycle(
application = ".MyApplication", //application類名
flag = ShareConstants.TINKER_ENABLE_ALL, //tinkerFlag
loaderClass = "com.tencent.tinker.loader.TinkerLoader", //loaderClassName, 我們這里使用默認(rèn)即可!
loadVerifyFlag = false
)
public class MyApplicationLike extends DefaultApplicationLike{
...
}
2闻书、將原先自己的Application類以及他的繼承類的所有代碼拷貝到新建的自己的ApplicationLike中。
3脑慧、原先Application的attachBaseContext方法實(shí)現(xiàn)要單獨(dú)移到onBaseContextAttached中魄眉。
4、在現(xiàn)在自己的ApplicationLike中闷袒,所有引用到application的地方改成getApplication()坑律。
5、對其他引用原先Application或者它的靜態(tài)對象和方法的地方囊骤,改成引用現(xiàn)在自己的ApplicationLike的靜態(tài)對象和方法晃择。
通過Annotation方式生成Application類,需要將原來自己的Application類刪除也物,其他任何類都不能再引用原來自己的Application類宫屠。
修改好Application以后,在AndroidManifest.xml中滑蚯,修改application的name屬性浪蹂,與MyApplicationLike注釋中寫的保持一致。之后在自己的ApplicationLike中的onBaseContextAttached()方法中告材,通過TinkerInstaller.install()來初始化Tinker坤次。到此為止,Tinker的接入已初步完成斥赋,接下來可以實(shí)現(xiàn)補(bǔ)丁功能浙踢。
實(shí)現(xiàn)補(bǔ)丁功能
將基準(zhǔn)包安裝到手機(jī),修改需要更新的內(nèi)容后灿渴,將基準(zhǔn)包和相應(yīng)文件的路徑填寫在build.gradle中的對應(yīng)位置上洛波,調(diào)用tinkerPatch Task進(jìn)行編譯胰舆,生成補(bǔ)丁包,將補(bǔ)丁包放在手機(jī)的sdcard中蹬挤,用基準(zhǔn)包安裝在手機(jī)上的應(yīng)用加載補(bǔ)丁缚窿,加載成功后重啟,補(bǔ)丁生效焰扳,更新成功倦零。
官方demo使用方法
生成補(bǔ)丁包
在成功接入tinker并且build完成以后,在Android Studio的工程目錄中會生成bakApk文件夾吨悍,路徑對應(yīng)自己在gradle中的設(shè)置扫茅。
在bakApk文件夾下的內(nèi)容,就是每次在生成補(bǔ)丁之前育瓜,在gradle中進(jìn)行配置時(shí)所需要的XXX.apk(基準(zhǔn)包)葫隙、XXX_mapping.txt(打開混淆以后才會產(chǎn)生)、XXX_R.txt文件躏仇。
同時(shí)恋脚,會在Android Studio的Gradle工具欄中會生成tinkerPatch Task文件,直接使用task:tinkerPatchVariantName(例如tinkerPatchDebug焰手、tinkerPatchRelease)即可自動根據(jù)Variant選擇相應(yīng)的編譯類型糟描。
在編譯過程中,還幫助我們完成了以下操作:
將TINKER_ID自動插入AndroidManifest的meta項(xiàng)书妻,輸出路徑為build/intermediates/tinker_intermediates/AndroidManifest.xml;
如果minifyEnabled為true即打開混淆船响,將自動將Tinker的proguard規(guī)則添加到proguardFiles中,輸出路徑為build/intermediates/tinker_intermediates/tinker_proguard.pro躲履,這里不需要將它們拷貝到自己的proguard配置文件中;
如果multiDexEnabled為true灿意,將自動生成Tinker需要放在主dex的keep規(guī)則。在tinker 1.7.6版本之前崇呵,你需要手動將生成規(guī)則拷貝到自己的multiDexKeepProguard文件中。例如Sample中的multiDexKeepProguard file("keep_in_main_dex.txt")馅袁。在1.7.6版本之后域慷,這里會通過腳本自動處理,無須手動填寫汗销。
把dexOptions的jumboMode打開犹褒。
在進(jìn)行tinkerPatch Task編譯之前,需要將bakApk文件夾下每個文件路徑填寫在對應(yīng)的位置上弛针。多flavor打包叠骑。
//demo中的配置將相關(guān)屬性寫在一起,方便每次更改
ext {
//demo中的這個屬性表示是否打開tinkerBuild
tinkerEnabled = true
//基準(zhǔn)包apk的位置
tinkerOldApkPath = "${bakPath}/"
//打開混淆生成的mapping.txt的路徑
tinkerApplyMappingPath = "${bakPath}/"
//R.txt文件的路徑
tinkerApplyResourcePath = "${bakPath}/"
//用了flavor以后在這里填寫編譯后的目錄路徑削茁,沒用flavor可以不填
tinkerBuildFlavorDirectory = "${bakPath}/"
}
填寫完成以后直接使用tinkerPatch Task中相應(yīng)的操作宙枷,即自動編譯最新的安裝包掉房,并與輸入基準(zhǔn)包作差異,得到最終的補(bǔ)丁包慰丛。卓囚,編譯完成后,會在tinkerPatch輸出的目錄build/outputs/tinkerPatch中產(chǎn)生我們需要的文件诅病。
文件名 | 描述 |
---|---|
patch_unsigned.apk | 沒有簽名的補(bǔ)丁包 |
patch_signed.apk | 簽名后的補(bǔ)丁包 |
patch_signed_7zip.apk | 簽名后并使用7zip壓縮的補(bǔ)丁包哪亿,也是我們通常使用的補(bǔ)丁包。但正式發(fā)布的時(shí)候贤笆,最好不要以.apk結(jié)尾蝇棉,防止被運(yùn)營商挾持。 |
log.txt | 在編譯補(bǔ)丁包過程的控制臺日志 |
dex_log.txt | 在編譯補(bǔ)丁包過程關(guān)于dex的日志 |
so_log.txt | 在編譯補(bǔ)丁包過程關(guān)于lib的日志 |
tinker_result | 最終在補(bǔ)丁包的內(nèi)容芥永,包括diff的dex篡殷、lib以及assets下面的meta文件 |
resources_out.zip | 最終在手機(jī)上合成的全量資源apk,你可以在這里查看是否有文件遺漏 |
tempPatchedDexes | 在Dalvik與Art平臺恤左,最終在手機(jī)上合成的完整Dex贴唇,我們可以在這里查看dex合成的產(chǎn)物。 |
官方提示:每次編譯結(jié)束飞袋,我們都應(yīng)該查看相關(guān)日志戳气,清楚最終在補(bǔ)丁包中的文件。尤其是dex的補(bǔ)丁文件巧鸭,即使是1k的dex補(bǔ)丁文件瓶您,也會帶來合成時(shí)的時(shí)間損耗以及合成完整dex文件ROM空間體積這兩部分影響!
加載補(bǔ)丁包
將基準(zhǔn)包安裝到手機(jī)纲仍,再將生成好的patch_signed_7zip.apk補(bǔ)丁包放到手機(jī)的sdcard中呀袱,正式使用時(shí)從服務(wù)器下載補(bǔ)丁包。然后在需要加載補(bǔ)丁包的位置調(diào)用TinkerInstaller.onReceiveUpgradePatch加載對應(yīng)路徑補(bǔ)丁包郑叠。
需要注意:在Tinker的更新使用中夜赵,默認(rèn)在DefaultTinkerResultService會殺掉:patch進(jìn)程,假設(shè)當(dāng)前是補(bǔ)丁升級并且成功了乡革,我們會殺掉當(dāng)前進(jìn)程寇僧,讓補(bǔ)丁包更快的生效(這就是為什么有的人接好Tinker,實(shí)現(xiàn)了更新功能沸版,但是每次加載完補(bǔ)丁會Crash的原因)嘁傀。若是修復(fù)類型的補(bǔ)丁包并且失敗了,我們會卸載補(bǔ)丁包视粮。
擴(kuò)展
Tinker中還有很多可選自定義類细办。可以通過繼承默認(rèn)的類蕾殴,實(shí)現(xiàn)自己感興趣的事件回調(diào)笑撞,比如在上面提到的岛啸,tinker在DefaultTinkerResultService中默認(rèn)在加載補(bǔ)丁成功后殺死當(dāng)前進(jìn)程,這在正式的使用中會很降低用戶體驗(yàn)娃殖,所以我們需要繼承這個類值戳,實(shí)現(xiàn)自己的回調(diào)。具體實(shí)現(xiàn)可以參考官方說明配合demo炉爆。