Android 微信Tinker 熱修復(fù) 踩坑記錄

簡(jiǎn)述

熱更新能力是Bugly為解決開(kāi)發(fā)者緊急修復(fù)線(xiàn)上bug盆犁,無(wú)需重新發(fā)版讓用戶(hù)無(wú)感知就能把問(wèn)題修復(fù)的一項(xiàng)能力。 通過(guò)管理后臺(tái)發(fā)布補(bǔ)丁,進(jìn)行的修復(fù)爽冕。
這里先給出Bugly Android熱更新使用指南 文檔,能力強(qiáng)的小伙伴們可以自己去過(guò)一遍批狐。

老規(guī)矩先上圖(感覺(jué)圖片沒(méi)有很好體現(xiàn)熱修復(fù)的效果)

未修復(fù)前扇售,點(diǎn)擊提示按鈕提示 1+1=3 的錯(cuò)誤提示


未修復(fù)前.png

發(fā)布補(bǔ)丁后 ,點(diǎn)擊提示: 修復(fù) 1+1=2


熱修復(fù)啟動(dòng)后.png

采用的方案原理

微信Tinker熱修復(fù)采用的是類(lèi)加載方案嚣艇,重啟App后讓ClassLoader重新加載新的類(lèi) 承冰,故無(wú)法實(shí)時(shí)生效,得需要用戶(hù)殺掉進(jìn)程關(guān)掉APP后食零,重新啟動(dòng)進(jìn)來(lái)才有效果困乒。 由于已經(jīng)有大佬對(duì)相關(guān)幾種修復(fù)原理進(jìn)行分析,咱們可以站在巨人肩膀上學(xué)習(xí)學(xué)習(xí):這里直接貼劉望舒大佬傳送門(mén)地址

開(kāi)啟代碼之旅

開(kāi)始之前先說(shuō)說(shuō)我的開(kāi)發(fā)版本:

  • gradle 版本 我測(cè)試過(guò)的3.3.0 和3.4.0 可以正常接入使用贰谣,但是接入3.6.1 就會(huì)出現(xiàn)提示
Gradle sync failed: No signature of method: org.gradle.api.internal.file.DefaultFilePropertyFactory$DefaultDirectoryVar.getFiles() is applicable for argument types: () values: []

此類(lèi)錯(cuò)誤本人暫無(wú)能力解決娜搂,有解決方案的小伙伴可以提出來(lái),大家互相學(xué)習(xí)學(xué)習(xí)吱抚。
我這邊是開(kāi)啟混淆百宇,并且設(shè)置了混淆規(guī)則 接入方式用enableProxyApplication = false 的情況,這個(gè)是關(guān)閉映射官方推薦的方式(為true的成本低秘豹,文檔也容易携御,就不重復(fù)寫(xiě))
Bugly平臺(tái)小伙伴們自己注冊(cè)和創(chuàng)建,然后取創(chuàng)建項(xiàng)目的appid,這幾個(gè)步驟相信不用貼出來(lái)啄刹,小伙伴們也能搗鼓清楚涮坐。

先按照開(kāi)發(fā)文檔,把我們需要的依賴(lài)的SDK配置進(jìn)來(lái)

  • 首先是項(xiàng)目下的gradle


    根目錄下的gradle配置.png

    在dependencies 中設(shè)置插件依賴(lài)

 // tinkersupport插件, 其中l(wèi)astest.release指拉取最新版本誓军,也可以指定明確版本號(hào)袱讹,例如1.0.4
  classpath "com.tencent.bugly:tinker-support:1.1.5"
  • 在app 目錄下的gradle 中集成SDK
    app module的build.gradle.png
    implementation "com.android.support:multidex:1.0.1" // 多dex配置
    //注釋掉原有bugly的倉(cāng)庫(kù)
    //compile 'com.tencent.bugly:crashreport:latest.release'//其中l(wèi)atest.release指代最新版本號(hào),也可以指定明確的版本號(hào)昵时,例如1.3.4
    implementation 'com.tencent.bugly:crashreport_upgrade:1.3.6'
    // 指定tinker依賴(lài)版本(注:應(yīng)用升級(jí)1.3.5版本起捷雕,不再內(nèi)置tinker)
    implementation 'com.tencent.tinker:tinker-android-lib:1.9.9'
    implementation 'com.tencent.bugly:nativecrashreport:latest.release'
    //其中l(wèi)atest.release指代最新版本號(hào),也可以指定明確的版本號(hào)债查,例如2.2.0
  • 同步完成之前順便把生成簽名也設(shè)置了混淆(這邊為了方便調(diào)試非区,把release 與 debug 同時(shí)配置為使用密鑰,同時(shí)開(kāi)啟混淆 盹廷,單純只是為了寫(xiě)個(gè)測(cè)試的可以minifyEnabled 設(shè)置為false)
    fix.jks密鑰目錄位置:


    fix.jks.png

