【原創】你見過最詳細的集成Bugly熱更新Tinker全流程

原創不易,尊重作者,轉載請注明出處,謝謝您

前言

集成Bugly的崩潰收集和應用升級是很簡單的,但是如果要集成熱更新,那就需要仔細閱讀文檔了,況且官方的文檔隨著SDK版本的更新有些地方是不太準確的,官方文檔還是很貼心,還有視頻教程,當然視頻比較老了,更多情況是開發者集成后出現很多問題不知道如何解決,基于這些情況,從實際出發,我將根據官方集成文檔一步步講解Bugly的熱更新集成方式,要是還學不會,我倒地喝水

目錄

  • 快速的集成過程(相信我,真的很快,省略掉官方文檔的廢話)
  • 集成完成后的測試
  • 基準包的補丁更新
  • 基準包多次修復BUG后的補丁更新
  • 多渠道打包
  • 基準包加固后

一、快速的集成過程

以下所有步驟都極度精簡官方文檔,并且依賴庫版本使用新的,請認真看添加注釋的代碼。

步驟1:在Project的build.gradle文件中添加bugly的tinker插件
dependencies {
        classpath 'com.android.tools.build:gradle:3.3.2'
        // 添加tinker插件支持
        classpath "com.tencent.bugly:tinker-support:1.1.5"
    }
步驟2:在Module的build.gradle文件中添加需要使用的依賴包
dependencies {
    ···
    // 解決65536方法數
    implementation 'com.android.support:multidex:1.0.3'
    // bugly Java崩潰收集和版本更新
    implementation 'com.tencent.bugly:crashreport_upgrade:1.4.0'
    // bugly native 崩潰收集
    implementation 'com.tencent.bugly:nativecrashreport:3.7.1'
    // tinker
    implementation 'com.tencent.tinker:tinker-android-lib:1.9.9'
}

以上依賴大家一看注釋就知道是做什么的,就不多闡述了

步驟3:在Module的build.gradle文件中開啟multiDex、配置ndk、配置app的簽名信息

release的簽名直接使用了官方demo的簽名文件

android {
   ···
    defaultConfig {
        ···
        // 配置ndk,根據自身情況來配置(粗略簡單來講,armeabi是真機,x86是模擬器)
        ndk {
            abiFilters 'armeabi', 'x86'//,'x86_64' ,'armeabi-v7a' , 'arm64-v8a'
        }
        // 開啟multiDex,解決65536方法數
        multiDexEnabled true 
    }

    // 簽名配置
    signingConfigs {
        // 正式包
        release {
            try {
                storeFile file("./keystore/release.keystore")
                storePassword "testres"
                keyAlias "testres"
                keyPassword "testres"
            } catch (ex) {
                throw new InvalidUserDataException(ex.toString())
            }
        }
        // 測試包
        debug {
            storeFile file("./keystore/debug.keystore")
        }
    }

    buildTypes {
        release {
            minifyEnabled true // 開啟了混淆(也可以不用開啟,默認就是false)
            signingConfig signingConfigs.release // 設置簽名文件
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

如果你開啟了混淆,在proguard-rules.pro添加代碼如下:

-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
# tinker混淆規則
-dontwarn com.tencent.tinker.**
-keep class com.tencent.tinker.** { *; }
步驟4:創建一個tinker-support.gradle文件,添加插件參數

參數如下,內容全部復制即可,一律不管···

apply plugin: 'com.tencent.bugly.tinker-support'

def bakPath = file("${buildDir}/bakApk/")

/**
 * 此處填寫每次構建生成的基準包目錄
 */
def baseApkDir = "app-0525-15-05-57"

/**
 * 對于插件各參數的詳細解析請參考
 */
tinkerSupport {

    // 開啟tinker-support插件,默認值true
    enable = true

    // 指定歸檔目錄,默認值當前module的子目錄tinker
    autoBackupApkDir = "${bakPath}"

    // 是否啟用覆蓋tinkerPatch配置功能,默認值false
    // 開啟后tinkerPatch配置不生效,即無需添加tinkerPatch
    overrideTinkerPatchConfiguration = true

    // 編譯補丁包時,必需指定基線版本的apk,默認值為空
    // 如果為空,則表示不是進行補丁包的編譯
    // @{link tinkerPatch.oldApk }
    baseApk = "${bakPath}/${baseApkDir}/app-release.apk"

    // 對應tinker插件applyMapping
    baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-release-mapping.txt"

    // 對應tinker插件applyResourceMapping
    baseApkResourceMapping = "${bakPath}/${baseApkDir}/app-release-R.txt"

    // 構建基準包和補丁包都要指定不同的tinkerId,并且必須保證唯一性
    tinkerId = "patch-1.0.3"

    // 構建多渠道補丁時使用
    // buildAllFlavorsDir = "${bakPath}/${baseApkDir}"

    // 是否啟用加固模式,默認為false.(tinker-spport 1.0.7起支持)
//     isProtectedApp = true

    // 是否開啟反射Application模式
    enableProxyApplication = false

    // 是否支持新增非export的Activity(注意:設置為true才能修改AndroidManifest文件)
    supportHotplugComponent = true

}

/**
 * 一般來說,我們無需對下面的參數做任何的修改
 * 對于各參數的詳細介紹請參考:
 * https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97
 */
tinkerPatch {
    //oldApk ="${bakPath}/${appName}/app-release.apk"
    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"
//        path = "/usr/local/bin/7za"
    }
    buildConfig {
        keepDexApply = false
        //tinkerId = "1.0.1-base"
        //applyMapping = "${bakPath}/${appName}/app-release-mapping.txt" //  可選,設置mapping文件,建議保持舊apk的proguard混淆方式
        //applyResourceMapping = "${bakPath}/${appName}/app-release-R.txt" // 可選,設置R.txt文件,通過舊apk文件保持ResId的分配
    }
}

supportHotplugComponent 在官方文檔是false,我設置成了true,注釋寫的很情況了。簡單說一下export的作用就是“是否允許外部來啟動該activity”,默認創建的activity的export其實就是false。
這里只講最重要的兩個參數:

baseApkDir : 填寫需要打補丁的基準包的文件夾名稱(只能填寫基準包的,后續詳情說明)。
tinkerId :構建基準包和補丁包的唯一id,必須保證唯一,補丁包就是根據基準包的tinkerId 來找到對應的包進行補丁更新的。

其他參數真的可以不用管,如果對其他參數有疑問,點這里https://bugly.qq.com/docs/utility-tools/plugin-gradle-hotfix/

步驟5:在Module的build.gradle中依賴步驟4的插件
apply plugin: 'com.android.application'
// 依賴插件
apply from: 'tinker-support.gradle'
步驟6:創建自定義的ApplicationLike類和Application類

自定義的ApplicationLike代碼如下:

public class MyApplicationLike extends DefaultApplicationLike {

    private static final String TAG = "MyApplicationLike";

    public MyApplicationLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent) {
        super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
    }

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

        // 第二個參數為bugly申請的應用appId,調試時,將第三個參數改為true
        Bugly.init(getApplication(), "1fc5a53101", true);


    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    @Override
    public void onBaseContextAttached(Context base) {
        super.onBaseContextAttached(base);
        // you must install multiDex whatever tinker is installed!
        MultiDex.install(base);

        // 安裝tinker
        // TinkerManager.installTinker(this); 替換成下面Bugly提供的方法
        Beta.installTinker(this);
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    public void registerActivityLifecycleCallback(Application.ActivityLifecycleCallbacks callbacks) {
        getApplication().registerActivityLifecycleCallbacks(callbacks);
    }
}

自定義的Application代碼如下:

public class MyApplication extends TinkerApplication {

    public MyApplication() {
        super(ShareConstants.TINKER_ENABLE_ALL, "app.fynnjason.tinkerdemo.MyApplicationLike",
                "com.tencent.tinker.loader.TinkerLoader", false);
    }
}

在MyApplicationLike 類中,只需要關注onCreate()方法,將所有需要在Application中初始化的代碼都放到這兒來。MyApplication 類是固定寫法了,只需要替換super的第二個參數為MyApplicationLike 的路徑即可,代碼和注釋已經非常清楚了,相信大家都看的懂。其他的可以一律不用管。

最后一步:在AndroidManifest.xml中添加Application
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="app.fynnjason.tinkerdemo">

    <application
        android:name=".MyApplication" // 這里
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        tools:ignore="GoogleAppIndexingWarning">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

到此,集成就算完成了。有人可能要問了,官方文檔中還需要在AndroidManifest中配置權限、特定的Activity、Android N的共享文件,為啥這里沒有。因為在SDK1.3.1及以上版本,可以不用進行以上配置,aar已經在AndroidManifest配置了,并且包含了對應的資源文件,所以我這里也不用配置了。好了,集成完畢,接下來是熱更新的重頭戲了。

二、集成完成后的測試

首先我們需要打一個基準包,基準包相當于就是我們的一個正式包,在tinker-support.gradle文件中,我們修改tinkerId的參數為:

tinkerId = "base-1.0.1"

tinkerId 的命名是自由的,但是為了我們規范,建議是填寫有規范性的命名(這里用base來代表基準包,用patch代表補丁包),baseApkDir不用修改,這個值在打基準包時是沒有作用的。然后我們打開Android Studio的右側邊欄(一般都是右邊),找到Gradle工具-assembleRelease,注意不同Android Studio版本的assembleRelease位置稍有不同,例如官方文檔是2.2版本,在build目錄下,而我使用的是3.3.2,在other目錄下。



可以看到相似的還有assembleDebug,就是打debug包的,很好理解。執行assembleRelease,等待一會兒,我們就可以得到創建好的基準包



這個就相當于是我們用來上線的正式包了,記住此時的文件包名(app-0525-15-05-57,app-0525-15-05-57,app-0525-15-05-57,重要事情說三遍),把里面的app-release.apk扔到手機跑起來吧!這里需要說一下,基準包必須聯網一次,也就是在手機上安裝一次并且打開訪問一次網絡,這樣Bugly后臺才會記錄這個基準包,以便后續補丁包的下發。如果我們繼承沒有任何問題,那么我們就可以在AndroidStudio日志中找到Bugly成功的日志。

可以看到tinkerId參數,app版本號等信息,接下來是熱更新的步驟,也是最最關鍵的。

三、基準包的補丁更新

現在我們去修改demo中的信息,修改TextView的文字(比如這是修改好了一個bug)。然后回到tinker-support.gradle文件中,我們修改tinkerId,此時就是補丁包的名稱,然后再修改baseApkDir,baseApkDir填寫基準包的包名,這就是上一步中三遍說的那個包名稱。如圖:



然后打開右側欄的Gradle工具欄,找到如圖所示:



雙擊成功編譯后,我們可以在如圖所示查看到補丁包:

上圖中第一個紅框是每次編譯都會生成的apk目錄,除了基準包的,其他生成的可以不用管,這里只是圈出來說明一下。第二個框是重點了!只使用xxxx_7zip.apk,將這個apk上傳到Bugly的熱更新管理后臺即可,具體看下圖:



上傳補丁文件后,會顯示目標版本,也就是基準包的版本,點擊立即下發即可,下發后等待大概十分鐘、下發后等待大概十分鐘、下發后等待大概十分鐘,重要事情說三遍,因為Bugly下發有延遲,接下來就是驗證補丁是否下載成功了,以下執行流程一定不能錯。
1.首先殺掉我們app的進程
2.打開我們的app,等待幾秒,查看log日志,如圖(圖片來自官方文檔)


如果出現上面這些信息,那么你的補丁就下發成功了
3.再次殺掉我們的app進程

4.再次打開我們的app,查看我們修改的內容是否成功
這就是基本的熱更新了~如果有疑問,請評論提出

四、基準包多次修復BUG后的補丁更新

上面我們只講到了一次修復bug,如果我們基準包出現了一次bug,我們用熱更新修復了,然后我們又發現了一個bug,這時候我們就需要再打一次補丁包了,那這時候我們應該怎么做呢?我會告訴你非常簡單,接著往下看:
首先我們修復好代碼,繼續來到tinker-support.gradle,這里我們只需要修改tinkerId的參數,第一次我們修復代碼,這里的參數為patch-1.0.1,那么這一次我們就改成patch-1.0.2,只要保證每次唯一就行。baseApkDir不改動,依然是基準包的包名,具體如圖所示:



然后我們使用Gradle工具欄,執行



最后我們上傳生成好的xxx_7zip.apk到管理后臺即可,流程和之前一樣,等待十分鐘后,我們再來測試是否成功!

五、多渠道打包

修修改改多次后,還是覺得官方文檔已經寫的很好了,所以推薦大家直接閱讀官方文檔
https://buglydevteam.github.io/2017/05/15/solution-of-multiple-channel-hotpatch/#%E6%80%BB%E7%BB%93

同一版本,不同渠道中代碼不同的熱更新

有這么一種情況,同一版本下,不同渠道的一部分代碼不同,比如我們開發的APP,在應用寶、小米、華為平臺叫微信,在OPPO、VIVO平臺叫微信聊天,或者部分代碼有區別(比如刺激戰場游戲針對不同廠商優化不同,但版本號是相同的)等等情況,這時候,我們怎么實現同一個BUG的熱更新呢?

解決方案:

1.首先是基準包的生成,我們需要將渠道分組,完全相同代碼的為一組,在tinker-support.gradle文件中,設置該組唯一的tinkerId,例如我們把
應用寶、小米、華為平臺設置為base-1.0.1-a,將OPPO、VIVO平臺設置為base-1.0.1-b,然后進行多渠道打包,最后我們可以得到每組的bakApk中的基準包的內容,需要說的是,美團多渠道打包的命令每次執行都會刪掉上次多渠道打包的內容,所以大家要每次記得要備份一下該組的基準包!一定要備份!記得備份!
然后就可以將多渠道打包的apk上線了。
2.現在我們來生成每組的補丁,將bug修復后,我們依舊回到tinker-support.gradle文件中,針對不同組的補丁,需要設置不同的tinkerId,例如應用寶、小米、華為平臺設置為patch-1.0.1-a,將OPPO、VIVO平臺設置為patch-1.0.1-b,其中bakApk的參數就填寫該組的包名即可,相信大家應該明白這個意思。接著我們生成應用寶、小米、華為平臺的補丁后,我們將補丁上傳到Bugly后臺管理上發布,然后繼續生成OPPO、VIVO平臺的補丁包,再上傳到Bugly后臺管理上發布。這樣,我們就實現了同一版本下,部分代碼不同下的熱更新效果了。
PS:只能共存最多5組補丁,只能共存最多5組補丁,只能共存最多5組補?。∫簿褪侵荒芊殖勺疃?組喲

六、基準包的加固

確實很簡單,畢竟大家都使用的第三方加固工具,官方文檔也沒有特意去寫文檔,這里就摘錄一下,實際效果也確實如此。

需要集成升級SDK版本1.3.0以上版本才支持加固。

經過測試的加固產品:

  1. 騰訊樂固
  1. 愛加密
  1. 梆梆加固
  1. 360加固(SDK 1.3.1之后版本支持)

其他產品需要大家進行驗證。

1.設置isProtectedApp參數

在tinker-support配置當中設置isProtectedApp = true,表示你要打加固的的apk。 是否使用加固模式,僅僅將變更的類合成補丁。注意,這種模式僅僅可以用于加固應用中。

2.將基準包進行加固

如果你的app需要進行加固,你需要將你打出的基準包上傳到具體的加固平臺進行加固,例如樂固,加固完成之后需要對apk進行重簽名

jarsigner -verbose -keystore <YOUR_KEYSTORE> -signedjar <SIGNED_APK> <UNSIGNED_APK> <KEY_ALIAS>

以上命令說明:
-verbose:指定生成詳細輸出
-keystore:指定證書存儲路徑
-signedjar:改選項的三個參數分別為簽名后的apk包、未簽名的apk包、數字證書別名

示例:

 jarsigner -verbose -keystore keystore/release.keystore -signedjar app-release-signed.apk app-release.encrypted.apk testres

如果你使用的加固產品提供了GUI的加固和簽名工具,可以通過工具來進行加固和簽名。

3.根據未加固的基準包生成補丁包

打patch包的操作跟普通打包方式一致。

注意:這里指定的基線版本是未加固的版本。

4.測試驗證

測試驗證的版本是經過加固并且已經重簽名后的apk,安裝到測試設備并上報聯網,其他操作與普通打包無異。

其他問題

美團多渠道包使用加固工具后,渠道信息null的解決辦法

https://github.com/Jay-Goo/ProtectedApkResignerForWalle

多渠道加固的新方式(樂固篇)

大多數情況我們的APP都是需要多渠道打包和加固的,如果按照之前的方式使用美團打多渠道包并且加固再解決加固后的渠道信息,這是一個挺麻煩的事,下面介紹一種更簡單的方法,不妨看看:
1.使用普通方式打一個基準包
2.將基準包直接使用樂固的簽名加固和多渠道輸出
3.運行樂固生成的多渠道apk后打一個基準包的補丁包
4.上傳補丁包,完成
是不是覺得很像目錄三的熱更新方式?其他加固工具方法類似,大家可以自行測試~

結論

如果文章對你有幫助,點個贊支持一下~

附錄:
官方文檔:https://bugly.qq.com/docs/user-guide/instruction-manual-android-hotfix/?v=20181014122344

官方文檔中熱更新的常見問題:https://bugly.qq.com/docs/user-guide/faq-android-hotfix/?v=20181014122344

官方文檔中Bugly的常見問題:https://bugly.qq.com/docs/user-guide/faq-android/?v=20181014122344

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

推薦閱讀更多精彩內容