1. 程式人生 > >熱更新--bugly整合及注意事項

熱更新--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中進行以下配置:
  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" />

如果只是進行熱更新,不進行版本升級,下面的就不需要進行配置。
2. Activity配置

<activity
	android:name="com.tencent.bugly.beta.ui.BetaActivity"
	android:configChanges="keyboardHidden|orientation|screenSize|locale"
	android:theme="@android:style/Theme.Translucent" />

  1. 配置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則非開發裝置,我們會根據這個配置進行策略控制。

官方demo

參考文章