熱更新--bugly整合及注意事項
本文主要是記錄了為什麼選擇bugly, bugly整合過程,使用過程中出現的問題,以及需要注意的事項。
熱更新就是動態下發程式碼,它可以使開發者在不釋出新版本的情況下,修復 BUG 和釋出功能的一個技術方案。
關於熱更新更詳細的解讀,可以轉到文末參考文章第一篇看看。
如何選擇熱更新方案?
當前市面的熱補丁方案有很多,其中比較出名的有阿里的AndFix、阿里Hotfix最新版 (Sophix)、美團的Robust,QZone的超級補丁方案和微信的Tinker。 以下是各個方案的對比:
總的來說:
1. AndFix作為native解決方案,首先面臨的是穩定性與相容性問題,更重要的是它無法實現類替換,它是需要大量額外的開發成本的;
2. Robust相容性與成功率較高,但是它與AndFix一樣,無法新增變數與類只能用做的bugFix方案;
3. Qzone方案可以做到釋出產品功能,但是它主要問題是插樁帶來Dalvik的效能問題,以及為了解決Art下記憶體地址問題而導致補丁包急速增大的。
綜合而言,阿里的Sophix和騰訊的Tinker是兩大熱門方案。
Tinker是騰訊開源的熱更新方案,不僅支援類、So以及資源的替換,它還是2.X-8.X(1.9.0以上支援8.X)的全平臺支援。他們的推薦理由:Tinker已執行在微信的數億Android裝置上,那麼為什麼你不使用Tinker呢?
Sophix是阿里推出的最新的熱更新方案,其產品基於阿里巴巴首創hotpatch技術,提供最細粒度熱修復能力,無需等待實時修復應用線上問題。推薦理由:傻瓜式接入,可以實現及時生效。
那我們該如何選擇呢?
單單從熱更新而言,Sophix可以實現補丁即時生效,不需要應用重啟;對應用無侵入,幾乎無效能損耗;傻瓜式接入。可以說是理想的選擇。筆者也嘗試整合Sophix,確實比較簡單。具體整合步驟,可以參考
筆者最終還是選擇了基於Tinker的bugly進行整合,原因如下:
1.筆者專案中採用了騰訊的樂加固方案,與Tinker同處一系。
2.Tinker方案直接可以通過Android Stuido的gradle生成響應的補丁包,Sophix需要有專門的補丁工具進行生成。
3.基於Tinker的Bugly上傳補丁包時會對於上線的基準包進行版本對比,不符合基準包版本的補丁包是不能夠上傳的(前提是及準備必須聯網上報,否者不能上傳補丁包)。
4.它是免費的!
基於以上原因,筆者最終選擇了Tinker熱更新方案,各位可以根據自己的實際情況進行選擇。
Tinker簡介
微信針對QQ空間超級補丁技術的不足提出了一個提供DEX差量包,整體替換DEX的方案。主要的原理是與QQ空間超級補丁技術基本相同,區別在於不再將patch.dex增加到elements陣列中,而是差量的方式給出patch.dex,然後將patch.dex與應用的classes.dex合併,然後整體替換掉舊的DEX,達到修復的目的。不足的地方:
1. Tinker不支援修改AndroidManifest.xml,Tinker不支援新增四大元件(1.9.0支援新增非export的Activity); 2. 由於Google Play的開發者條款限制,不建議在GP渠道動態更新程式碼; 3. 在Android N上,補丁對應用啟動時間有輕微的影響; 4. 不支援部分三星android-21機型,載入補丁時會主動丟擲"TinkerRuntimeException:checkDexInstall failed"; 5. 對於資源替換,不支援修改remoteView。例如transition動畫,notification icon以及桌面圖示。
Bugly介紹
Bugly目前採用微信Tinker的開源方案,開發者只需要整合我們提供的SDK就可以實現自動下載補丁包、合成、並應用補丁的功能,我們也提供了熱更新管理後臺讓開發者對每個版本補丁進行管理。 使用Bugly的幾大理由: 1.無需關注Tinker是如何合成補丁的 2.無需自己搭建補丁管理後臺 3.無需考慮後臺下發補丁策略的任何事情 4.無需考慮補丁下載合成的時機,處理後臺下發的策略 5.我們提供了更加方便整合Tinker的方式 6.我們通過HTTPS及簽名校驗等機制保障補丁下發的安全性 7.豐富的下發維度控制,有效控制補丁影響範圍 8.我們提供了應用升級一站式解決方案Bugly整合
Bugly整合參考Bugly官方文件。
注:以下部分均來自bugly官網。
第一步:新增外掛依賴
工程根目錄下“build.gradle”檔案中新增:buildscript {
repositories {
jcenter()
}
dependencies {
// tinkersupport外掛, 其中lastest.release指拉取最新版本,也可以指定明確版本號,例如1.0.4
classpath "com.tencent.bugly:tinker-support:1.1.5"
}
}
注意:自tinkersupport 1.0.3版本起無需再配tinker外掛的classpath。
版本對應關係:
tinker-support 1.1.5 對應 tinker 1.9.9
tinker-support 1.1.2 對應 tinker 1.9.6
tinker-support 1.1.1 對應 tinker 1.9.1
tinker-support 1.0.9 對應 tinker 1.9.0
tinker-support 1.0.8 對應 tinker 1.7.11
tinker-support 1.0.7 對應 tinker 1.7.9
tinker-support 1.0.4 對應 tinker 1.7.7
tinker-support 1.0.3 對應 tinker 1.7.6
tinker-support 1.0.2 對應 tinker 1.7.5(需配置tinker外掛的classpath)
第二步:整合SDK
gradle配置 在app module的“build.gradle”檔案中新增(示例配置):android {
defaultConfig {
ndk {
//設定支援的SO庫架構
abiFilters 'armeabi' //, 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
}
}
}
dependencies {
compile "com.android.support:multidex:1.0.1" // 多dex配置
//註釋掉原有bugly的倉庫
//compile 'com.tencent.bugly:crashreport:latest.release'//其中latest.release指代最新版本號,也可以指定明確的版本號,例如1.3.4
compile 'com.tencent.bugly:crashreport_upgrade:1.3.6'
// 指定tinker依賴版本(注:應用升級1.3.5版本起,不再內建tinker)
compile 'com.tencent.tinker:tinker-android-lib:1.9.9'
compile 'com.tencent.bugly:nativecrashreport:latest.release' //其中latest.release指代最新版本號,也可以指定明確的版本號,例如2.2.0
}
後續更新升級SDK時,只需變更配置指令碼中的版本號即可。
由於ApplicationLike已徹底與Application隔離,為了避免AndroidNClassLoader繼續將相關的類當成loader類而回滾到系統ClassLoader去載入,ApplicationLike、DefaultApplicationLike、ApplicationLifeCycle的包名也做了修改。升級到此版本後請將程式碼中對這三個類的全名引用中的包名從“com.tencent.tinker.loader.app.XXX”改成“com.tencent.tinker.entry.XXX"
注意: 升級SDK已經整合crash上報功能,已經整合Bugly的使用者需要註釋掉原來Bugly的jcenter庫; 已經配置過符號表的Bugly使用者保留原有符號表配置; Bugly SDK(2.1.5及以上版本)已經將Java Crash和Native Crash捕獲功能分開,如果想使用NDK庫,需要配置: compile 'com.tencent.bugly:nativecrashreport:latest.release'
筆者提示:此次官網又升級了SDK了,並且不再內建tinker了,這與前一個版本1.3.4是有區別的,需要多加註意。
在app module的“build.gradle”檔案中新增:
// 依賴外掛指令碼
apply from: 'tinker-support.gradle'
tinker-support.gradle內容如下所示(示例配置):
注:您需要在同級目錄下建立tinker-support.gradle這個檔案哦。
tinker-support.gradle的內容粘過去就行,需要注意的下面有說。
apply plugin: 'com.tencent.bugly.tinker-support'
def bakPath = file("${buildDir}/bakApk/")
/**
* 此處填寫每次構建生成的基準包目錄
*/
def baseApkDir = "app-0208-15-10-00"
/**
* 對於外掛各引數的詳細解析請參考
*/
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 = "base-1.0.1"
// 構建多渠道補丁時使用
// 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的分配
}
}
第三步:初始化SDK
分為enableProxyApplication = false 的情況和enableProxyApplication = true 的情況 筆者為了節約改造成本選擇了enableProxyApplication = true,即開啟反射模式。public class UserApplication extends MultiDexApplication {
@Override
public void onCreate() {
super.onCreate();
initBugly();
}
private void initBugly() {
setStrictMode();
// 設定是否開啟熱更新能力,預設為true
Beta.enableHotfix = true;
// 設定是否自動下載補丁
Beta.canAutoDownloadPatch = true;
// 設定是否提示使用者重啟
Beta.canNotifyUserRestart = AppConstant.SHOW_LOG;
// 設定是否自動合成補丁
Beta.canAutoPatch = true;
/**
* 補丁回撥介面,可以監聽補丁接收、下載、合成的回撥
*/
Beta.betaPatchListener = new BetaPatchListener() {
@Override
public void onPatchReceived(String patchFileUrl) {
if(AppConstant.SHOW_LOG) {
Toast.makeText(getApplicationContext(), patchFileUrl, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onDownloadReceived(long savedLength, long totalLength) {
if(AppConstant.SHOW_LOG) {
Toast.makeText(getApplicationContext(), String.format(Locale.getDefault(),
"%s %d%%",
Beta.strNotificationDownloading,
(int) (totalLength == 0 ? 0 : savedLength * 100 / totalLength)), Toast.LENGTH_SHORT).show();
}
}
@Override
public void onDownloadSuccess(String patchFilePath) {
if(AppConstant.SHOW_LOG) {
Toast.makeText(getApplicationContext(), patchFilePath, Toast.LENGTH_SHORT).show();
}
// Beta.applyDownloadedPatch();
}
@Override
public void onDownloadFailure(String msg) {
if(AppConstant.SHOW_LOG) {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onApplySuccess(String msg) {
if(AppConstant.SHOW_LOG) {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onApplyFailure(String msg) {
if(AppConstant.SHOW_LOG) {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onPatchRollback() {
if(AppConstant.SHOW_LOG) {
Toast.makeText(getApplicationContext(), "onPatchRollback", Toast.LENGTH_SHORT).show();
}
//需要新增下面的程式碼才能實現後臺的撤回操作
Beta.cleanTinkerPatch(true);
}
};
long start = System.currentTimeMillis();
// 設定開發裝置,預設為false,上傳補丁如果下發範圍指定為“開發裝置”,需要呼叫此介面來標識開發裝置
if(BuildConfig.IS_DEVELOP) {
Bugly.setIsDevelopmentDevice(getApplication(), true);
}
// 這裡實現SDK初始化,appId替換成你的在Bugly平臺申請的appId,除錯時將第三個引數設定為true
Bugly.init(this, BuildConfig.BUGLY_ID, AppConstant.SHOW_LOG);
long end = System.currentTimeMillis();
Log.e("init time--->", end - start + "ms");
}
@TargetApi(9)
protected void setStrictMode() {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
// 安裝tinker
Beta.installTinker();
}
}
**注意:補丁回撥介面Beta.betaPatchListener的回撥方法onPatchRollback(),這裡需要主動呼叫Beta.cleanTinkerPatch(true)才能實現補丁的回滾。
第四步:AndroidManifest.xml配置
在AndroidMainfest.xml中進行以下配置:- 許可權配置
<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" />
如果只是進行熱更新,不進行版本升級,下面的就不需要進行配置。
2. Activity配置
<activity
android:name="com.tencent.bugly.beta.ui.BetaActivity"
android:configChanges="keyboardHidden|orientation|screenSize|locale"
android:theme="@android:style/Theme.Translucent" />
- 配置FileProvider
注意:如果您想相容Android N或者以上的裝置,必須要在AndroidManifest.xml檔案中配置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>
這裡要注意一下,FileProvider類是在support-v4包中的,檢查你的工程是否引入該類庫。
在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>
這裡配置的兩個外部儲存路徑是升級SDK下載的檔案可能存在的路徑,一定要按照上面格式配置,不然可能會出現錯誤。
注:1.3.1及以上版本,可以不用進行以上配置,aar已經在AndroidManifest配置了,並且包含了對應的資原始檔。
第五步:混淆配置
為了避免混淆SDK,在Proguard混淆檔案中增加以下配置:
在此需要提一下的是tinker-support.gradle檔案。**
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
# tinker混淆規則
-dontwarn com.tencent.tinker.**
-keep class com.tencent.tinker.** { *; }
如果你使用了support-v4包,你還需要配置以下混淆規則:
-keep class android.support.**{*;}
以上就是整合bugly的主要步驟,同時也可以移步到官方提供的demo處看看:
Bugly Demo
打補丁包需要注意的事項
需要著重注意一下下方紅色方框內圈住的地方
總而言之:就是打基準包時,將tinkerId修改為與版本號相關的名稱,比如base-1.0.1;打補丁包時,baseApkDir路徑修改為之前打的基準包的報名,並且還要將tinkerId修改,比如patch-1.0.1。目的就是要將補丁包patch-1.0.1最終指向要修復的基準包base-1.0.1。
只要搞清楚上面這點就能夠很方便的打出相對應基準包的補丁包。
ps:上傳補丁包之前,一定要確保基準包已經聯網上報(只要一臺手機上報過就可以額)。
至於構建多渠道包,就要放開buildAllFlavorsDir = "${bakPath}/${baseApkDir}"
,官方也說了,對於渠道較少的可以採用,對於渠道較多的不建議如此採用,但是,目前Bugly只識別在app.gradle中的flavor構建的多渠道打包
// 多渠道打包(示例配置)
productFlavors {
xiaomi {
}
yyb {
}
}
如果你採用了其他多渠道打包框不行了,這點確實比較坑!
如何進行測試?
修復bug容易,關鍵是如何進行測試呢? 以下是個人做法,如果有更好的辦法,請留言告知,感激不盡。 1、申請兩個appkey,一個用於測試,一個用於產品釋出。 具體設定如下:buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField("String", "BUGLY_ID", '"aaaaaaaaaa"')//aaaaaaaaaa就是你申請的用於產品釋出的appkey
signingConfig signingConfigs.release
}
debug {
buildConfigField("String", "BUGLY_ID", '"bbbbbbbbbb"')//bbbbbbbbbb就是你申請的用於測試的appkey
signingConfig signingConfigs.release
}
}
然後在需要appkey的地方,直接如下用就可以了。
Bugly.init(this, BuildConfig.BUGLY_ID, AppConstant.SHOW_LOG);
2、多渠道包中設定一個渠道的裝置為開發裝置,這個渠道專門用於產品測試。
具體設定如下:
productFlavors {
baidu {
buildConfigField("boolean", "IS_DEVELOP", "false");
}
//用於產品測試
develop {
buildConfigField("boolean", "IS_DEVELOP", "true");
}
productFlavors.all { flavor ->
//UMENG_CHANNEL_VALUE即為AndroidManifest.xml中的具體值,此值代表統計渠道
flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
}
}
然後在bugly初始化的地方進行如下設定:
if(BuildConfig.IS_DEVELOP) {
Bugly.setIsDevelopmentDevice(TinkerManager.getApplication(), true);
}
這樣設定的話,我們就可以針對某個線上的基準包進行測試了。
如何對補丁進行撤回
bugly後臺在狀態與操作中有撤回操作,進行此操作的話,該補丁將不能進行任何編輯,而且已下發補丁的裝置將回退到基準包狀態。 但是,這個需要在前端,呼叫如下方法的Beta.cleanTinkerPatch(true);
在bugly給的補丁監聽的onPatchRollback方法中呼叫上面的程式碼
/**
* 補丁回撥介面,可以監聽補丁接收、下載、合成的回撥
*/
Beta.betaPatchListener = new BetaPatchListener() {
@Override
public void onPatchReceived(String patchFileUrl) {
if(AppConstant.SHOW_LOG) {
Toast.makeText(getApplicationContext(), patchFileUrl, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onDownloadReceived(long savedLength, long totalLength) {
if(AppConstant.SHOW_LOG) {
Toast.makeText(getApplicationContext(), String.format(Locale.getDefault(),
"%s %d%%",
Beta.strNotificationDownloading,
(int) (totalLength == 0 ? 0 : savedLength * 100 / totalLength)), Toast.LENGTH_SHORT).show();
}
}
@Override
public void onDownloadSuccess(String patchFilePath) {
if(AppConstant.SHOW_LOG) {
Toast.makeText(getApplicationContext(), patchFilePath, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onDownloadFailure(String msg) {
if(AppConstant.SHOW_LOG) {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onApplySuccess(String msg) {
if(AppConstant.SHOW_LOG) {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onApplyFailure(String msg) {
if(AppConstant.SHOW_LOG) {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onPatchRollback() {
if(AppConstant.SHOW_LOG) {
Toast.makeText(getApplicationContext(), "onPatchRollback", Toast.LENGTH_SHORT).show();
}
//需要新增下面的程式碼才能實現後臺的撤回操作
Beta.cleanTinkerPatch(true);
}
};
緊盯Tinker
因為bugly是對tinker的進一步封裝,所以當tinker有版本升級時,bugly也會進行相應的升級,但是從bugly官方的整合文件上不能及時反應,經過多次檢視,發現bugly的官方demo會進行響應的升級,有升級需要的可以多關注bugly官方demo.
踩坑經過
本來集成了bugly,想著能夠很高興的玩耍了。沒有想到,準備發第一個bugfix時,遭遇當頭一棒,在android4.4.2的手機上報下面的異常:Class ref in pre-verified class resolved to unexpected implementation
...
這是什麼鬼。。。
百度一下,有些文章說可能是分包的問題導致的,根據tinker官方給的出現這個問題的解決方法:如果出現Class ref in pre-verified class resolved to unexpected implementation異常, 請確認以下幾點:Application中傳入ApplicationLike的引數時是否採用字串而不是Class.getName方式;新的Application是否已經加入到dex loader pattern中; 額外新增到dex loader pattern中類的引用類也需要載入到loader pattern中。
難道是自動分包時出現了問題,然後找dex loader pattern,找分包方案,都沒有什麼效果,由此我開始懷疑難道是bugly好久沒有更新的原因嗎?
但是,最終結果不是,還是在tinker的github上面我找到了答案:(載入patch包,出現pre-verified crash)原來是我在tinker-support.gradle設定了啟用加固模式isProtectedApp = true,但是在測試時,沒有進行加固,直接拿加固前的包進行測試的。改後,果然如此。。。
出現了問題去怎麼解決?
因為bugly是對tinker的進一步封裝,如果不是本身封裝出問題的話,大家都應該去tinker的[github上的網站](https://github.com/Tencent/tinker)上去找找。
先在2標識的搜尋框中搜一搜有沒有類似的問題及解決方法,如果沒有的話,再問tinker的維護人員吧。
注意事項
以下節選部分需要注意的事項,具體請看Bugly Android 熱更新常見問題
Q: 是不是每次發版都要保留基準包、混淆配置檔案、資源Id檔案?
A:當然啦,你不儲存基準包,我們打補丁怎麼知道要基於哪個版本打補丁?所以建議大家每次發版注意儲存基準apk包,還有對應編譯生成的mapping檔案和R.txt檔案
Q:完整的測試流程是怎樣的?
A:
* 打基準包安裝並上報聯網(注:填寫唯一的tinkerId)
* 對基準包的bug修復(可以是Java程式碼變更,資源的變更)
* 修改基準包路徑、修改補丁包tinkerId、mapping檔案路徑(如果開啟了混淆需要配置)、resId檔案路徑
* 執行buildTinkerPatchRelease打Release版本補丁包
* 選擇app/build/outputs/patch目錄下的補丁包並上傳(注:不要選擇tinkerPatch目錄下的補丁包,不然上傳會有問題)
* 編輯下發補丁規則,點選立即下發
* 殺死程序並重啟基準包,請求補丁策略(SDK會自動下載補丁併合成)
* 再次重啟基準包,檢驗補丁應用結果
* 檢視頁面,檢視啟用資料的變化
Q: 日常除錯需要使用instant run,怎麼關閉tinker
A:這裡分兩種情況:
使用反射Application方式接入:可以直接在build.gradle中將apply from: 'tinker-support.gradle’註釋掉。
改造Application方式接入:先將tinkerSupport中overrideTinkerPatchConfiguration設定為false 修改成將tinkerSupport中enable設定為false。
Q:你們是怎麼定義開發裝置的?
A:我們會提供介面Bugly.setIsDevelopmentDevice(getApplicationContext(), true);,我們後臺就會將你當前裝置識別為開發裝置,如果設定為false則非開發裝置,我們會根據這個配置進行策略控制。