一、簡述
在上一篇《熱修復——Tinker的集成與使用》中,根據Tinker官方Wiki集成了Tinker,但那僅僅只是本地集成,有一個重要的問題沒有解決,那就是補丁從服務器下發到用戶手機上,如果你團隊中的後臺開發人員實力夠強,那麼完全可以自己做一個補丁管理系統,但我想應該沒多少人願意花精力在這個後臺管理系統的開發上面吧,且開發有時候就是在造bug,鬼知道會挖出一個多大的坑呢?對於這樣的一個問題,據我所知,市面上有3種Tinker的補丁管理系統,如下:
「Bugly」和「tinker-manager」是免費的,「tinkerpatch」是收費的,因爲「tinkerpatch」收費,所以暫時不做考慮。Bugly由騰訊團隊開發並維護,穩定性肯定沒得說,而「tinker-manager」是GitHub上個人開發者開發維護的,穩定性沒法保證(我沒有貶低開發者的意思,畢竟勢單力薄,人多力量大嘛),故本人覺得,Bugly是目前最優的Tinker熱修復解決方案。在開始進入Bugly集成之前,你可以先點擊下載Demo試試看看。
二、獲取App ID
要使用Bugly的熱修復功能,首先得註冊並登錄Bugly,然後點擊進入「Bugly產品頁面」,或點擊“我的產品 ”。
我這個賬號之前是沒有創建過產品,所以這裏什麼也沒有,接着點擊“新建產品”。
填寫必要的信息後,點擊“保存”。
通過“產品設置”,選擇剛剛創建的產品(圖中第3步),可以查看到產品對應的App ID。
這個App ID很重要,先記錄好,後續會用到。
Demo的App ID爲: 3062edb401。不要用我的,對你來說一點用處都沒有,請使用你自己產品的App ID。
二、添加插件依賴
項目的build.gradle:
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0'
// tinkersupport插件(1.0.3以上無須再配置tinker插件)
classpath "com.tencent.bugly:tinker-support:1.1.1"
}
三、集成SDK
app的build.gradle:
apply from: 'tinker-support.gradle'
android {
defaultConfig {
...
// 開啓multidex
multiDexEnabled true
}
// recommend
dexOptions {
jumboMode = 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
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
debuggable true
minifyEnabled false
signingConfig signingConfigs.debug
}
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
dependencies {
...
implementation "com.android.support:multidex:1.0.1" // 多dex配置
implementation 'com.tencent.bugly:crashreport_upgrade:1.3.4'// 遠程倉庫集成方式(推薦)
}
簽名配置部分請根據你項目的實際情況修改,如:
四、配置Tinker
在app的build.gradle文件同級目錄下創建一個tinker-support.gradle文件,內容如下:
apply plugin: 'com.tencent.bugly.tinker-support'
def bakPath = file("${buildDir}/bakApk/")
/**
* 此處填寫每次構建生成的基準包目錄
*/
def baseApkDir = "tinker-bugly-1211-16-01-34"
def myTinkerId = "base-" + rootProject.ext.android.versionName // 用於生成基準包(不用修改)
//def myTinkerId = "patch-" + rootProject.ext.android.versionName + ".0.0" // 用於生成補丁包(每次生成補丁包都要修改一次,最好是 patch-${versionName}.x.x)
/**
* 對於插件各參數的詳細解析請參考
*/
tinkerSupport {
// 開啓tinker-support插件,默認值true
enable = true
// 是否啓用加固模式,默認爲false.(tinker-spport 1.0.7起支持)
// isProtectedApp = true
// 是否開啓反射Application模式
enableProxyApplication = true
// 是否支持新增非export的Activity(注意:設置爲true才能修改AndroidManifest文件)
supportHotplugComponent = 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 = "${myTinkerId}"
// 構建多渠道補丁時使用
// buildAllFlavorsDir = "${bakPath}/${baseApkDir}"
}
/**
* 一般來說,我們無需對下面的參數做任何的修改
* 對於各參數的詳細介紹請參考:
* https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97
*/
tinkerPatch {
...
}
1、overrideTinkerPatchConfiguration
當overrideTinkerPatchConfiguration = true時,tinkerPatch可以省略不寫,Bugly會加載默認的Tinker配置。但請注意,如果你的so文件不是存放在libs目錄下(與src目錄同級),又或者資源文件的存放在你自定義的目錄中,那麼這時你要小心了,這些文件在製作補丁包時不會被檢測,也就是說這些so文件和資源文件將不會被熱修復,這種情況下就需要將overrideTinkerPatchConfiguration = false,並設置tinkerPatch的lib和res屬性。
其它具體的配置與說明可以查看「Tinker-接入指南」。
2、baseApkDir
baseApkDir是基準包(也稱基線包)的目錄,在生產補丁時需要根據基準包在bakApk下具體文件夾名字修改,如:bakApk/xxxx,到時生成補丁包時要將baseApkDir的值改爲xxxx。(xxxx是Tinker自動生成的,根據時間戳來命名)。
3、tinkerId
tinkerId是Bugly熱修復方案最最重要的一個因素,一般取值爲git版本號、versionName等等(我習慣用versionName),它會將補丁包與基準包產生對應關係,假設基準包的tinkerId爲 base-1.0,則生成的補丁包中的YAPATCH.MF文件關係如下:
Bugly要求baseApk(基準包)的tinkerId與補丁包的tinkerId要不一樣。所以,在生成基準包時,請用如下tinkerId:
def myTinkerId = "base-" + rootProject.ext.android.versionName // 用於生成基準包(不用修改)
當生成補丁包時,請使用如下tinkerId:
def myTinkerId = "patch-" + rootProject.ext.android.versionName + ".0.0" // 用於生成補丁包(每次生成補丁包都要修改一次,最好是 patch-${versionName}.x.x)
對於同一個基準包,我們可能會多次生成補丁包上傳到Bugly的熱修復管理後臺,這時,這些補丁包的tinkerId也要不一樣,不然的話,當客戶手機上的App在獲取補丁時,會錯亂(親測,當同個基準包的補丁包的tinkerId一樣時,App每次重啓都會獲取不同的補丁包,導致tinkerId相同的補丁包輪流下發)。所以,”patch-” + rootProject.ext.android.versionName + “.0.0”中的”.0.0”(稱爲計數)就是爲了區分每次生成的補丁包,如.0.1,.0.2等等,建議versionName更新時計數重置。
因爲Tinker的配置放在了tinker-support.gradle文件中,與app的build.gradle不在同一個文件中,所以沒辦法通過android.defaultConfig.versionName直接獲取App的versionName,這裏我使用了config.gradle來提取共同的屬性,rootProject.ext.android.versionName獲取的是config.gradle中的versionName屬性,詳情請百度。
4、補丁新舊判定
def myTinkerId = "patch-" + rootProject.ext.android.versionName + ".0.0" // 用於生成補丁包(每次生成補丁包都要修改一次,最好是 patch-${versionName}.x.x)
對於一個基準包,可以在Bugly上發佈多個補丁包(切記tinkerid不同),這裏或許會讓你誤以爲計數越大,表明補丁越新,這是錯誤的,這個計數僅僅只是區分不同的補丁包而已,它沒有標記補丁新舊的作用,補丁新舊由Bugly來判定,最後上傳的補丁便是最新的補丁,舉個例子,我在昨天上傳了tinkerid爲”patch-1.0.0.9”的補丁1,在今天上傳了tinkerid爲”patch-1.0.0.1”的補丁2,雖然補丁2的計數比補丁1小,但補丁2比補丁1晚上傳,所以補丁2是最新的補丁,即補丁新舊與計數無關。Bugly會下發並應用最新的補丁(即補丁2),但還是建議計數從小到大計算,這裏僅僅只是說明Bugly如何判定補丁新舊罷了。
五、初始化SDK
Bugly的初始化工作需要在Application中完成,但對原生Tinker來說,默認的Application是無法實現熱修復的。看過Tinker官方Wiki的人應該知道,Tinker針對Application無法熱修復的問題,給予開發者兩個選擇,分別是:
- 使用「繼承TinkerApplication + DefaultApplicationLike」。
- 使用「DefaultLifeCycle註解 + DefaultApplicationLike」。
這2種選擇都需要對自定義的Application進行改造,對於自定義Application代碼不多的情況來說還可以接受,但有些情況還是比較”討厭”這2種選擇的,對此,Bugly給出了它的2種解決方法,分別如下:
- 使用原來的自定義Application,Bugly通過反射爲App動態生成新的Application。
- 使用「繼承TinkerApplication + DefaultApplicationLike」。
DefaultLifeCycle註解在Bugly中被閹割了。
分別對應tinker-support.gradle文件中enableProxyApplication的值:true或false。
1、enableProxyApplication = true
Bugly將通過反射的方式針對項目中自定義的Application動態生成新的Application,下圖是源碼中的AndroidManifest.xml和編譯好的apk中的AndroidManifest.xml:
既然將enableProxyApplication的值設置爲true,那接下來的重點就是完成Bugly的初始化工作了。需要在自定義的Application的onCreate()中進行Bugly的配置,在attachBaseContext()中進行Bugly的安裝:
public class MyApplication extends Application {
private Context mContext;
@Override
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
// 這裏實現SDK初始化,appId替換成你的在Bugly平臺申請的appId
// 調試時,將第三個參數改爲true
configTinker();
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
// you must install multiDex whatever tinker is installed!
MultiDex.install(mContext);
// 安裝tinker
// 此接口僅用於反射Application方式接入。
Beta.installTinker();
}
}
注意:
1. Bugly的安裝必須在attachBaseContext()方法中,否則將無法從Bugly服務器獲取最新補丁。
2. tinker需要你開啓MultiDex,你需要在dependencies中進行配置compile “com.android.support:multidex:1.0.1”纔可以使用MultiDex.install方法。
最後在清單文件中,聲明使用我們自定義的Application即可:
<application
android:name="com.lqr.MyApplication"
...>
2、enableProxyApplication = false
這是Bugly推薦的方式,穩定性有保障(因爲第1種方式使用的是反射,可能會存在不穩定的因素),它需要對Application進行改造,首先就是繼承TinkerApplication,然後在默認的構造函數中,將第2個參數修改爲你項目中的ApplicationLike繼承類的全限定名稱:
public class SampleApplication extends TinkerApplication {
public SampleApplication() {
super(ShareConstants.TINKER_ENABLE_ALL, "com.lqr.SampleApplicationLike",
"com.tencent.tinker.loader.TinkerLoader", false);
}
}
注意:這個類集成TinkerApplication類,這裏面不做任何操作,所有Application的代碼都會放到ApplicationLike繼承類當中
參數解析
參數1:tinkerFlags 表示Tinker支持的類型 dex only、library only or all suuport,default: TINKER_ENABLE_ALL
參數2:delegateClassName Application代理類 這裏填寫你自定義的ApplicationLike
參數3:loaderClassName Tinker的加載器,使用默認即可
參數4:tinkerLoadVerifyFlag 加載dex或者lib是否驗證md5,默認爲false
接着就是創建ApplicationLike繼承類:
public class SampleApplicationLike extends DefaultApplicationLike {
public static final String TAG = "Tinker.SampleApplicationLike";
private Application mContext;
public SampleApplicationLike(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();
mContext = getApplication();
configTinker();
}
@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
Beta.installTinker(this);
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public void registerActivityLifecycleCallback(Application.ActivityLifecycleCallbacks callbacks) {
getApplication().registerActivityLifecycleCallbacks(callbacks);
}
@Override
public void onTerminate() {
super.onTerminate();
Beta.unInit();
}
}
注意:
SampleApplicationLike這個類是Application的代理類,以前所有在Application的實現必須要全部拷貝到這裏,在onCreate方法調用SDK的初始化方法,在onBaseContextAttached中調用Beta.installTinker(this)。
最後在清單文件中,聲明改造好的Application(注意不是ApplicationLike):
<application
android:name="com.lqr.SampleApplication"
...>
3、配置Bugly
這是Bugly官方給出的配置,應有盡有,註釋也很nice,請仔細看看,對項目的功能拓展與用戶體驗有幫助:
private void configTinker() {
// 設置是否開啓熱更新能力,默認爲true
Beta.enableHotfix = true;
// 設置是否自動下載補丁,默認爲true
Beta.canAutoDownloadPatch = true;
// 設置是否自動合成補丁,默認爲true
Beta.canAutoPatch = true;
// 設置是否提示用戶重啓,默認爲false
Beta.canNotifyUserRestart = true;
// 補丁回調接口
Beta.betaPatchListener = new BetaPatchListener() {
@Override
public void onPatchReceived(String patchFile) {
Toast.makeText(mContext, "補丁下載地址" + patchFile, Toast.LENGTH_SHORT).show();
}
@Override
public void onDownloadReceived(long savedLength, long totalLength) {
Toast.makeText(mContext,
String.format(Locale.getDefault(), "%s %d%%",
Beta.strNotificationDownloading,
(int) (totalLength == 0 ? 0 : savedLength * 100 / totalLength)),
Toast.LENGTH_SHORT).show();
}
@Override
public void onDownloadSuccess(String msg) {
Toast.makeText(mContext, "補丁下載成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onDownloadFailure(String msg) {
Toast.makeText(mContext, "補丁下載失敗", Toast.LENGTH_SHORT).show();
}
@Override
public void onApplySuccess(String msg) {
Toast.makeText(mContext, "補丁應用成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onApplyFailure(String msg) {
Toast.makeText(mContext, "補丁應用失敗", Toast.LENGTH_SHORT).show();
}
@Override
public void onPatchRollback() {
}
};
// 設置開發設備,默認爲false,上傳補丁如果下發範圍指定爲“開發設備”,需要調用此接口來標識開發設備
Bugly.setIsDevelopmentDevice(mContext, false);
// 多渠道需求塞入
// String channel = WalleChannelReader.getChannel(getApplication());
// Bugly.setAppChannel(getApplication(), channel);
// 這裏實現SDK初始化,appId替換成你的在Bugly平臺申請的appId
Bugly.init(mContext, "e9d0b7f57f", true);
}
這裏就用到了一開始獲取到的App ID了,將其傳入Bugly.init()方法的第二個參數,切記,用你自己的App ID。
其中如下兩個方法很重要:
- Bugly.setIsDevelopmentDevice()
設置當前設備是不是開發設備,這跟Bugly上傳補丁包時所選的”下發範圍”有關。
- Bugly.init(context, appid, isDebug)
這個方法除了設置App ID外,還可以設置是否輸出Log,可以觀察到Bugly在App啓動時做了哪些聯網操作。
六、AndroidManifest.xml
1、 權限配置
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.READ_LOGS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
2、Activity配置
<activity
android:name="com.tencent.bugly.beta.ui.BetaActivity"
android:configChanges="keyboardHidden|orientation|screenSize|locale"
android:theme="@android:style/Theme.Translucent"/>
3、FileProvider配置
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
如果你使用的第三方庫也配置了同樣的FileProvider, 可以通過繼承FileProvider類來解決合併衝突的問題,示例如下:
<provider
android:name=".utils.BuglyFileProvider"
android:authorities="${applicationId}.fileProvider"
android:exported="false"
android:grantUriPermissions="true"
tools:replace="name,authorities,exported,grantUriPermissions">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"
tools:replace="name,resource"/>
</provider>
4、升級SDK下載路徑配置
在res目錄新建xml文件夾,創建provider_paths.xml文件如下:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!-- /storage/emulated/0/Download/${applicationId}/.beta/apk-->
<external-path name="beta_external_path" path="Download/"/>
<!--/storage/emulated/0/Android/data/${applicationId}/files/apk/-->
<external-path name="beta_external_files_path" path="Android/data/"/>
</paths>
注:1.3.1及以上版本,可以不用進行以上配置,aar已經在AndroidManifest配置了,並且包含了對應的資源文件。
七、混淆
# Bugly混淆規則
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
# 避免影響升級功能,需要keep住support包的類
-keep class android.support.**{*;}
好了,集成完畢,接下來就是製作基準包、補丁包和上傳補丁包了。
八、製作基準包
在app編碼完成並測試完成後,就是打包上線了,上線前打的包就是基準包啦,下面我們就來製作基準包,分3步:
- 打開app下的tinker-support.gradle文件。
- 將帶”base”的tinkerId註釋解開,並註釋掉帶”patch”的tinkerId。
- 雙擊運行build下的assembleRelease。
通常主Module的名字是”app”,但我這個Demo是”tinker-bugly”,所以你執行第3步時,要根據具體項目找到要製作基準包的主Module。
AS在執行assembleRelease指令時,就是在編譯基準包了,當編譯完成時,app的build目錄下會自動生成基準包文件夾,以時間戳來命名的(也就是說,每次執行assembleRelease指令都會在build目錄創建不同的基準包文件夾)。
這3個文件對之後製作補丁包來說是相當重要的,你需要做的就是將這3個文件保存好,可以保存到雲盤、Git服務器上等等,但就不要讓它就這麼放着,因爲在你執行clean Project時,app的build目錄會被刪除,這樣基準包及mapping與R文件都會丟失。
到這裏,你就可以把它(基準包:tinker-bugly-release.apk)上架到應用市場了。試下Demo:
tip:加固與多渠道打包
本篇不涉及具體的加固與多渠道打包。
1、加固
如果你的app需要加固,那就需要在製作基準包之前,將tinker-support.gradle文件的isProtectedApp = true的註釋去掉,然後加固,重新簽名,最後上架,它對加固平臺也有一定的要求。
詳情見「Bugly熱更新使用範例文檔最後:加固打包」部分。
2、多渠道打包
分「gradle配置productFlavors方式」與「多渠道打包工具打多渠道包方式(推薦)」。
詳情見「Bugly熱更新使用範例文檔:多渠道打包」部分。
九、補丁包
現在要動態修復App了,對於代碼修復、so庫修復、資源文件修復,分別對應Demo中的”say something”、”get string from .so”、”我的頭像”,修復過程無非是改代碼,替換so文件,替換資源文件,這裏就不演示了,直接開始製作補丁包,先將tinker-support.gradle文件打開。
1、基準包命名
確保基準包及相關文件的命名與配置文件中的一致:
2、修改baseApkDir與tinkerId
- 修改baseApkDir的值爲基準包所有文件夾的名字。
- 註釋掉帶”base”的tinkerId,取消帶”patch”的tinkerId的註釋(多次生成補丁時,記得修改”計數”,區分不同的補丁)。
3、執行編譯,生成補丁
打開側邊的Gradle標籤,找到項目的主Module,雙擊tinker-support下的buildTinkerPatchRelease指令,生成補丁包。
當編譯完成後,在app的build/outputs/patch目錄下會在”patch_singed_7zip.apk”文件,它就是補丁包,雙擊打開它,可以看到其中有一個YAPATCH.MF,裏面記錄了基準包與補丁包的tinkerId(兩者是肯定不同,如果一樣則說明配置有問題了)。
十、上傳補丁包
1、流程圖解
首先,點擊進入「Bugly產品頁面」,或點擊“我的產品 ”查看我的產品。
點擊你要管理的產品後,依次點擊”應用升級”、”熱更新”,可以查看到該產品的補丁下發情況(這個產品我還沒上傳過補丁,故一片空白)。
按下圖順序操作即可上傳補丁包:
2、上傳失敗分析
有可能你在上傳完補丁包時,頁面會提示”未匹配到可應用補丁包的App版本,請確認補丁包的基線版本是否已經發布”。
遇到這種情況請先冷靜,首先來說明一件事:Bugly怎麼知道基線版本是否已經發布?
通常按我們理解的,基準包發佈就是上架到應用市場,但應用市場又不會通知Bugly某某產品已經上架了,對吧。其實,Bugly的上架通知是這樣的:當基準包在手機上啓動時,Bugly框架就會讓App聯網通知Bugly的服務器,同時上傳當前App的版本號、tinkerId等信息,它這麼做的目的有如下兩個:
- 標記某個tinkerId的基準包已經被安裝到手機上使用了(即發佈)。
- 獲取該tinkerId的基準包最新的補丁信息。
所以,當出現了”未匹配到可應用補丁包的App版本,請確認補丁包的基線版本是否已經發布”這樣的提示時,可以確定,這個基準包的tinkerId等信息沒有被上傳到Bugly服務器,對此,鄙人將踩過的坑總結起來,摸索出了自己的解決方法,分如下幾步:
- 檢查App是否能夠聯網。
- 檢查App ID是否正確。
- 結合enableProxyApplication的取值,檢查AndroidManifest.xml中聲明的Application是否寫對。
- 檢查Bugly的安裝是不是在attachBaseContext()或onBaseContextAttached()方法中完成。
像我就犯過這樣的錯,明明在tinker-support.gradle文件中設置了enableProxyApplication = true,結果在AndroidManifest.xml中卻聲明瞭TinkerApplication的繼承類。
所以這裏只需要將AndroidManifest.xml中聲明我們自定義的Application即可(MyApplication)。
除了聯網問題以外,其他的幾種情況都需要重新生成基準包。這裏再分享一個可以快速確定App是否有上傳過版本信息的方法:
3、上傳成功
先驗證下上面的方法,當我把問題解決掉之後,把重新生成的基準包安裝到手機上打開(此時Bugly框架會上傳App的版本號、tinkerId到服務器),再查看”版本管理”,出現了,版本號爲”1.0”(其實就是App的versionName)。
再回頭來看看上傳補丁,這次又會有什麼不同呢?
耶,成功。點擊”立即下發”,可以看到現在補丁處於”下發中”狀態:
隨便來看看用戶手中的App是什麼反應吧(真正將補丁下發到用戶手機上的這段時間可能會有點久,不是立即下發的):
再回頭看看Bugly服務器上的補丁下發情況:
十一、其他
1、補丁管理
Bugly服務器除了可以上傳下發補丁外,還可以對補丁進行管理:
- 停止下發:不再把該補丁下發到客戶手機上(停止後可重新開啓)。
- 撤回:將Bugly服務器上的某個補丁刪掉,這個操作是不可逆的(不知道用戶手機上被成功打上的補丁是否也會被卸載)。
- 編輯:可以修改”下發範圍”(開發設備、全量設備、備註等等)。
- 歷史:查看修改記錄。
2、強調一下一些需要注意的地方
- 一個基準包可以有多個補丁包,Bugly會將最新的補丁進行下發(舊補丁默認會變成”停止下發狀態”),客戶手機上的App的舊補丁會被新補丁覆蓋。
- 製作基礎包時,請使用帶”base”的tinkerId,執行的是assembleRelease指令。
- 製作基礎包後,一定要將baseApk、mapping.txt、R.txt保存好,不能弄丟了。
- 製作補丁包時,先將baseApkDir的值修改爲基準包所有文件夾的名字,然後啓用帶”patch”的tinkerId,同時修改”計數”,執行的是buildTinkerPatchRelease指令。
- 製作補丁包後,最後打開它檢查YAPATCH.MF文件中的from和to信息,檢查該補丁包對應的基準包的tinkerId是否正確。
- 建議上線的基準包將Bugly的Log輸出關閉:Bugly.init(mContext, AppID, false);
- 如果是測試補丁包是否用效果,建議設置爲開發設備:Bugly.setIsDevelopmentDevice(mContext, true);
- so文件需要手動先調用一下 TinkerLoadLibrary.installNavitveLibraryABI(this, CPU_ABI) 方法才能生效。