Android熱修復之Tinker整合最新詳解

Dale_Dawson發表於2019-04-02

前言

該文章屬於初級整合詳解,側重Tinker的使用,如若想深入瞭解其原理請自行查閱相關文件Tinker相關文件

當前市面的熱補丁方案有很多,其中比較出名的有阿里的 AndFix、美團的 Robust 以及 QZone 的超級補丁方案,先來做一個對比

熱修復對比表.png
通過對比我們發現Tinker還是有一定的優勢。 下面我們就來擺一擺Tinker的用法

1.先去Tinker平臺註冊一個AppKey,後面會使用到

註冊流程

2.新增 gradle 外掛依賴

gradle 遠端倉庫依賴 jcenter

buildscript {
    repositories {
        //mavenLocal()
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
        //無需再單獨引用tinker的其他庫
        classpath "com.tinkerpatch.sdk:tinkerpatch-gradle-plugin:${TINKERPATCH_VERSION}"
    }
}
複製程式碼

3.整合 TinkerPatch SDK

新增 TinkerPatch SDK 庫的 denpendencies 依賴

//若使用annotation需要單獨引用,對於tinker的其他庫都無需再引用
annotationProcessor("com.tinkerpatch.tinker:tinker-android-anno:${TINKER_VERSION}") { changing = true }
compileOnly("com.tinkerpatch.tinker:tinker-android-anno:${TINKER_VERSION}") { changing = true }
implementation("com.tinkerpatch.sdk:tinkerpatch-android-sdk:${TINKERPATCH_VERSION}") { changing = true }
複製程式碼

4.新建tinkerpatch.gradle tinkerpatch.gradle

為了操作簡單,可以直接複製上面連結中的tinkerpatch.gradle放在app目錄下 為了簡單方便,我們將 TinkerPatch 相關的配置都放於 tinkerpatch.gradle 中 在app的build.gradle中引入

apply from: 'tinkerpatch.gradle'
複製程式碼

tinkerpatch.gradle中的baseInfo和variantName可以先不用管,後面使用

def bakPath = file("${buildDir}/bakApk/")
def baseInfo = "app-1.0.0-0115-14-59-51"
def variantName = "debug"
複製程式碼

5.配置tinkerpatchSupport引數

1)baseInfo和variantName引數先按照這個放著暫時不管
2)appKey請自行登入tinker官網登入並建立應用獲取即可 To: www.tinkerpatch.com/
3)appVersion版本號一般對應你的versionName就行了(versionName改的話 這裡就改,這裡的appVersion對應tinker官網上傳patch時的版本,切記!)
/** 可以在debug的時候關閉 tinkerPatch **/
    /** 當disable tinker的時候需要新增multiDexKeepProguard和proguardFiles,
        這些配置檔案本身由tinkerPatch的外掛自動新增,當你disable後需要手動新增
        你可以copy本示例中的proguardRules.pro和tinkerMultidexKeep.pro,
        需要你手動修改'tinker.sample.android.app'本示例的包名為你自己的包名, com.xxx字首的包名不用修改
     **/
    tinkerEnable = true
    reflectApplication = true
    /**
     * 是否開啟加固模式,只能在APK將要進行加固時使用,否則會patch失敗。
     * 如果只在某個渠道使用了加固,可使用多flavors配置
     **/
    protectedApp = false
    /**
     * 實驗功能
     * 補丁是否支援新增 Activity (新增Activity的exported屬性必須為false)
     **/
    supportComponent = true

    autoBackupApkPath = "${bakPath}"

    appKey = "你第一步申請的appkey"

    /** 注意: 若釋出新的全量包, appVersion一定要更新 **/
    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"
複製程式碼

具體的引數詳解如下

引數 預設值 描述
tinkerEnable true 是否開啟 tinkerpatchSupport 外掛功能。
appKey "" 在 TinkerPatch 平臺 申請的 appkey, 例如 sample 中的 'f828475486f91936'
appVersion "" 在 TinkerPatch 平臺 輸入的版本號, 例如 sample 中的 '1.0.0'。 注意,我們使用 appVersion 作為 TinkerId, 我們需要保證每個釋出出去的基礎安裝包的 appVersion 都不一樣。
reflectApplication false 是否反射 Application 實現一鍵接入;一般來說,接入 Tinker 我們需要改造我們的 Application, 若這裡為 true, 即我們無需對應用做任何改造即可接入。
autoBackupApkPath "" 將每次編譯產生的 apk/mapping.txt/R.txt 歸檔儲存的位置
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 異常。
protectedApp false 是否開啟支援加固,注意:只有在使用加固時才能開啟此開關
supportComponent false 是否開啟支援在補丁包中動態增加Activity 注意:新增Activity的Exported屬性必須為false
backupFileNameFormat '${appName}-${variantName}' 格式化命名備份檔案 這裡請使用單引號

上述步驟 配置完之後 sync編譯即可

6.初始化 TinkerPatch SDK

以上屬性中有一個reflectApplication 屬性,所以初始化 TinkerPatch SDK有兩種方法