完整gradler如下:

apply plugin: 'com.android.application'
// 依賴(lài)插件腳本
apply from: 'tinker-support.gradle'
android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"
    signingConfigs {
        config {
            keyAlias 'guo'
            keyPassword 'g123456'
            storeFile file("fix.jks")
            storePassword 'g123456'
        }
    }
    defaultConfig {
        applicationId "com.mutou.fixapplication"
        minSdkVersion 16
        targetSdkVersion 29
        versionCode 1
        versionName "1.0.1"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        ndk {
            //設(shè)置支持的SO庫(kù)架構(gòu)
            abiFilters 'armeabi', 'x86'//, 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
        }
    }

    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.config
        }
        debug {
            minifyEnabled true

            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            //signingConfig signingConfigs.config
            signingConfig signingConfigs.config
        }

    }

}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

    implementation "com.android.support:multidex:1.0.1" // 多dex配置
    //注釋掉原有bugly的倉(cāng)庫(kù)
    //compile 'com.tencent.bugly:crashreport:latest.release'//其中l(wèi)atest.release指代最新版本號(hào)征绸,也可以指定明確的版本號(hào),例如1.3.4
    implementation 'com.tencent.bugly:crashreport_upgrade:1.3.6'
    // 指定tinker依賴(lài)版本(注:應(yīng)用升級(jí)1.3.5版本起俄占,不再內(nèi)置tinker)
    implementation 'com.tencent.tinker:tinker-android-lib:1.9.9'
    implementation 'com.tencent.bugly:nativecrashreport:latest.release'
    //其中l(wèi)atest.release指代最新版本號(hào)管怠,也可以指定明確的版本號(hào),例如2.2.0
}

  • 建議把SDK集成完成后再配置插件腳本
 apply from: 'tinker-support.gradle'

在app目錄下新建文件 tinker-support.gradle


目錄截圖.png

我這邊是已經(jīng)存在了故報(bào)了這個(gè)錯(cuò)誤缸榄,你們點(diǎn)擊 ok 即可生成該文件


新建文件截圖.png

該插件配置直接照著文檔copy即可,相關(guān)參數(shù)含義可以參見(jiàn)文檔渤弛。
配置如下

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

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

/**
 * 此處填寫(xiě)每次構(gòu)建生成的基準(zhǔn)包目錄
 */
def baseApkDir = "app-0916-16-41-06"

/**
 * 對(duì)于插件各參數(shù)的詳細(xì)解析請(qǐng)參考
 */
tinkerSupport {

    // 開(kāi)啟tinker-support插件,默認(rèn)值true
    enable = true

    // 指定歸檔目錄甚带,默認(rèn)值當(dāng)前module的子目錄tinker
    autoBackupApkDir = "${bakPath}"

    // 是否啟用覆蓋tinkerPatch配置功能她肯,默認(rèn)值false
    // 開(kāi)啟后tinkerPatch配置不生效,即無(wú)需添加tinkerPatch
    overrideTinkerPatchConfiguration = true

    // 編譯補(bǔ)丁包時(shí)鹰贵,必需指定基線(xiàn)版本的apk晴氨,默認(rèn)值為空
    // 如果為空,則表示不是進(jìn)行補(bǔ)丁包的編譯
    // @{link tinkerPatch.oldApk }
    baseApk = "${bakPath}/${baseApkDir}/app-release.apk"

    // 對(duì)應(yīng)tinker插件applyMapping
    baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-release-mapping.txt"

    // 對(duì)應(yīng)tinker插件applyResourceMapping
    baseApkResourceMapping = "${bakPath}/${baseApkDir}/app-release-R.txt"

    // 構(gòu)建基準(zhǔn)包和補(bǔ)丁包都要指定不同的tinkerId碉输,并且必須保證唯一性 base-1.0.1 對(duì)應(yīng) patch-1.0.1
    tinkerId = "patch-1.0.1"

    // 構(gòu)建多渠道補(bǔ)丁時(shí)使用
    // buildAllFlavorsDir = "${bakPath}/${baseApkDir}"

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

    // 是否開(kāi)啟反射Application模式
    enableProxyApplication = false

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

}

