前言
更新一直是一个很重要的点,从最开始的全量更新,到我之前讲过的增量更新,当然也到今天我要讲的bugly热更新。
今天涉及的内容:
- bugly热更新前的准备
1.1 bugly上注册账号,新建产品
1.2 项目前期准备 - 热更新集成步骤
2.1 添加插件依赖
2.2 集成SDK
2.3 app_module的build.gradle中添加tinker-support.gradle引用
2.4 新建tinker-support.gradle文件
2.5 初始化SDK
2.6 AndroidManifest.xml配置
2.7 混淆配置 - 编译基包和补丁包
3.1 编译基包
3.2 启动apk,上报联网数据
3.3 编译补丁包 - 上传补丁包到bugly
- 热更新集成注意的问题
5.1 在编译基包完成后,bakApk文件夹下缺少app-release-mapping.txt文件
5.2 生成的基包apk app-release.apk 安装后不能运行
5.3 编译完补丁包后,项目/app/outputs/patch/ 文件夹下无文件
5.4 编译完补丁包后,项目/app/outputs/patch/ 文件夹下无patch_release_7zip.apk文件
5.5 打补丁包生成错误
5.6 bugly上传补丁包失败
5.7 bugly上传补丁包时,提示没有创建时间等信息
5.8 bugly上传补丁包时,没有自动提示目标版本
5.9 如何判断基包联网上传成功
5.10 补丁包已经上传了,但是运行手机上的apk,并没有热更新 - 更多相关帮助
一. bugly热更新前的准备
1.1 bugly上注册账号,新建产品
这一操作比较简单,就是登录bugly官网(没有的话自己注册一个账号),然后新建一个产品,在产品设置界面获得该产品AppId
(假设此时我获得的AppId=hyeieooe
)
1.2 项目前期准备
你项目在前期需要做以下准备:
- 在app_module的build.gradle中配置项目签名
也即是你需要给你的项目在·build.gradle`中配置打包签名。具体签名操作可以参考:
Gradle中给项目签名
- 给你的项目添加混淆配置
为了给增加app的密保性能且为了更好的接入bugly热更新
功能,你需要给自己的项目添加混淆配置。
给项目加入混淆配置的步骤你可以参考androidstudio混淆签名
然后这里,我添加一个我使用的混淆文件proguard-rules.pro
,代码如下:
-dontshrink
-flattenpackagehierarchy
#指定压缩级别
-optimizationpasses 7
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
#不跳过非公共的库的类成员
-dontskipnonpubliclibraryclassmembers
#混淆时采用的算法 后面的参数是一个过滤器,这个过滤器是谷歌推挤的算法
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
#把混淆类中的方法名也混淆了
-useuniqueclassmembernames
#优化时允许访问并修改有修饰符的类和类的成员
-allowaccessmodification
#包明不混合大小写
-dontusemixedcaseclassnames
#不去忽略非公共的库类
-dontskipnonpubliclibraryclasses
#不混淆输入的类文件
-dontobfuscate
#混淆时是否记录日志
-verbose
# 混淆时不做预校验 Android 不需要做 preverify,去掉可加快混淆速度
-dontpreverify
#将文件来源重命名为“SourceFile”字符串
-renamesourcefileattribute SourceFile
#保留行号
-keepattributes SourceFile,LineNumberTable,Deprecated
#保护注解
-keepattributes *Annotation*,InnerClasses,Deprecated
# 泛型与反射
-keepattributes Signature
-keepattributes EnclosingMethod
-keepattributes *Annotation*
-keep class package.classname{*;}
#保持 native 方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
}
#不提示兼容库的错误警告
-dontwarn android.support.**
#保持所有实现 Serializable 接口的类成员
-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();
}
# 保持测试相关的代码
-dontnote junit.framework.**
-dontnote junit.runner.**
-dontwarn android.test.**
-dontwarn android.support.test.**
-dontwarn org.junit.**
#Fragment不需要在AndroidManifest.xml中注册,需要额外保护下
-keep public class * extends android.support.v4.app.Fragment
-keep public class * extends android.support.v7.app.AppCompatActivity
-keep public class * extends android.app.Fragment
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-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 java.lang.Throwable {*;}
-keep public class * extends java.lang.Exception {*;}
-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
-keep class android.support.** {*;}
-keep public class * extends android.os.IInterface
-keep interface android.support.constraint.** { *; }
-keep class androidx.** {*;}
-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);
}
# 保持自定义控件类不被混淆
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
-keep class * extends android.support.v4.app.FragmentManager{ *; }
-keepclasseswithmembernames class android.support.v4.widget.ViewDragHelper{ *; }
#不混淆资源类及其方法
-keep class **.R$* {
*;
}
# 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆
-keepclassmembers class * {
void *(**On*Event);
void *(**On*Listener);
}
# 保持自定义控件类不被混淆
-keepclasseswithmembers class * {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
public void set*(...);
}
# 保持枚举 enum 类不被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# 保持 Parcelable 不被混淆
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
#-----------处理实体类---------------
# 在开发的时候我们可以将所有的实体类放在一个包内,这样我们写一次混淆就行了。
#-keep public class com.test.models.** {
# public void set*(***);
# public *** get*();
# public *** is*();
#
#}
#保护WebView对HTML页面的API不被混淆
-keep class **.Webview2JsInterface { *; }
#如果项目中用到了WebView的复杂操作
-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, java.lang.String);
}
# 移除Log类打印各个等级日志的代码,打正式包的时候可以做为禁log使用,这里可以作为禁止log打印的功能使用
# 记得proguard-android.txt中一定不要加-dontoptimize才起作用
# 另外的一种实现方案是通过BuildConfig.DEBUG的变量来控制
#-assumenosideeffects class android.util.Log {
# public static int v(...);
# public static int i(...);
# public static int w(...);
# public static int d(...);
# public static int e(...);
#}
#############################################
#
# 项目中特殊处理部分
#
#############################################
#-----------处理反射类---------------
#-----------处理js交互---------------
#-----------处理第三方依赖库---------
-printmapping mapping.txt
#EventBus
-keepattributes *Annotation*
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# Glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.AppGlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
# ButterKnife
-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }
-keepclasseswithmembernames class * {
@butterknife.* <fields>;
}
-keepclasseswithmembernames class * {
@butterknife.* <methods>;
}
# 极光推送
-dontoptimize
-dontpreverify
-dontwarn cn.jpush.**
-keep class cn.jpush.** { *; }
-keep class * extends cn.jpush.android.helpers.JPushMessageReceiver { *; }
-dontwarn cn.jiguang.**
-keep class cn.jiguang.** { *; }
-keep public class cn.jiguang.analytics.android.api.** {
*;
}
-dontwarn com.google.**
-keep class com.google.gson.** {*;}
-keep class com.google.protobuf.** {*;}
# OkHttp
-keep class com.squareup.okhttp.** { *; }
-keep interface com.squareup.okhttp.** { *; }
-dontwarn com.squareup.okhttp.**
# OkHttp3
-dontwarn com.squareup.okhttp3.**
-keep class com.squareup.okhttp3.** { *;}
-dontwarn okio.**
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**
# JSR 305 annotations are for embedding nullability information.
-dontwarn javax.annotation.**
# 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
# Okio
-keep class sun.misc.Unsafe { *; }
-dontwarn java.nio.file.*
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
-keep class okio.**{*;}
-dontwarn okio.**
-keep class org.litepal.** {
*;
}
-keep class * extends org.litepal.crud.DataSupport {
*;
}
-keep class * extends org.litepal.crud.LitePalSupport {
*;
}
# Retrolambda
-dontwarn java.lang.invoke.*
## ---------Retrofit混淆方法---------------
-dontwarn javax.annotation.**
-dontwarn javax.inject.**
# OkHttp3
-dontwarn okhttp3.logging.**
-keep class okhttp3.internal.**{*;}
-dontwarn okio.**
# Retrofit
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes Signature
-keepattributes Exceptions
# RxJava RxAndroid
-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
long producerIndex;
long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
rx.internal.util.atomic.LinkedQueueNode producerNode;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
rx.internal.util.atomic.LinkedQueueNode consumerNode;
}
-dontnote rx.internal.util.PlatformDependent
# Gson
-keepattributes EnclosingMethod
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.stream.** { *; }
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
# 使用Gson时需要配置Gson的解析对象及变量都不混淆。不然Gson会找不到变量。
# 将下面替换成自己的实体类
-keep class com.inm.models.** { *; }
-keep public class * extends android.app.Activity
-keep public class * extends android.support.v4.app.Fragment
-keepclassmembers class * extends android.app.Activity {
public int getContainerViewId();
public boolean onRiggerBackPressed();
public void onFragmentResult(int,int,android.os.Bundle);
public void onLazyLoadViewCreated(android.os.Bundle);
public int[] getPuppetAnimations();
public String getFragmentTag();
public boolean onInterruptBackPressed();
}
-keepclassmembers class * extends android.support.v4.app.Fragment {
public int getContainerViewId();
public boolean onRiggerBackPressed();
public void onFragmentResult(int,int,android.os.Bundle);
public void onLazyLoadViewCreated(android.os.Bundle);
public int[] getPuppetAnimations();
public String getFragmentTag();
public boolean onInterruptBackPressed();
}
# androidx 的混淆代码
-keep class com.google.android.** {*;}
-keep class androidx.** {*;}
-keep public class * extends androidx.**
-keep interface androidx.** {*;}
-dontwarn com.google.android.material.**
-dontnote com.google.android.material.**
-dontwarn androidx.**
-keepattributes *Annotation*
-keepattributes *JavascriptInterface*
-keep class android.webkit.JavascriptInterface {*;}
# ------------------ Keep LineNumbers and properties ---------------- #
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
# --------------------------------------------------------------------------
#---------------------------------------------------------------------------
#------------------ 下方是android平台自带的排除项,这里不要动 ----------------
-keep public class * extends android.app.Activity{
public <fields>;
public <methods>;
}
-keep public class * extends android.app.Application{
public <fields>;
public <methods>;
}
-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
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepattributes *Annotation*
-keepclasseswithmembernames class *{
native <methods>;
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
#------------------ 下方是共性的排除项目 ----------------
# 方法名中含有“JNI”字符的,认定是Java Native Interface方法,自动排除
# 方法名中含有“JRI”字符的,认定是Java Reflection Interface方法,自动排除
-keepclasseswithmembers class * {
... *JNI*(...);
}
-keepclasseswithmembernames class * {
... *JRI*(...);
}
-keep class **JNI* {*;}
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
# tinker混淆规则
-dontwarn com.tencent.tinker.**
-keep class com.tencent.tinker.** { *; }
-keep public class com.tencent.tinker.entry.TinkerApplicationInlineFence {
<init>(...);
void attachBaseContext(com.tencent.tinker.loader.app.TinkerApplication, android.content.Context);
}
-keep public class com.tencent.bugly.beta.tinker.TinkerPatchReflectApplication {
<init>(...);
}
这里需要注意的是,如果你项目在混淆的时候报 R8
错误,那么你需要在你项目的gradle.properties
文件中加入以下代码以关闭R8
相关处理:
# 混淆关闭 R8
android.enableR8=false
二.热更新集成步骤
下面开始进入正题,也即是bugly热更新
的集成。
2.1 添加插件依赖
工程根目录下“build.gradle”文件中添加:
buildscript {
repositories {
jcenter()
}
dependencies {
//tinkersupport插件
classpath "com.tencent.bugly:tinker-support:1.2.0"
}
}
2.2 集成SDK
在app_module
的build.gradle
中添加如下配置:
android {
defaultConfig {
//开启multidex
multiDexEnabled true
ndk {
// 设置支持的SO库架构
abiFilters 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'//,'armeabi'
}
}
}
dependencies {
//============bugly热更新======
// 多dex配置
implementation "com.android.support:multidex:1.0.1"
// 集成Bugly热更新aar(本地集成使用方式)
//compile(name: 'bugly_crashreport_upgrade-1.3.2', ext: 'aar')
// 远程仓库集成方式(推荐)
//compile 'com.tencent.bugly:crashreport_upgrade:1.3.8'
implementation 'com.tencent.bugly:crashreport_upgrade:1.3.8'
//1. 指定tinker依赖版本(注:应用升级1.3.5版本起,不再内置tinker)
//2.为了便于解答问题,这里的tinker版本建议跟随此处demo设置,如果微信更新了tinker版本,bugly会定期同步更新
implementation 'com.tencent.tinker:tinker-android-lib:1.9.14.3'
implementation 'com.tencent.bugly:nativecrashreport:2.2.0'
// walle(多渠道使用)
// compile 'com.meituan.android.walle:library:1.1.3'
//=============================
}
2.3 app_module的build.gradle中添加tinker-support.gradle引用
在 app_module
的build.gradle
的文件头部添加tinker-support.gradle
文件的引用,如下:
// 依赖插件脚本
apply from: 'tinker-support.gradle'
2.4 新建tinker-support.gradle文件
在 项目\app\
路径下新建tinker-support.gradle
文件,具体路径如下
然后
tinker-support.gradle
文件代码如下: