依赖配置流程
引入依赖
- 在项目的 build.gradle 中加入依赖
buildscript {
repositories {
jcenter()
google()
}
dependencies {
//...
classpath "com.tencent.bugly:tinker-support:1.1.5"
}
}
- 在项目 app 中加入依赖 ( 之前引入过的 bugly 异常捕获和应用升级的依赖可以直接注释掉,下面依赖已经包含了)
compile 'com.android.support:multidex:1.0.0' //有了就不需要再加了
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:3.7.1'
tinker 配置
- 在 app 文件夹的根目录新建 tinker-support.gradle 文件内容如下(建议直接复制)
apply plugin: 'com.tencent.bugly.tinker-support'
def bakPath = file("${buildDir}/bakApk/")
/**
* 此处填写每次构建生成的基准包目录
*/
def baseApkDir = "app-1024-16-15-32"
/**
* 对于插件各参数的详细解析请参考
*/
tinkerSupport {
// 开启tinker-support插件,默认值true
enable = true
// 指定归档目录,默认值当前module的子目录tinker
autoBackupApkDir = "${bakPath}"
//建议设置true,用户就不用再自己管理tinkerId的命名,插件会为每一次构建的base包自动生成唯一的tinkerId,默认命名规则是versionname.versioncode_时间戳
//具体参考https://github.com/BuglyDevTeam/Bugly-Android-Demo/wiki/Tinker-ID%E8%AF%A5%E6%80%8E%E4%B9%88%E8%AE%BE%E7%BD%AE
// autoGenerateTinkerId = true
//tinkerId必须保证唯一性,如果两个base包的tinkerid是一样的,并且都联网激活了,那么后续补丁上传到后台的时候会出现匹配错误
// tinkerId = "if autoGenerateTinkerId=true ,no need set here"
// 是否启用覆盖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 = "v1.8.5_patch_1"
// 构建多渠道补丁时使用
// buildAllFlavorsDir = "${bakPath}/${baseApkDir}"
// 是否启用加固模式,默认为false.(tinker-spport 1.0.7起支持)
// isProtectedApp = true
// 是否开启反射Application模式
enableProxyApplication = true
// 是否支持新增非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的分配
}
}
- 在 app 的 build.gradle 加入 apply from: ‘tinker-support.gradle’ 这个配置文件
apply plugin: 'com.android.application'
apply from: 'tinker-support.gradle'
android {
//...
}
项目配置
这里我只说一种可以改 Application 的入侵性比较弱一点的方式,但是相对没那么稳定:)
- Application 配置( Bugly 获取 appId 这里就不说怎么弄的了吧),记得引用这个 Application
@Override
public void onCreate() {
super.onCreate();
//热修复
initBetaHotfix();
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
//热更新
private void initBetaHotfix() {
// 设置是否开启热更新能力,默认为true
Beta.enableHotfix = true;
// 设置是否自动下载补丁,默认为true
Beta.canAutoDownloadPatch = true;
// 设置是否自动合成补丁,默认为true
Beta.canAutoPatch = true;
// 设置是否提示用户重启,默认为false
Beta.canNotifyUserRestart = false;
// 安装tinker
Beta.installTinker();
// 设置开发设备,默认为false,上传补丁如果下发范围指定为“开发设备”,需要调用此接口来标识开发设备
Bugly.setIsDevelopmentDevice(this, true);
// 调试时,将第三个参数改为true
Bugly.init(this, Config.BUGLY_ID, true);
//异常捕获
CrashReport.initCrashReport(getApplicationContext(), Config.BUGLY_ID, false);
}
- AndroidManifest.xml 文件配置,不考虑 bugly 热修复依赖版本太低问题,不引入 FileProvider 相关配置
<activity
android:name="com.tencent.bugly.beta.ui.BetaActivity"
android:configChanges="keyboardHidden|orientation|screenSize|locale"
android:theme="@android:style/Theme.Translucent" />
添加权限
<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" />
配置到这里就完成啦
----------------------------------------- 华丽的分割 -----------------------------------------
打包流程
打包流程主要分成两个一个是打基包,一个是打补丁包
打基包
指定一个版本的基包例如 v1.8.5 版本的基包
- 在之前配置好的 tinker-support.gradle 文件下修改 tinkerId 例如: tinkerId = “v1.8.5_base” 注意:这里的thinkerId 要保证在你之前发过的版本里是唯一的,这样我们的补丁包才可以检测匹配到
- 点击左边标签 (Gradle)选择 项目名 > :app > Tasks > build > assembleRelease 运行(注意:如果没有启用混淆或者没有签名会出错,必须启用混淆并且签名)
- 运行完成后,在 app > build > bakApk > app-xxxx-xx-xx-xx 里面有三个文件,分别为: tinker-support.gradle 配置里设置的三个文件名(如果没有 app-release-mapping.txt 文件的话有可能是你没有启用混淆,或者是你的混淆文件有问题)
- 将 app-1024-16-15-32(命名规则 月日-小时-分-秒) 这个文件夹存档(因为后面你 clean 项目或者 rebuild 项目这个文件会消失)后面我们打包补丁包的时候需要用到
通过以上步骤就完成了基包的打包(直接将这个包发到各大应用商城,不说分渠道打包)
打包补丁包
- 先把你想要打补丁的基包文件(上面的 app-1024-16-15-32 文件夹)放到 app > build > bakApk 文件夹下,如果没有 bakApk 文件夹就新建一个。
- copy 你基包文件夹名称 eg: app-1024-16-15-32 然后黏贴到 tinker-support.gradle 配置文件的 def baseApkDir = “基包文件夹名称”
- 在这个配置文件修改 tinkerId (跟上面基类包一样 需要唯一)规则你自己来定
- 生成补丁包,Gradle > 项目名 > app > Tasks > thinker_support > buildTinkerPatchRelease
- 生成的补丁包会在 app > bulid > outputs > patch > release 文件夹中,有三个 apk 文件这里我们需要用到的是 _7zip.apk 的 apk 文件
- 检查我们的补丁包,双击 patch_signed_7zip.apk 文件 会有一个 YAPATCH.MF 文件,点击这个文件就可以查看你的补丁包的一些信息,他是通过匹配到基包的 thinkId 去下发补丁包的,下面 tinkerId 打错了(懒得改)
- 接下来就可以直接上 bugly 的官网上发布补丁了,如果你怕出错你可以先发一个测试设备的补丁包,没问题再发全量设备的补丁包。
注意点
- 项目需要签名打包
- 项目需要引入混淆配置 并打开
- 保留每次生成基类包的 apk , mapping , R 文件 还有 tinkerId (唯一)
- 每次生成 patch 补丁包的时候要修改 热修复配置文件的 tinkerId (需要唯一)
- 出现 tinker_intermediates\values_backup notfoundFile 的时候 clean 一下项目 然后把你之前基包文件(例如 app-1024-16-15-32 文件夹)放到 app > build > bakApk 文件夹下,如果没有 bakApk 文件夹就新建一个。
混淆文件问题
我们混淆文件可能会出现问题造成 app-release-mapping.txt 文件没有生成,在这里我直接给出一个比较万能的签名文件(你的其他库的混淆还有你自己混淆加再后面就行啦,bugly 的混淆我已经加到最后面了)
注意: 之前存在的一些库有需要加入混淆的都需要加上不然打包出来的安装包可能会报错
-keep public class com.android.vending.licensing.ILicensingService # 保持哪些类不被混淆
-keep public class com.google.vending.licensing.ILicensingService # 保持哪些类不被混淆
-keepclassmembers class **.R$* {
public static <fields>;
public static final int *;
}
-keepclasseswithmembernames class * { # 保持 native 方法不被混淆
native <methods>;
}
-keepclasseswithmembers class * { # 保持自定义控件类不被混淆
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {# 保持自定义控件类不被混淆
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers class * extends android.app.Activity { # 保持自定义控件类不被混淆
public void *(android.view.View);
}
-keepclassmembers enum * { # 保持枚举 enum 类不被混淆
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable { # 保持 Parcelable 不被混淆
public static final android.os.Parcelable$Creator *;
}
-keep public class * extends android.view.View {
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*(...);
}
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
}
#apk 包内所有 class 的内部结构
#-dump class_files.txt
#未混淆的类和成员
#-printseeds seeds.txt
#列出从 apk 中删除的代码
#-printusage unused.txt
#混淆前后的映射
#-printmapping mapping.txt
#fastjson 可以混淆也可以不混淆
#-keep class javax.ws.rs.** { *; }
#-dontwarn com.alibaba.fastjson.**
#-keep class com.alibaba.fastjson.** { *; }
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
public <fields>;
}
-keepattributes Signature
#gson
-dontwarn com.google.gson.**
-keep class com.google.gson.** { *;}
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
public <fields>;
}
#v4
-dontwarn android.support.v4.**
-keep class android.support.v4.** { *; }
-keep interface android.support.v4.app.** { *; }
-keep public class * extends android.support.v4.**
-keep public class * extends android.app.Fragment
#greendao
#-dontwarn de.greenrobot.dao.**
#-keep class de.greenrobot.dao.** { *;}
-keepclassmembers class * extends de.greenrobot.dao.AbstractDao {
public static java.lang.String TABLENAME;
}
-keep class **$Properties
# 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆
-keepclassmembers class * {
void *(**On*Event);
void *(**On*Listener);
}
# webView处理,项目中没有使用到webView忽略即可
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
public *;
}
-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);
}
#
# Bugly混淆规则
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
# tinker
-dontwarn com.tencent.tinker.**
-keep class com.tencent.tinker.** { *; }
# 避免影响升级功能,需要keep住support包的类
#-keep class android.support.**{*;}
踩了一个坑,过来补充一下。
问题:继承了微信分享后没有引入依赖造成分享无法使用。
所以在要混淆文件中加入
-keep class com.tencent.mm.opensdk.** {*;}
-keep class com.tencent.wxop.** {*;}
-keep class com.tencent.mm.sdk.** {*;}