/**
 * 一般來(lái)說(shuō),我們無(wú)需對(duì)下面的參數(shù)做任何的修改
 * 對(duì)于各參數(shù)的詳細(xì)介紹請(qǐng)參考:
 * 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" //  可選,設(shè)置mapping文件敷钾,建議保持舊apk的proguard混淆方式
        //applyResourceMapping = "${bakPath}/${appName}/app-release-R.txt" // 可選枝哄,設(shè)置R.txt文件,通過(guò)舊apk文件保持ResId的分配
    }
}
  • 再來(lái)配置我們的混淆規(guī)則 app目錄下的 proguard-rules.pro (gradle minifyEnabled 設(shè)置為false的可以跳過(guò) )
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
# tinker混淆規(guī)則
-dontwarn com.tencent.tinker.**
-keep class com.tencent.tinker.** { *; }
-keep class android.support.**{*;}

# 繼承了DefaultApplicationLike 的類(lèi) 記得保持不被混淆阻荒,否則找不到該類(lèi)直接閃退
-keep public class com.mutou.fixapplication.AppContext{*;}


-optimizationpasses 5

# 混合時(shí)不使用大小寫(xiě)混合挠锥,混合后的類(lèi)名為小寫(xiě)
-dontusemixedcaseclassnames
# 指定不去忽略非公共庫(kù)的類(lèi)
-dontskipnonpubliclibraryclasses
# 這句話(huà)能夠使我們的項(xiàng)目混淆后產(chǎn)生映射文件
# 包含有類(lèi)名->混淆后類(lèi)名的映射關(guān)系
-verbose
# 指定不去忽略非公共庫(kù)的類(lèi)成員
-dontskipnonpubliclibraryclassmembers
# 不做預(yù)校驗(yàn),preverify是proguard的四個(gè)步驟之一侨赡,Android不需要preverify瘪贱,去掉這一步能夠加快混淆速度纱控。
-dontpreverify
# 保留Annotation不混淆
-keepattributes *Annotation*,InnerClasses
# 避免混淆泛型
-keepattributes Signature
# 拋出異常時(shí)保留代碼行號(hào)
-keepattributes SourceFile,LineNumberTable
# 指定混淆是采用的算法,后面的參數(shù)是一個(gè)過(guò)濾器
# 這個(gè)過(guò)濾器是谷歌推薦的算法菜秦,一般不做更改
-optimizations !code/simplification/cast,!field/*,!class/merging/*
#############################################
#
# Android開(kāi)發(fā)中一些需要保留的公共部分
#
#############################################
# 保留我們使用的四大組件,自定義的Application等等這些類(lèi)不被混淆
# 因?yàn)檫@些子類(lèi)都有可能被外部調(diào)用
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Appliction
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View
-keep public class com.android.vending.licensing.ILicensingService

# 保留R下面的資源
-keep class **.R$* {*;}

# 保留本地native方法不被混淆
-keepclasseswithmembernames class * {
    native <methods>;
}

# 保留在Activity中的方法參數(shù)是view的方法舶掖,
-keepclassmembers class * extends android.app.Activity{
    public void *(android.view.View);
}

# 保留枚舉類(lèi)不被混淆
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

# support
-keep public class * extends android.support.v4.**
-keep public class * extends android.support.v7.**
-keep public class * extends android.support.annotation.**

# androidx的混淆
-keep class com.google.android.material.** {*;}
-keep class androidx.** {*;}
-keep public class * extends androidx.**
-keep interface androidx.** {*;}
-dontwarn com.google.android.material.**
-dontnote com.google.android.material.**
-dontwarn androidx.**

# 保留我們自定義控件(繼承自View)不被混淆
-keep public class * extends android.view.View{
    *** get*();
    void set*(***);
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
}

# 保留Parcelable序列化類(lèi)不被混淆
-keep class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator *;
}

# 保留Serializable序列化的類(lèi)不被混淆
-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    !static !transient <fields>;
    !private <fields>;
    !private <methods>;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

# 對(duì)于帶有回調(diào)函數(shù)的onXXEvent球昨、**On*Listener的,不能被混淆
-keepclassmembers class * {
    void *(**On*Event);
    void *(**On*Listener);
}

# webview 還要注意native接口
-keepclassmembers class * extends android.webkit.WebViewClient {
    public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
    public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.WebViewClient {
    public void *(android.webkit.webView, jav.lang.String);
}

# keep annotated by NotProguard
-keep @top.andnux.proguard.annotation.Keep class * {*;}
-keep class * {
    @top.andnux.proguard.annotation.Keep <fields>;
}
-keepclassmembers class * {
    @top.andnux.proguard.annotation.Keep <methods>;
}

# 刪除代碼中Log相關(guān)的代碼
-assumenosideeffects class android.util.Log {
    public static boolean isLoggable(java.lang.String, int);
    public static int v(...);
    public static int i(...);
    public static int w(...);
    public static int d(...);
    public static int e(...);
}


其中-keep public class com.mutou.fixapplication.AppContext{*;}
這個(gè)看各位小伙伴自己建立的項(xiàng)目包名和類(lèi)名眨攘,這個(gè)繼承了DefaultApplicationLike 的類(lèi) 記得保持不被混淆主慰,不然Application代理類(lèi)找不到路徑就閃退了。

  • 自定義我們的CustomTinkerApplication 來(lái)繼承TinkerApplication 鲫售,在構(gòu)造函數(shù)里直接調(diào)用父類(lèi)TinkerApplication 帶四個(gè)參數(shù)的構(gòu)造函數(shù)(具體代表含義參考文檔)
public class CustomTinkerApplication extends TinkerApplication {
    public CustomTinkerApplication() {
        super(ShareConstants.TINKER_ENABLE_ALL,
                "com.mutou.fixapplication.AppContext",
                "com.tencent.tinker.loader.TinkerLoader",
                false);
    }
}

其中第二個(gè)參數(shù) "com.mutou.fixapplication.AppContext" 是我們自定義的Application代理類(lèi)共螺,注意路徑正確和寫(xiě)全,這是我包的目錄結(jié)構(gòu)情竹,截圖僅供參考藐不,小伙伴們更改為自己項(xiàng)目的正確環(huán)境


目錄結(jié)構(gòu).png
  • 將該類(lèi)配置到清單manifest中


    AndroidManifest.png
  • 自定義AppContext并且實(shí)現(xiàn)SDK初始化

public class AppContext extends DefaultApplicationLike {
    public AppContext(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();
        // 這里實(shí)現(xiàn)SDK初始化,appId替換成你的在Bugly平臺(tái)申請(qǐng)的appId
        // 調(diào)試時(shí)秦效,將第三個(gè)參數(shù)改為true
        Bugly.init(getApplication(), "7eda4c07f3", 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);
    }
}

其中 Bugly 第二個(gè)參數(shù)是我在平臺(tái)上申請(qǐng)的項(xiàng)目的AppId ,第三個(gè)參數(shù)設(shè)置為開(kāi)啟debug 雏蛮,其他小伙伴們?cè)谡江h(huán)境可以關(guān)掉. 我們其他集成的SDK的初始化也在這里面實(shí)現(xiàn)。

  • AndroidManifest 進(jìn)行權(quán)限配置
<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" />

其中里面有些需要?jiǎng)討B(tài)申請(qǐng)權(quán)限阱州,小伙伴們注意下挑秉,我這邊為求方便,直接用5.0的手機(jī)測(cè)試苔货,若是高于6.0測(cè)試時(shí)候最好自行打開(kāi)所有權(quán)限犀概。

  • Activity配置
    在application 標(biāo)簽內(nèi)的,注意位置(四大組件的注冊(cè))
        <activity
            android:name="com.tencent.bugly.beta.ui.BetaActivity"
            android:configChanges="keyboardHidden|orientation|screenSize|locale"
            android:theme="@android:style/Theme.Translucent" />
  • 配置FileProvider夜惭,這邊建議繼承FileProvider類(lèi)來(lái)解決合并沖突姻灶,一般第三方拍照裁剪的庫(kù),會(huì)解決7.0 上遇到的Uri 問(wèn)題滥嘴,如果依賴(lài)了就會(huì)和這邊的設(shè)置起沖突木蹬,我們一口氣走一遍完整的解決沖突問(wèn)題(單純想測(cè)試的可直接按照文檔來(lái))
    新建類(lèi)UriProvider 并且繼承FileProvider
public class UriProvider extends FileProvider {
}

在res目錄新建xml文件夾,創(chuàng)建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>

截圖如下:


provider_paths.png

在清單上配置

        <provider
            android:name=".providers.UriProvider"
            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>

name 是我們創(chuàng)建該類(lèi)的路徑與自身類(lèi)名若皱,小伙伴們自行改更

到這里寫(xiě)完了我們SDK的接入了镊叁,接下來(lái)這邊簡(jiǎn)單使用下補(bǔ)丁發(fā)布,先來(lái)生成我們的基準(zhǔn)包走触,里面寫(xiě)個(gè)錯(cuò)誤的提示,代碼和布局過(guò)于簡(jiǎn)單晦譬,只貼下活動(dòng)頁(yè)代碼了


MainActivity.png
  • 生成基準(zhǔn)包
    在tinker-support中,基準(zhǔn)包的 tinkerId 我這邊設(shè)置為 base-1.0.1 (后面不要命名一模一樣的其他基準(zhǔn)包互广,以免沖突)


    image.png

然后在AS右上角 的Gradle 中找到并且執(zhí)行assembleRelease編譯生成基準(zhǔn)包


Gradle .png
assembleRelease.png

若是沒(méi)有找到的敛腌,可以在other目錄下找找卧土,我的便是在里面找到的


other目錄.png

雙擊運(yùn)行之后,會(huì)生成編譯的基準(zhǔn)包像樊、混淆配置文件(開(kāi)啟混淆才有)尤莺、資源Id,這份文件建議保存起來(lái)生棍。


bakApk.png

這個(gè)就是我們要用于生產(chǎn)環(huán)境的包(現(xiàn)在里面的有錯(cuò)誤提示的代碼)颤霎,然后找臺(tái)手機(jī)安裝打開(kāi),之后會(huì)聯(lián)網(wǎng)上報(bào)(確保網(wǎng)絡(luò)正常)涂滴,在我們的Bugly平臺(tái)中能看到統(tǒng)計(jì)

  • 生成補(bǔ)丁包
    還是在我們的tinker-support中友酱,這次要修改的有兩處:
  1. baseApkDir ,將其設(shè)置為我們生成的基準(zhǔn)包目錄的文件名(生成的是時(shí)間戳)柔纵,我這邊的是app-0916-16-41-06


    baseApkDir .png
  1. 將tinkerId 設(shè)置為 patch-1.0.1


    tinkerId .png

然后在AS右上角那地方找到tinker-suppor插件目錄下的buildTinkerPatchRelease 雙擊運(yùn)行


tinker-suppor插件目錄.png

生成的補(bǔ)丁包在app\build\outputs\apk\tinkerPatch\release目錄下(我這邊AS顯示不出缔杉,但是按照路徑上便能找到),小伙伴們可以直接找到的搁料。


image.png
  • 將patch_signed_7zip補(bǔ)丁包上傳
    打開(kāi)我們的管理平臺(tái)或详,選中我們創(chuàng)建的項(xiàng)目


    發(fā)布補(bǔ)丁.png
補(bǔ)丁信息補(bǔ)充.png

管理平臺(tái)可以看到 1/全量設(shè)備, 說(shuō)明已經(jīng)激活一臺(tái)加缘,熱修復(fù)成功了鸭叙。
整個(gè)流程就到此為止。希望能對(duì)小伙伴們有啟發(fā)拣宏,不足之處還望指出沈贝。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市勋乾,隨后出現(xiàn)的幾起案子宋下,更是在濱河造成了極大的恐慌,老刑警劉巖辑莫,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件学歧,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡各吨,警方通過(guò)查閱死者的電腦和手機(jī)枝笨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)揭蜒,“玉大人横浑,你說(shuō)我怎么就攤上這事√敫” “怎么了徙融?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)瑰谜。 經(jīng)常有香客問(wèn)我欺冀,道長(zhǎng)树绩,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任隐轩,我火速辦了婚禮饺饭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘职车。我一直安慰自己砰奕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布提鸟。 她就那樣靜靜地躺著,像睡著了一般仅淑。 火紅的嫁衣襯著肌膚如雪称勋。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天涯竟,我揣著相機(jī)與錄音赡鲜,去河邊找鬼。 笑死庐船,一個(gè)胖子當(dāng)著我的面吹牛银酬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播筐钟,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼揩瞪,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了篓冲?” 一聲冷哼從身側(cè)響起李破,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎壹将,沒(méi)想到半個(gè)月后嗤攻,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡诽俯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年妇菱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片暴区。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡闯团,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出颜启,到底是詐尸還是另有隱情偷俭,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布缰盏,位于F島的核電站涌萤,受9級(jí)特大地震影響淹遵,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜负溪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一透揣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧川抡,春花似錦辐真、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至密幔,卻和暖如春楔脯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背胯甩。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工昧廷, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人偎箫。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓木柬,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親淹办。 傳聞我的和親對(duì)象是個(gè)殘疾皇子眉枕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354