1. reflectApplication = true 的情況

若我們使用 reflectApplication 模式,我們無需為接入 Tinker 而改造我們的 Application 類。相當於自己寫一個Application,本人就是使用的這種方式。

public class MyApplication extends Application {
    private ApplicationLike tinkerApplicationLike;

    @Override
    public void onCreate() {
        super.onCreate();
        initTinkerPatch();
    }

    /**
     * 我們需要確保至少對主程式跟patch程式初始化 TinkerPatch
     */
    private void initTinkerPatch() {
        if (BuildConfig.TINKER_ENABLE) {
            // 我們可以從這裡獲得Tinker載入過程的資訊
            // 我們可以從這裡獲得Tinker載入過程的資訊
            tinkerApplicationLike = TinkerPatchApplicationLike.getTinkerPatchApplicationLike();

            // 初始化TinkerPatch SDK, 更多配置可參照API章節中的,初始化SDK
            TinkerPatch.init(tinkerApplicationLike)
                    .reflectPatchLibrary()
                    .setPatchRollbackOnScreenOff(true)
                    .setPatchRestartOnSrceenOff(true)
                    .setFetchPatchIntervalByHours(3);

            // 每隔3個小時(通過setFetchPatchIntervalByHours設定)去訪問後臺時候有更新,通過handler實現輪訓的效果
            TinkerPatch.with().fetchPatchUpdateAndPollWithInterval();
        }
    }
}
複製程式碼
2. reflectApplication = false 的情況

不需要自己寫Application類,新建一個整合DefaultApplicationLike的類即可

public class SampleApplicationLike extends DefaultApplicationLike {
   ...
    @Override
    public void onCreate() {
        super.onCreate();
        // 初始化TinkerPatch SDK, 更多配置可參照API章節中的,初始化 SDK
        TinkerPatch.init(this)
            .reflectPatchLibrary()
            .setPatchRollbackOnScreenOff(true)
            .setPatchRestartOnSrceenOff(true)
            .setFetchPatchIntervalByHours(3);

        // 每隔3個小時(通過setFetchPatchIntervalByHours設定)去訪問後臺時候有更新,通過handler實現輪訓的效果
        TinkerPatch.with().fetchPatchUpdateAndPollWithInterval();
    }
   ...
}
複製程式碼

setFetchPatchIntervalByHour(3)// 每隔3個小時(通過setFetchPatchIntervalByHours設定)去訪問後臺時候有更新,通過handler實現輪訓的效果 如果你想快點看見效果,可以將這個數字改小點

7.AndroidManifest.xml中完成配置

將AndroidManifest.xml中的新增上相應的網路和SD的許可權,還要在application中加上 android:name=".MyApplication"

 <!--tinker修復所需-->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <application
        android:name=".MyApplication"
複製程式碼

到現在tinker基本上已經整合完了

8.開始體驗

我們在xml裡隨便寫個view以便區分新舊包。

 <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="我是舊版"
            android:textSize="30dp" />
複製程式碼

寫完之後找到編輯器右側的Gradle下點選build下的assembleDebug進行編譯

Android熱修復之Tinker整合最新詳解

完成後在左側build檢視,最下面就是最新生成的apk

Android熱修復之Tinker整合最新詳解
生成之後,我們將此debug包安裝到手機上,執行效果為我們剛才在xml裡寫的舊佈局

接下來,我們打補丁包(模擬修復bug)

首先到tinkerpatch.gradle裡更改我們們先前介紹的兩個引數: baseInfo:修改為上面生成的對應資料夾名(請修改為自己的) variantName:因為打的debug包,所以傳入debug即可

/**
 * TODO: 請按自己的需求修改為適應自己工程的引數
 */
//基包路徑
def bakPath = file("${buildDir}/bakApk/")
//基包資料夾名(打補丁包的時候,需要修改)
def baseInfo = "app-1.0.0-0116-11-22-29"(上圖中的最後一個app-.....)
//版本名稱
def variantName = "debug"
複製程式碼

然後我們在xml中做寫修改

 <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="我是新版"
            android:textSize="30dp" />
複製程式碼

然後打出差異包補丁,繼續找到Gradle下的tinker目錄,點選tinkerPatchDebug進行編譯

Android熱修復之Tinker整合最新詳解
編譯完成之後,請到工程目錄下app->outputs->檢視生成的資料夾 tinkerPatch:
Android熱修復之Tinker整合最新詳解
接下來,我們將圖中箭頭所指的patch_signed_7zip.apk上傳至tinker官網(釋出補丁):
Android熱修復之Tinker整合最新詳解
點選圖中的補丁下發,然後將上面生成的patch_signed_7zip.apk上傳至補丁檔案處,點選提交

接下來就是見證奇蹟的時刻

退出應用,再次開啟應用,顯示的就是新的佈局檔案的內容。

然後到tinker官網,我們可以檢視補丁下發進度及當前成功率。

簡書地址:www.jianshu.com/p/09a3902fc…

相關文章