Android-Tinker&Bugly热修复的集成(2021版本)

项目基于Androidx,主语言是Kotlin,Android studio 4.0.2版本

本文目标

集成bugly的tinker热修复能力到项目中

gradle的wrapper

用的gradle-5.6.4-all.zip

#Fri Sep 11 14:53:57 CST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip

项目根目录build.gradle

  dependencies {
        //gradle需要从原来的3.5.0降级到3.4.2  bugly插件还没适配
        classpath 'com.android.tools.build:gradle:3.4.2'
        classpath "com.tencent.bugly:tinker-support:1.1.5"
   }

tinker-support.gradle

在根目录创建tinker-support.gradle文件,内容如下

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

//配置打包生成的apk的路径,定位在APP模块下的build/bakApk目录下
//形式如/user/**/desktop/com/.../main/app/bakApk
//基准包路径:app/build/bakApk/app-0709-14-54-16/app-release.apk
def bakPath = file("${project(":app").buildDir}/bakApk/")

//此处填写需要构建patch的发布包所在目录
//因为每次打包都会在上面的bakApk目录下生成一个以当前打包时间命名的文件件,文件件中存放着本次打包生成的apk,mapping.txt,R.txt.
def baseApkDir = 'app-0226-13-59-13'

//https://bugly.qq.com/docs/utility-tools/plugin-gradle-hotfix/
tinkerSupport {

    // 开启tinker-support插件,默认值true
    enable = true

    // 是否启用覆盖tinkerPatch配置功能,默认值false
    // tinkerSupport是bugly提供的配置属性
    // 如果为true ,下面tinkerPatch中配置的属性不会生效,推荐这种。tinker的配置太繁琐复杂
    overrideTinkerPatchConfiguration = true

    // 生成patch.dex目录,默认值当前module的子目录tinker
    //补丁包路径: app/build/outputs/patch/release/patch_signed.apk
    autoBackupApkDir = "${bakPath}"

    // 编译补丁包时,必需指定基线版本的apk,也就是针对哪个线上版本生成patch
    baseApk = "${bakPath}/${baseApkDir}/app-release.apk"

    // 对应tinker插件applyMapping 混淆规则
    baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-release-mapping.txt"

    // 对应tinker插件applyResourceMapping 资源id映射
    baseApkResourceMapping = "${bakPath}/${baseApkDir}/app-release-R.txt"

    //自动生成下面的tinker的值 时间戳形式0707-12-10-10
    autoGenerateTinkerId = true

    // 构建基准包和补丁包都要指定不同的tinkerId,并且必须保证唯一性
    tinkerId = "if autoGenerateTinkerId=true,no need set here"

    // 是否开启反射Application模式
    enableProxyApplication = true

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

    //支持加固,360加固是ok的
    isProtectedApp = 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 = true
    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的分配
    }
}

App根目录build.gradle

在主项目APP添加依赖配置签名和拆分包,应用tinker打包配置gradle文件
关于signingConfigs的配置,如果有不明白的小伙伴可以参考下Android-SigningConfigs打包配置

apply from: '../tinker-support.gradle'//应用上面创建的tinker-support.gradle文件
android {
    signingConfigs {
        config {
            storeFile file('../as_key_store')
            storePassword '123456'
            keyAlias 'mall'
            keyPassword '123456'
        }
    }

    defaultConfig {
        applicationId "com.i.proj.main"
        //开启拆分包
        multiDexEnabled true
        //把tinker的相关类放到主包
        multiDexKeepFile file('../app/multiDexKeep.txt') 
        //配置支持的动态库类型
        ndk {
            abiFilters 'armeabi-v7a', 'x86'
        }
    }

    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.config//签名文件release和debug保存一致
        }
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.config//签名文件release和debug保存一致
        }
    }

    //开启jumboMode开关,tinker在diff时,可以使得dex文件更小
    dexOptions {
        jumboMode = true
    }

    compileOptions {
        sourceCompatibility = 1.8
        targetCompatibility = 1.8
    }

    kotlinOptions {
        jvmTarget = "1.8"
    }
}

dependencies {
    //添加multidex
    implementation "androidx.multidex:multidex:2.0.1"
    //添加对bugly  和 tinker,包含了crash捕获和热修复
    implementation "com.tencent.bugly:crashreport_upgrade:1.3.6"
    implementation "com.tencent.tinker:tinker-android-lib:1.9.9"
}

在app目录创建multiDexKeep.txt文件,保证tinker的相关类打包的时候都放到了主包

