騰訊Tinker 熱修復 Andriod studio 3.0 配置和集成(三)Bugly集成

騰訊Tinker 熱修復 Andriod studio 3.0 Bugly集成和多渠道補丁管理髮布

本文說明
上一篇我說完了騰訊Tinker 熱修復之多渠道打包,這篇我們來初步瞭解下騰訊Tinker和Bugly結合來做熱修復多渠道補丁管理和集成。(其實在上週我已經集成測試完了demo也已經上傳了,就是突然想不到怎麼寫這篇,想了很久這篇不會寫得很多,因爲官方給出了視頻啊,視頻詳細得多了,我主要分享下我所在集成中碰到的問題)

開始

答疑

在公司做技術分享的時候,我老大提出了幾個問題,我當時沒有回答出來,因爲確實我沒看得很深入(比如源碼層,文件生成目的等)這裏我重新看了下官方文檔解析下

1.生成baseApk文件夾中的mapping.txt是什麼 有什麼用? R文件呢?
mapping.txt其實就是apk的混淆後的代碼文本 R文件也是一樣 防止反編譯

2.baseApk 每次都會生成怎麼管理?
baseApk對應的是上一次的oldApk,第一次是baseApk是爲空的,建議自己創文件保存,根據文件名日期去管理,而補丁包也是一樣。

3.TinkerSupport 插件中 tinkerPatch配置 有什麼用?
它其實就是全局信息相關的配置項,對應overrideTinkerPatchConfiguration 默認flase 不啓用使用默認的配置 開啓可以自定義配置。

介紹

熱更新能力是Bugly爲解決開發者緊急修復線上bug,而無需重新發版讓用戶無感知就能把問題修復的一項能力。Bugly目前採用微信Tinker的開源方案,開發者只需要集成我們提供的SDK就可以實現自動下載補丁包、合成、並應用補丁的功能,我們也提供了熱更新管理後臺讓開發者對每個版本補丁進行管理

爲什麼使用Bugly熱更新?

  • 無需關注Tinker是如何合成補丁的
  • 無需自己搭建補丁管理後臺
  • 無需考慮後臺下發補丁策略的任何事情
  • 無需考慮補丁下載合成的時機,處理後臺下發的策略
  • 提供了更加方便集成Tinker的方式
  • 通過HTTPS及簽名校驗等機制保障補丁下發的安全性
  • 豐富的下發維度控制,有效控制補丁影響範圍
  • 提供了應用升級一站式解決方案

視頻教程

2017年3月的有參考意義

視頻地址

本文demo

本文最新Bugly熱修復集成包括多渠道demo

Bugly配置集成

添加插件依賴

andriod studio 3.0 配置

工程根目錄下“build.gradle”文件中添加:
    classpath 'com.android.tools.build:gradle:3.0.1'


        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
        // tinkersupport插件, 其中lastest.release指拉取最新版本,也可以指定明確版本號,例如1.0.9
       // classpath "com.tencent.bugly:tinker-support:lastest.release"
        classpath "com.tencent.bugly:tinker-support:1.1.1"//tinker版本至1.9.1 對應bugly 1.1.1 看更新文檔
        // 多渠道插件(多渠道打包推薦使用)
        classpath 'com.meituan.android.walle:plugin:1.1.3'
    }

集成SDK

gradle配置


在app module的“build.gradle”文件中添加(示例配置):
apply plugin: 'com.android.application'

android {
    signingConfigs {
        debug {
            storeFile file('./keystore/debug.keystore')
        }

        release {
            keyAlias 'buglyrelease'
            keyPassword '123456'
            storeFile file('D:/Users/Achers/AsBuglyTinker/app/keystore/buglyrelease.jks')
            storePassword '123456'
        }
    }
    compileSdkVersion 26
    defaultConfig {
        applicationId "com.achers.asbuglytinker"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        // 開啓multidex
        multiDexEnabled true
    }
    // 編譯選項
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
    // recommend
    dexOptions {
        jumboMode = true
    }
    // 構建類型
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
        debug {
            debuggable true
            minifyEnabled false
            signingConfig signingConfigs.debug
        }
    }
    sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }
    repositories {
        flatDir {
            dirs 'libs'
        }
    }
    // 多渠道配置
    /*productFlavors {
         xiaomi {

         }
         yyb {

         }
     }*/
    lintOptions {
        checkReleaseBuilds false
        abortOnError false
    }




}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
    // 多dex配置
    implementation 'com.android.support:multidex:1.0.1'
    // 集成Bugly熱更新aar(本地集成使用方式)
    //      compile(name: 'bugly_crashreport_upgrade-1.3.2', ext: 'aar')
    // 遠程倉庫集成方式(推薦)
    implementation 'com.tencent.bugly:crashreport_upgrade:1.3.4'
    // walle(多渠道使用)
     compile 'com.meituan.android.walle:library:1.1.3'
}


// 依賴插件腳本
apply from: 'tinker-support.gradle'

// 多渠道使用walle示例(注:多渠道使用)
apply from: 'multiple-channel.gradle'

配置依賴插件腳本tinker-support.gradle

在app下新建tinker-support.gradle


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

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

/**
 * 此處填寫每次構建生成的基準包目錄
 */
def baseApkDir = "app-0114-18-17-58"

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

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

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

    autoGenerateTinkerId = false

    // 是否啓用覆蓋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 = "patch-1.0.2"

//    buildAllFlavorsDir = "${bakPath}/${baseApkDir}"
    // 是否開啓加固模式,默認爲false
    // isProtectedApp = true

    //默認爲false 就是需要自己改造application  爲true 就是不需要改造 通過反射
    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-patch"
       // applyMapping = "${bakPath}/${appName}/app-release-mapping.txt" //  可選,設置mapping文件,建議保持舊apk的proguard混淆方式
        //applyResourceMapping = "${bakPath}/${appName}/app-release-R.txt" // 可選,設置R.txt文件,通過舊apk文件保持ResId的分配
    }

}

TinkerSupport插件使用指南

參數介紹地址TinkerSupport插件使用指南

配置分包多渠道插件multiple-channel.gradle

在app下新建multiple-channel.gradle

apply plugin: 'walle'
walle {
    // 指定渠道包的輸出路徑
    apkOutputFolder = new File("${project.buildDir}/outputs/channels");
    // 定製渠道包的APK的文件名稱
    apkFileNameFormat = '${appName}-${packageName}-${channel}-${buildType}-v${versionName}-${versionCode}-${buildTime}.apk';
    // 渠道配置文件
    channelFile = new File("${project.getProjectDir()}/channel")
}

初始化SDK

Tinker推薦配置模式enableProxyApplication = false 的情況

自定義Application

/**
 * Create on 2018/1/14 12:23
 * <p>
 * author lhm
 * <p>
 * Description:  enableProxyApplication = false 的情況
 * 這是Tinker推薦的接入方式,一定程度上會增加接入成本,但具有更好的兼容性。
 * <p>
 * Version: 1.2.3
 *
 * 注意:這個類集成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
 */
public class APP extends TinkerApplication {
    public APP() {
        super(ShareConstants.TINKER_ENABLE_ALL, "com.achers.asbuglytinker.SampleApplicationLike",
                "com.tencent.tinker.loader.TinkerLoader", false);
    }

}

清單文件配置 application


    <application
        android:name=".APP"
        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">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

SampleApplicationLike 配置

package com.achers.asbuglytinker;

import android.annotation.TargetApi;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.support.multidex.MultiDex;
import android.widget.Toast;

import com.meituan.android.walle.WalleChannelReader;
import com.tencent.bugly.Bugly;
import com.tencent.bugly.beta.Beta;
import com.tencent.bugly.beta.interfaces.BetaPatchListener;
import com.tencent.tinker.loader.app.DefaultApplicationLike;

import java.util.Locale;

/**
 * Create on 2018/1/14 12:26
 * <p>
 * author lhm
 * <p>
 * Description:
 * <p>
 * Version: 1.2.3
 */
public class SampleApplicationLike  extends DefaultApplicationLike {

    public static final String TAG = "Tinker.SampleApplicationLike";

    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();
        // 設置是否開啓熱更新能力,默認爲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(getApplication(), "補丁下載地址" + patchFile, Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onDownloadReceived(long savedLength, long totalLength) {
                Toast.makeText(getApplication(),
                        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(getApplication(), "補丁下載成功", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onDownloadFailure(String msg) {
                Toast.makeText(getApplication(), "補丁下載失敗", Toast.LENGTH_SHORT).show();

            }

            @Override
            public void onApplySuccess(String msg) {
                Toast.makeText(getApplication(), "補丁應用成功", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onApplyFailure(String msg) {
                Toast.makeText(getApplication(), "補丁應用失敗", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onPatchRollback() {

            }
        };

        // 設置開發設備,默認爲false,上傳補丁如果下發範圍指定爲“開發設備”,需要調用此接口來標識開發設備
        Bugly.setIsDevelopmentDevice(getApplication(), true);
        // 多渠道需求塞入
         String channel = WalleChannelReader.getChannel(getApplication());
         Bugly.setAppChannel(getApplication(), channel);
        // 這裏實現SDK初始化,appId替換成你的在Bugly平臺申請的appId
        Bugly.init(getApplication(), "4694167734", 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);

        // TODO: 安裝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();
    }

}

Tinker推薦配置模式enableProxyApplication = true的情況

無須你改造Application,主要是爲了降低接入成本,我們插件會動態替換AndroidMinifest文件中的Application爲我們定義好用於反射真實Application的類(需要您接入SDK 1.2.2版本 和 插件版本 1.0.3以上)。
public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        // 這裏實現SDK初始化,appId替換成你的在Bugly平臺申請的appId
        // 調試時,將第三個參數改爲true
        Bugly.init(this, "900029763", false);
    }

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        // you must install multiDex whatever tinker is installed!
        MultiDex.install(base);


        // 安裝tinker
        Beta.installTinker();
    }

}

混淆配置

爲了避免混淆SDK,在Proguard混淆文件中增加以下配置:

-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}

如果你使用了support-v4包,你還需要配置以下混淆規則:


-keep class android.support.**{*;}

Bugly 集成後的使用

生成baseApk

 看過前兩篇關於Tinker 這種基本操作就很簡單了,差不多一毛一樣。

生成baseApk

查看生成目錄

baseApk目錄

生成tinker補丁包

這裏和tinker不同 tinker是執行  tinker包下的 而bugly是執行tinker-support

生成tinker補丁包

生成補丁包的同時會生成新的baseApk文件夾 這個就是在oldApk(有bug)基礎上更新代碼後執行tinker-support NewApk(修復版)上圖

包

Bugly平臺使用

Bugly平臺

上傳補丁

選擇項目文件下的7zip   注意不是thinkePatch下的apk 而是 patch下的

這裏寫圖片描述

上傳補丁包遇到的問題

這裏寫圖片描述

原因是因爲我們的baseApk雖然集成了bugly 但是沒有在運行就是沒有聯網與bugly對接 平臺沒有記錄,所以找不到。解決辦法 運行下baseApk 就可以。

關於碰到的問題

tinkerid的設置

這裏寫圖片描述

這裏官方是默認爲true的 它會自己生成tinkerid 格式是versionName+versionCode+生成時間,他的意思很明顯就是你不配置tinkerid 就是按這個格式來配置,你就不用自己每次改一變tinkerid 但是非常難看 
比如 1.01TinkerId2018-14-11-20;看起來很難管理
因爲Bugly與tinker不同的地方在與他會在補丁包中增加一個文件YAPATCH.MF用來記錄補丁

這裏寫圖片描述

這是我將autoGenerateTinkerId=false 後 每次手動設置的tinkerid
tinkerid 規則 基礎包爲 base-1.0.1 對應補丁包 patch-1.0.1
            基礎包爲 base-1.0.2 對應補丁包 patch-1.0.2
            就是爲了自己定義方便管理。

Bugly Android 熱更新常見問題

Q:之前使用Tinker怎麼切換過來使用Bugly?

A:Bugly使用源碼集成Tinker,如果之前集成過Tinker,你需要註釋掉以下配置:

//    compile("com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}") { changing = true }
//    provided("com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}") { changing = true } 

插件配置不需要更改,只需要加上我們Bugly額外的tinker-support插件即可:

 classpath "com.tencent.bugly:tinker-support:latest.release"

Q: 基線版本表示什麼意思?

A:表示你需要修復apk的版本,比如你已經上線了某個版本的apk,你需要用一個唯一的tinkerId來標識這個版本,而補丁包也是基於這個版本打的。

Q:打一個補丁包需要改哪些東西?

A:

修復bug的類、修改資源
修改oldApk配置
修改tinkerId
Q:tinkerId該怎麼填?

A:在運行過程中,我們需要驗證基準apk包的tinkerId是否等於補丁包的tinkerId。這個是決定補丁包能運行在哪些基準包上面,一般來說我們可以使用git版本號、versionName等等。

Q:兩次傳入的tinkerId是否一樣?

A:不一樣的,編譯補丁包時,tinker會自動讀取基準包AndroidManifest的tinkerId作爲package_meta.txt中的TINKER_ID。將本次編譯傳入的tinkerId,作爲package_meta.txt的NEW_TINKER_ID。

Q. 爲什麼我上傳補丁提示我“未匹配到可用補丁的App版本”?

A:如果你的基線版本沒有上報過聯網,基於這個版本生成的補丁包就無法匹配到,請檢查你的基線版本配置是否正確。(具體參考:啓動apk,上報聯網數據)

Q. 我該上傳哪個補丁?patch目錄跟tinkerPatch目錄下的補丁有什麼區別嗎?

A:你必須要上傳build/outputs/patch目錄下的補丁包,

Q:我以前的是Bugly SDK,現在集成升級SDK會有什麼影響?

A:不會有影響的,升級SDK內置Bugly功能模塊,你只需要將初始化方法改爲統一的Bugly.init(getApplicationContext(), “註冊時申請的APPID”, false);即可。

Q:你們是怎麼定義開發設備的?

A:我們會提供接口Bugly.setIsDevelopmentDevice(getApplicationContext(), true);,我們後臺就會將你當前設備識別爲開發設備,如果設置爲false則非開發設備,我們會根據這個配置進行策略控制。

Q:如果我配置了升級策略,又配置了補丁策略,會是怎樣的效果?

A:升級策略優先級會高於補丁策略,後臺會優先下發升級策略。畢竟你都要升級了,熱更新只是幫助你修復bug而已。

Q:我只想使用熱更新,不想使用升級?

A:熱更新是包含在升級SDK裏面的,你可以不配置任何升級策略,只需按照熱更新文檔集成即可。

Q:是否支持加固模式?

A:

Bugly 1.3.0及以上版本支持(tinker 1.7.9)

需要你在tinker-support配置中設置isProtectedApp = true。

更多

熱更新常見問題

Bugly多渠道熱更新解決方案

bugly多渠道就跟tinker一毛一樣了,這裏貼這些沒什麼意義大家請看官方文章

Bugly多渠道熱更新解決方案

往期文章

騰訊Tinker 熱修復 Andriod studio 3.0 配置和集成

騰訊Tinker 熱修復 Andriod studio 3.0 多渠道打包和發佈補丁方式推薦

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章