Android熱更新Tinker集成配置【詳細】

關于熱更新目前的各種方案原理看我之前這篇文章的介紹熱補丁動態修復技術調研
這篇文章將選擇Trinker集成體驗一下熱更新。

1.什么是Tinker?


Tinker 是一個開源項目(Github鏈接),它是微信官方的 Android 熱補丁解決方案,它支持動態下發代碼、So 庫以及資源,讓應用能夠在不需要重新安裝的情況下實現更新。但需要注意的是需要重啟Activity或重啟Application才能達到更新效果。

2.對比其他以及選擇Tinker的理由?


將Tinker與其他原理的熱補丁方案做對比:阿里的 AndFix、美團的 Robust 以及 QZone 的超級補丁方案,如果不了解這三類原理,就看這篇文章的介紹熱補丁動態修復技術調研

總的來說:

  • AndFix作為native解決方案,首先面臨的是穩定性與兼容性問題,更重要的是它無法實現類替換,它是需要大量額外的開發成本的;
  • Robust兼容性與成功率較高,但是它與AndFix一樣,無法新增變量與類只能用做的bugFix方案;
  • Qzone方案可以做到發布產品功能,但是它主要問題是插樁帶來Dalvik的性能問題,以及為了解決Art下內存地址問題而導致補丁包急速增大的。

Tinker熱補丁方案不僅支持類、So 以及資源的替換,它還是2.X-7.X的全平臺支持。利用Tinker我們不僅可以用做 bugfix,甚至可以替代功能的發布。Tinker 已運行在微信的數億 Android 設備上,那么為什么你不使用 Tinker 呢?

3.接入TinkerPatch 平臺


  • Tinker 需要使用者有一個后臺可以下發和管理補丁包,并且需要處理傳輸安全等部署工作,TinkerPatch 平臺幫你做了這些工作,提供了補丁后臺托管,版本管理,保證傳輸安全等功能,讓你無需搭建一個后臺,無需關心部署操作,只需引入一個 SDK 即可立即使用 Tinker。
  • 接入平臺,無需理解復雜的熱修復原理,一行代碼即可接入熱修復。實現了自動反射 Appliction 與 Library,使用者無需對自己的項目做任何的改動;
  • 當然如果我們要更好的理解tinker,需要研究其原理,源碼,若不使用TinkerPatch平臺,也可以根據需求自己搭建后臺,根據開源的tinker自行接入。

4.【通過接入TinkerPatch 平臺,開啟Tinker之旅】


第一步:注冊 TinkerPatch 平臺賬號點擊前往注冊
第二步 添加 APP

名字只是一個標識,沒有其他要求

第三步 記錄 AppKey

添加完APP,會生成一個AppKey,這個將在后續代碼中使用

第四步 客戶端SDK接入 【重點】
1.添加 gradle 插件依賴 :位于【工程】的build.gradle下,gradle 遠程倉庫依賴 jcenter。
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
        // 添加TinkerPatch 插件
        //無需再單獨引用tinker的其他庫 
       classpath "com.tinkerpatch.sdk:tinkerpatch-gradle-plugin:1.2.2"
    }
}

allprojects {
    repositories {
        jcenter()
    }
}
task clean(type: Delete) {
    delete rootProject.buildDir
}
2.集成 TinkerPatch SDK, 位于【app】的build.gradle下
dependencies {
...
    compile 'com.android.support:multidex:1.0.1'
    // 若使用annotation需要單獨引用,對于tinker的其他庫都無需再引用
    provided("com.tinkerpatch.tinker:tinker-android-anno:1.9.2")
    compile "com.tinkerpatch.sdk:tinkerpatch-android-sdk:1.2.2"
}
對于有的使用的as3.0,引入語法有變化,如下配置
dependencies {
...
    implementation 'com.android.support:multidex:1.0.1'
    // 若使用annotation需要單獨引用,對于tinker的其他庫都無需再引用
    compileOnly("com.tinkerpatch.tinker:tinker-android-anno:1.9.2")
    implementation "com.tinkerpatch.sdk:tinkerpatch-android-sdk:1.2.2"
}

注意,若使用 annotation 自動生成 Application, 需要單獨引入 Tinker 的 tinker-android-anno 庫。除此之外,我們無需再單獨引入 tinker 的其他庫。
同時在【app】目錄下需要將 TinkerPatch 相關的配置都放于 tinkerpatch.gradle 中,所以新建一個file,命名為tinkerpatch.gradle