com/tencent/tinker/loader/shareutil/ShareResPatchInfo.class
com/tencent/tinker/loader/SystemClassLoaderAdder$V23.class
com/tencent/tinker/loader/hotplug/ActivityStubManager.class
com/tencent/tinker/loader/shareutil/ShareOatUtil$1.class
com/tencent/tinker/loader/shareutil/ShareOatUtil.class
com/tencent/tinker/loader/shareutil/SharePatchFileUtil.class
com/tencent/tinker/loader/shareutil/ShareFileLockHelper.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTKStub_04.class
com/tencent/tinker/loader/TinkerDexOptimizer$StreamConsumer$1.class
com/tencent/tinker/loader/TinkerDexLoader.class
com/tencent/tinker/loader/hotplug/ActivityStubs$STDStub_06.class
com/tencent/tinker/loader/TinkerDexOptimizer$1.class
com/tencent/tinker/loader/TinkerDexOptimizer$StreamConsumer.class
com/tencent/tinker/loader/hotplug/IncrementComponentManager.class
com/tencent/tinker/loader/AndroidNClassLoader.class
com/tencent/tinker/loader/TinkerResourcesKey.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SIStub_09.class
com/tencent/tinker/loader/hotplug/interceptor/ServiceBinderInterceptor.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTStub_07.class
com/tencent/tinker/loader/shareutil/ShareIntentUtil.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SIStub_00.class
com/tencent/tinker/loader/TinkerResourcesKey$V24.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTKStub_05.class
com/tencent/tinker/loader/TinkerTestAndroidNClassLoader.class
com/tencent/tinker/loader/hotplug/ActivityStubs$STDStub_01_T.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SIStub_01_T.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTStub_01_T.class
com/tencent/tinker/loader/hotplug/ActivityStubs$STDStub_07.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTKStub_00_T.class
com/tencent/tinker/loader/hotplug/handler/MHMessageHandler.class
com/tencent/tinker/loader/shareutil/ShareElfFile$SectionHeader.class
com/tencent/tinker/loader/TinkerDexOptimizer$ResultCallback.class
com/tencent/tinker/loader/TinkerResourceLoader.class
com/tencent/tinker/loader/SystemClassLoaderAdder$V19.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTStub_06.class
com/tencent/tinker/loader/hotplug/interceptor/ServiceBinderInterceptor$1.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SIStub_08.class
com/tencent/tinker/loader/hotplug/interceptor/ServiceBinderInterceptor$BinderInvocationHandler.class
com/tencent/tinker/loader/shareutil/ShareResPatchInfo$LargeModeInfo.class
com/tencent/tinker/loader/hotplug/ActivityStubs.class
com/tencent/tinker/loader/hotplug/interceptor/TinkerHackInstrumentation.class
com/tencent/tinker/loader/TinkerResourcesKey$V19.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTKStub_02.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTStub_09.class
com/tencent/tinker/loader/hotplug/ActivityStubs$STDStub_00.class
com/tencent/tinker/loader/shareutil/ShareElfFile$1.class
com/tencent/tinker/loader/hotplug/ActivityStubs$STDStub_02_T.class
com/tencent/tinker/loader/TinkerTestDexLoad.class
com/tencent/tinker/loader/shareutil/ShareElfFile$ProgramHeader.class
com/tencent/tinker/loader/TinkerSoLoader.class
com/tencent/tinker/loader/hotplug/interceptor/HandlerMessageInterceptor$CallbackWrapper.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTKStub_01_T.class
com/tencent/tinker/loader/hotplug/ActivityStubs$STDStub_08.class
com/tencent/tinker/loader/shareutil/ShareElfFile$ElfHeader.class
com/tencent/tinker/loader/TinkerResourcePatcher.class
com/tencent/tinker/loader/shareutil/ShareTinkerInternals.class
com/tencent/tinker/loader/hotplug/ComponentHotplug.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SIStub_07.class
com/tencent/tinker/loader/TinkerDexOptimizer$OptimizeWorker.class
com/tencent/tinker/loader/hotplug/interceptor/InterceptFailedException.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTStub_01.class
com/tencent/tinker/loader/BuildConfig.class
com/tencent/tinker/loader/TinkerLoader.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTKStub_03.class
com/tencent/tinker/loader/hotplug/ActivityStubs$STDStub_01.class
com/tencent/tinker/loader/SystemClassLoaderAdder$V4.class
com/tencent/tinker/loader/hotplug/interceptor/Interceptor$ITinkerHotplugProxy.class
com/tencent/tinker/loader/shareutil/ShareSecurityCheck.class
com/tencent/tinker/loader/hotplug/ActivityStubs$STDStub_09.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTStub_00.class
com/tencent/tinker/loader/hotplug/interceptor/ServiceBinderInterceptor$FakeInterfaceHandler.class
com/tencent/tinker/loader/SystemClassLoaderAdder.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTStub_08.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SIStub_06.class
com/tencent/tinker/loader/TinkerResourcesKey$V17.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTKStub_00.class
com/tencent/tinker/loader/hotplug/interceptor/HandlerMessageInterceptor.class
com/tencent/tinker/loader/shareutil/ShareBsDiffPatchInfo.class
com/tencent/tinker/loader/shareutil/ShareReflectUtil.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTStub_00_T.class
com/tencent/tinker/loader/hotplug/ActivityStubs$STDStub_00_T.class
com/tencent/tinker/loader/hotplug/ActivityStubs$STDStub_02.class
com/tencent/tinker/loader/TinkerUncaughtHandler.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SIStub_02_T.class
com/tencent/tinker/loader/TinkerDexOptimizer.class
com/tencent/tinker/loader/shareutil/ShareConstants.class
com/tencent/tinker/loader/shareutil/SharePatchInfo.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTKStub_09.class
com/tencent/tinker/loader/hotplug/handler/PMSInterceptHandler.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SIStub_05.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTStub_03.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTKStub_01.class
com/tencent/tinker/loader/SystemClassLoaderAdder$V14.class
com/tencent/tinker/loader/hotplug/ActivityStubs$STDStub_03.class
com/tencent/tinker/loader/hotplug/IncrementComponentManager$AttrTranslator.class
com/tencent/tinker/loader/shareutil/ShareOatUtil$InstructionSet.class
com/tencent/tinker/loader/app/TinkerApplication.class
com/tencent/tinker/loader/hotplug/interceptor/ServiceBinderInterceptor$FakeClientBinderHandler.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTStub_02.class
com/tencent/tinker/loader/hotplug/UnsupportedEnvironmentException.class
com/tencent/tinker/loader/TinkerRuntimeException.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SIStub_04.class
com/tencent/tinker/loader/hotplug/interceptor/HandlerMessageInterceptor$MessageHandler.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTKStub_06.class
com/tencent/tinker/loader/TinkerDexLoader$1.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SIStub_00_T.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTStub_02_T.class
com/tencent/tinker/loader/hotplug/ActivityStubs$STDStub_04.class
com/tencent/tinker/loader/hotplug/IncrementComponentManager$1.class
com/tencent/tinker/loader/hotplug/EnvConsts.class
com/tencent/tinker/loader/hotplug/handler/AMSInterceptHandler.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTStub_05.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SIStub_03.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SIStub_02.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTKStub_07.class
com/tencent/tinker/loader/hotplug/ActivityStubs$STDStub_05.class
com/tencent/tinker/loader/AbstractTinkerLoader.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTKStub_02_T.class
com/tencent/tinker/loader/SystemClassLoaderAdder$1.class
com/tencent/tinker/loader/TinkerResourcesKey$V7.class
com/tencent/tinker/loader/shareutil/ShareDexDiffPatchInfo.class
com/tencent/tinker/loader/shareutil/ShareElfFile.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTKStub_08.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SGTStub_04.class
com/tencent/tinker/loader/hotplug/interceptor/Interceptor.class
com/tencent/tinker/loader/hotplug/ActivityStubs$SIStub_01.class
com/tencent/tinker/loader/R.class

混淆文件的配置,每个项目用到的三方库都不尽相同,在这里面只是给个示例

#-------app start-------
-keep class com.i.proj.main.app.IApplication
-keep class com.yadong.biz_main.model** { *; }
-keep class com.yadong.biz_detail.model** { *; }
-keep class com.yadong.pub_model** { *; }
-keep class com.yadong.service_login** { *; }

-keepclassmembers class * extends androidx.lifecycle.ViewModel {
   *;
}
#-------app end-------


#-------arouter start-------
-keep public class com.alibaba.android.arouter.routes.**{*;}
-keep public class com.alibaba.android.arouter.facade.**{*;}
-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}
# 如果使用了 byType 的方式获取 Service,需添加下面规则,保护接口
-keep interface * implements com.alibaba.android.arouter.facade.template.IProvider
# 如果使用了 单类注入,即不定义接口实现 IProvider,需添加下面规则,保护实现
-keep class * implements com.alibaba.android.arouter.facade.template.IProvider
#-------arouter end-------


#-------retrofit start-------
-keepattributes Signature, InnerClasses, EnclosingMethod
# Retrofit does reflection on method and parameter annotations.
-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations
# Retain service method parameters when optimizing.
-keepclassmembers,allowshrinking,allowobfuscation interface * {
    @retrofit2.http.* <methods>;
}
# Ignore annotation used for build tooling.
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
# Ignore JSR 305 annotations for embedding nullability information.
-dontwarn javax.annotation.**
# Guarded by a NoClassDefFoundError try/catch and only used when on the classpath.
-dontwarn kotlin.Unit
# Top-level functions that can only be used by Kotlin.
-dontwarn retrofit2.KotlinExtensions
-dontwarn retrofit2.KotlinExtensions$*
# With R8 full mode, it sees no subtypes of Retrofit interfaces since they are created with a Proxy
# and replaces all potential values with null. Explicitly keeping the interfaces prevents this.
-if interface * { @retrofit2.http.* <methods>; }
-keep,allowobfuscation interface <1>
#-------retrofit end-------


#-------okhttp start-------
# A resource is loaded with a relative path so the package of this class must be preserved.
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
-dontwarn org.codehaus.mojo.animal_sniffer.*
# OkHttp platform used only on JVM and when Conscrypt dependency is available.
-dontwarn okhttp3.internal.platform.ConscryptPlatform
-dontwarn org.conscrypt.ConscryptHostnameVerifier
#-------okhttp end-------


#-------okio start-------
# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
-dontwarn org.codehaus.mojo.animal_sniffer.*
#-------okio end-------


#-------AndroidX start-------
-keep class com.google.android.material.** {*;}
-keep class androidx.** {*;}
-keep public class * extends androidx.**
-keep interface androidx.** {*;}
-dontwarn com.google.android.material.**
-dontwarn androidx.**
#-------AndroidX end-------


#-------bugly start-------
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
# tinker混淆规则
-dontwarn com.tencent.tinker.**
-keep class com.tencent.tinker.** { *; }
#-------bugly end-------


#-------Gson start-------
# For using GSON @Expose annotation
-keepattributes *Annotation*
# Gson specific classes
-dontwarn sun.misc.**
-keep class * extends com.google.gson.TypeAdapter
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
# Prevent R8 from leaving Data object members always null
-keepclassmembers,allowobfuscation class * {
  @com.google.gson.annotations.SerializedName <fields>;
}
#-------Gson end-------


#-------glide start-------
-dontwarn com.bumptech.glide.**
-keep class com.bumptech.glide.**{*;}
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
  **[] $VALUES;
  public *;
}
#-------glide end-------


#kotlin
-keep class kotlin.** { *; }
-keep class kotlin.Metadata { *; }
-dontwarn kotlin.**
-keepclassmembers class **$WhenMappings {
    <fields>;
}
-keepclassmembers class kotlin.Metadata {
    public <methods>;
}
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
    static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
}

-keepclasseswithmembernames class * {
    native <methods>;
}

-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}
-keepclassmembers class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator *;
}
-keep class **.R$* {*;}
-keepclassmembers enum * { *;}

同步一下项目

Application初始化

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        //分包
        MultiDex.install(base);
        //tinker
        Beta.installTinker();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Bugly.init(this, "846911389d", true);
        //安装了这个app的设备设置为启动测试机,这样可以在开发测试阶段,只下发测试补丁,不影响线上用户
        Bugly.setIsDevelopmentDevice(this, true);
    }

Bugly的init中的ID需要去官网申请

mainfext清单文件,添加权限以及BetaActivity

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <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.READ_PHONE_STATE" />

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

tinker-support.gradle打包配置文件,比原生tinker配置要简化的非常多,到这一步已经集成成功了,接下来重点来了

验证热修复能力

基准包

构建基准包(可以理解为发布的包).并且安装到你的手机,并联网打开,使得bugly上报该版本的tinkerId有人使用,否则发布patch时,将会报错没有匹配到基准包tinkerId


构建成功后,先把基准包apk安装到手机上跑起来,这样就可以让bugly知道已经有基准包已经安装上了

补丁包

333.png

然后把build/bakApk文件下的app-0708-19-25-58赋值到tinker-support文件的 def baseApkDir="app-0708-19-25-58",然后找个地方随便修改几行代码,比如说首页加个toast,然后按照上图步骤构建patch补丁文件,最后将补丁文件上传到bugly补丁管理后台

完整的测试流程

1.打基准包安装并上报联网(注:填写唯一的tinkerId)
2.对基准包的bug修复(可以是java代码变更,资源的变更)
3.修改基准包路径,修改补丁包tinkerId,mapping文件路径(如果开启了混淆需要配置),resId文件路径
4.执行buildTinkerPatchRelease打Release版本补丁包
5.选择app/build/outputs/patch目录下的补丁包并上传(注:不要选择tinkerPatch目录下的补丁包,不然上传会有问题)
6.编辑下发补丁规则,点击立即下发
7.杀死进程并重启基准包,请求补丁策略(SDK会自动下载补丁并合成)
8.再次重启基准包,检验补丁应用结果
9.查看页面,查看激活数据的变化

注意事项

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.例如transtion动画,notification icon以及桌面图标
6.每个项目的配置打包,构建补丁都会出现各种各样的问题.那此时,请注意观察控制台错误日志的输出.接着可以到bugly和tinker在github上的issues上面,搜一搜,看一看,目前还有哪些现存的问题.那么这些问题有可能就是你遇到的问题.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容