所以我們需要在app下的build.gradle引入
dependencies {
...
}
//添加
apply from: 'tinkerpatch.gradle'
3.配置 tinkerpatchSupport 參數 ,在tinkerpatch.gradle下
apply plugin: 'tinkerpatch-support'
//這里需要說明bakPath定義了基準包的輸出位置
def bakPath = file("${buildDir}/bakApk/")
//baseInfo和variantName 對于基準包我們可以忽略不填,
//因為tinker也不會對這個做處理
//它主要的作用是在打補丁包是,我們要根據基準包輸出的路徑下的信息做修改。主要是給補丁包配置
//這里我們先默認為"",等到基準包完成后再修改。
def baseInfo = "xxx"
def variantName = "xxx"

tinkerpatchSupport {
    tinkerEnable = true
    reflectApplication = true
    protectedApp = true
    supportComponent = false
    autoBackupApkPath = "${bakPath}"
    appKey = "d8de698f2ac404aa"
    appVersion = "1.0.0"
    def pathPrefix = "${bakPath}/${baseInfo}/${variantName}"
    def name = "${project.name}-${variantName}"
    baseApkFile = "${pathPrefix}/${name}.apk"
    baseProguardMappingFile = "${pathPrefix}/${name}-mapping.txt"
    baseResourceRFile = "${pathPrefix}/${name}-R.txt"
}
android {
    defaultConfig {
        buildConfigField "boolean", "TINKER_ENABLE", "${tinkerpatchSupport.tinkerEnable}"
    }
}
tinkerPatch {
    ignoreWarning = false
    useSign = true
    dex {
        dexMode = "jar"
        pattern = ["classes*.dex"]
        loader = []
    }
    lib {
        pattern = ["lib/*/*.so"]
    }

    res {
        pattern = ["res/*", "r/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
        ignoreChange = []
        largeModSize = 100
    }

    packageConfig {
    }
    sevenZip {
        zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
    }
    buildConfig {
        keepDexApply = false
    }
}

重要參數描述

我們將原apk包稱為基準apk包,tinkerPatch直接使用基準apk包與新編譯出來的apk包做差異,得到最終的補丁包。

參數 默認值 描述 其他說明
tinkerEnable true 是否開啟 tinkerpatchSupport 插件功能。 在開啟時將不能instant run,所以若想調試程序,在debug的時候關閉false
reflectApplication false 是否反射 Application 實現一鍵接入 一般來說,接入 Tinker 我們需要改造我們的 Application, 若這里為 true, 即我們無需對應用做任何改造即可接入。
protectedApp false 是否開啟支持加固 若我們的程序需要加固,則開啟此選項
supportComponent false 是否開啟支持在補丁包中動態增加Activity 注意:新增Activity的Exported屬性必須為false?
autoBackupApkPath "" 指定每次編譯產生的 apk/mapping.txt/R.txt 歸檔存儲的位置
appKey "" 在 TinkerPatch 平臺 申請的 appkey 就是我們在上面提到的appkey
appVersion "" 和 TinkerPatch 平臺 的版本號需要對應,若這里我們輸入1.0.0,之后在TinkerPatch平臺上我們也需要輸入1.0.0 注意,我們使用 appVersion 作為 TinkerId, 我們需要保證每個發布出去的基礎安裝包的 appVersion 都不一樣。
baseApkFile "" 基準包的文件路徑,對應 tinker 插件中的 oldApk 參數 編譯補丁包時,必需指定基準版本的 apk(路徑以及名字,如果不對應,將不到找到對應的基準包進行打補丁操作),默認值為空,則表示不是進行補丁包的編譯。所以這個參數很重要。
baseProguardMappingFile "" 基準包的 Proguard mapping.txt 文件路徑, 對應 tinker 插件 applyMapping 參數 在編譯新的 apk 時候,我們希望通過保持基準 apk 的 proguard 混淆方式,從而減少補丁包的大小。這是強烈推薦的,編譯補丁包時,我們推薦輸入基準 apk 生成的 mapping.txt 文件。
baseResourceRFile "" 基準包的資源 R.txt 文件路徑, 對應 tinker 插件 applyResourceMapping 參數 在編譯新的apk時候,我們希望通基準 apk 的 R.txt 文件來保持 Resource Id 的分配,這樣不僅可以減少補丁包的大小,同時也避免由于 Resource Id 改變導致 remote view 異常。
tinkerPatch 全局信息相關的配置項 一般來說,我們無需對其中的參數做任何的修改
outputFolder 設置編譯輸出路徑,也就是補丁包生成的位置,默認在build/outputs/tinkerPatch中 一般我們不做指定,也不用配置,只需要知道補丁生成的位置在哪里
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 ture 在運行過程中,我們需要驗證基準apk包與補丁包的簽名是否一致,我們是否需要為你簽名。 做好不要做修改
4.初始化 TinkerPatch SDK
最后在我們的代碼中,只需簡單的初始化 TinkerPatch 的 SDK 即可,我們無需考慮 Tinker 是如何下載/合成/應用補丁包, 也無需引入各種各樣 Tinker 的相關類。
對于初始化操作我們分為兩種情況:
  • 第一種是我們的 reflectApplication = true,即一鍵接入,此時我們無需為接入 Tinker 而改造我們的 Application 類。
public class MyApplication extends Application {
    ...
    @Override
    public void onCreate() {
        super.onCreate();
        // 我們可以從這里獲得Tinker加載過程的信息
       ApplicationLike tinkerApplicationLike = TinkerPatchApplicationLike.getTinkerPatchApplicationLike();
        // 初始化TinkerPatch SDK, 更多配置可參照API章節中的,初始化SDK
        TinkerPatch.init(tinkerApplicationLike)
            .reflectPatchLibrary()
            .setPatchRollbackOnScreenOff(true)
            .setPatchRestartOnSrceenOff(true)
            .setFetchPatchIntervalByHours(3);
        // 每隔3個小時(通過setFetchPatchIntervalByHours設置)去訪問后臺時候有更新,通過handler實現輪訓的效果
        TinkerPatch.with().fetchPatchUpdateAndPollWithInterval();
    }
    ...
  • 第二種是我們的 reflectApplication = false,就是根據自己的需求自己改造應用的 Application。參考 tinkerpatch-sample 中的 SampleApplicationLike 類這里不做更多說明。
5.使用步驟
到此為止,我們的配置已經完成,就剩下實踐操作了:
1.獲得基準包
  • 確保更新tinkerpatchSupport中的appVersion
  • 運行 assembleRelease task 構建基準包。也就是我們通常通過命令./gradlew assembleRelease 出的apk包。
  • tinkerPatch會基于你填入的autoBackupApkPath自動備份基礎包信息到相應的文件夾,包含:apk文件、R.txt文件和mapping.txt文件 (注:mapping.txt是proguard的產物,如果你沒有開啟proguard則不會有這個文件?)
  • 根據我們的配置,此時構建完成基準包的位置即為如圖


    基準包
  • tinker按照時間戳給我們命名
  • 你要發布的apk就是該路徑下的apk,如果需要加固也是用該apk。
2.獲得補丁包
  • 構建之前:將自動保存下來的文件分別填到tinkerpatchSupport中的baseApkFile、baseProguardMappingFile和baseResourceRFile 參數中,因為我們需要根據這些參數路徑對應的apk和當前工程對比出不同,然后打出補丁。
  • 修改配置文件【重點】:


  • 所以你應該理解了,每次打補丁只需要改根據基準apk修改這兩處就可以了,baseApkFile、baseProguardMappingFile和baseResourceRFile 也就自動改變了
    def pathPrefix = "${bakPath}/${baseInfo}/${variantName}"
    def name = "${project.name}-${variantName}"

    baseApkFile = "${pathPrefix}/${name}.apk"
    baseProguardMappingFile = "${pathPrefix}/${name}-mapping.txt"
    baseResourceRFile = "${pathPrefix}/${name}-R.txt"
  • 最后運行 tinkerPatchRelease task 構建補丁包,若無渠道,可通過./gradlew tinkerPatchRelease構建,我這個有一個小米渠道,可以通過./gradlew tinkerPatchXiaomiRelease構建。或者最簡單的方式如圖:


  • 完成后,補丁包將位于 build/outputs/tinkerPatch下。


3.發布補丁包
  • 將剛所得的補丁包patch_signed_7zip.apk重命名,去掉.apk后綴,我將其命名為patch_signed。
  • 然后回到tinker平臺上,找到我們的app,然后新建版本,我們代碼中的appVersion為1.0.0


  • 上傳補丁包并下發:


4.重新安裝我們的基準包,然后刷新tinker后臺
5.殺掉進程,重新啟動。補丁加載成功

成功接入了~給個小心心鼓勵一下吧

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

推薦閱讀更多精彩內容