bugly 支持异常上报,应用升级与热更新;异常上报与应用升级使用较为简单,也没有什么难点。
下面看一下热更新,官网的文档说的虽然非常明白,但是罗里吧嗦,看的让人头大。
下面给出一个特别简单的集成与使用方式。
打开下面 bugly 给出的 GitHub 示例,下面是链接:
https://github.com/BuglyDevTeam/Bugly-Android-Demo
下载完毕,会看到有两个 demo ,如下图所示:
可以看到里面有两个:官网推荐 BuglyHotfixDemo;一键接入 BuglyHotfixEasyDemo ;
我们直接打开第二个,也就是 BuglyHotfixEasyDemo ,这个 demo 是热更新一键接入示例,后续也不再更新这个;
热更新sdk版本至1.3.6, tinker-support版本至1.1.5,对应tinker版本 1.9.9;
虽然不再更新这个,但是作为普通的用户,目前使用这个是完全够用的。
如果你想使用第一个 demo ,也建议先看第二个,因为这个非常简单,看完这个再看第一个会感觉并不复杂。
在接入热更新之前,我们要去申请 appid 过程很简单,打开官网登录 qq 账号申请即可,就不说了,下面是官网地址:
下面就看一下如何接入,我并不是按照官网来的,因为官网过于复杂;如果第一次使用建议新建一个 demo ,作为测试使用;
首先在项目的 build.gradle 中添加下面代码:
classpath 'com.tencent.bugly:tinker-support:1.1.5'
如下图:
在 app 下的 build.gradle 中添加:
// 依赖插件脚本,添加在最上面
apply from: 'tinker-support.gradle'
和
// 远程依赖集成方式(推荐)
compile "com.tencent.bugly:crashreport_upgrade:1.3.6"
// 指定tinker依赖版本(注:应用升级1.3.5版本起,不再内置tinker)
compile 'com.tencent.tinker:tinker-android-lib:1.9.9'
如下图所示:
和
新建 tinker-support.gradle 与 app 的 build.gradle 同级,代码如下:
apply plugin: 'com.tencent.bugly.tinker-support'
def bakPath = file("${buildDir}/bakApk/")
/**
* 此处填写每次构建生成的基准包目录
*/
def baseApkDir = "app-1119-14-54-04"
/**
* 对于插件各参数的详细解析请参考
*/
tinkerSupport {
// 开启tinker-support插件,默认值true
enable = true
// 自动生成tinkerId, 你无须关注tinkerId,默认为false
autoGenerateTinkerId = true
// 指定归档目录,默认值当前module的子目录tinker
autoBackupApkDir = "${bakPath}"
// 是否启用覆盖tinkerPatch配置功能,默认值false
// 开启后tinkerPatch配置不生效,即无需添加tinkerPatch
overrideTinkerPatchConfiguration = true
//测试
baseApk = "${bakPath}/${baseApkDir}/app-debug.apk"
baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-debug-mapping.txt"
baseApkResourceMapping = "${bakPath}/${baseApkDir}/app-debug-R.txt"
//正式
// baseApk = "${bakPath}/${baseApkDir}/app-release.apk"
// baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-release-mapping.txt"
// baseApkResourceMapping = "${bakPath}/${baseApkDir}/app-release-R.txt"
// 构建基准包跟补丁包都要修改tinkerId,主要用于区分
// tinkerId = "1.0.3-ccc"
// 打多渠道补丁时指定目录
buildAllFlavorsDir = "${bakPath}/${baseApkDir}"
// 是否使用加固模式,默认为false
// isProtectedApp = true
// 是否采用反射Application的方式集成,无须改造Application
enableProxyApplication = true
// 支持新增Activity
supportHotplugComponent = true
}
/**
* 一般来说,我们无需对下面的参数做任何的修改
* 对于各参数的详细介绍请参考:
* https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97
*/
tinkerPatch {
tinkerEnable = true
ignoreWarning = false
useSign = false
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 = "base-2.0.1"
}
}
如下图,作为参考:
在 tinkerSupport { } 里面有很多参数,请不要修改,还有需要看一下里面每个参数的意义;
这里说明几个比较重要的参数:
// 自动生成tinkerId, 你无须关注tinkerId,默认为false
autoGenerateTinkerId = true
参数 autoGenerateTinkerId = ture 自动生成 tinkerId,无需手动填写,方便了很多,也是和官网推荐 BuglyHotfixDemo 的一个区别;
添加这个参数也无需指定 tinkerId = "1.0.3-ccc" 这个参数;
正式和测试切换:
//测试
baseApk = "${bakPath}/${baseApkDir}/app-debug.apk"
baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-debug-mapping.txt"
baseApkResourceMapping = "${bakPath}/${baseApkDir}/app-debug-R.txt"
//正式
// baseApk = "${bakPath}/${baseApkDir}/app-release.apk"
// baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-release-mapping.txt"
// baseApkResourceMapping = "${bakPath}/${baseApkDir}/app-release-R.txt"
测试使用 debug ,正式使用 release;
参数 buildAllFlavorsDir,配置多渠道打包请打开,不需要请注释,最下面要配置多渠道打包,所有我就打开了;
// 打多渠道补丁时指定目录
buildAllFlavorsDir = "${bakPath}/${baseApkDir}"
参数 isProtectedApp = true;测试千万不要开启,会引发不必要的 bug ,当应用需要加固时再开启;
// 是否使用加固模式,默认为false
// isProtectedApp = true
参数 enableProxyApplication = true,无需改造 Application;
// 是否采用反射Application的方式集成,无须改造Application
enableProxyApplication = true
新建 MyApplication 加入下面代码:
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
setStrictMode();
// 设置是否开启热更新能力,默认为true
Beta.enableHotfix = true;
// 设置是否自动下载补丁
Beta.canAutoDownloadPatch = true;
// 设置是否提示用户重启
Beta.canNotifyUserRestart = false;
// 设置是否自动合成补丁
Beta.canAutoPatch = true;
/**
* 补丁回调接口,可以监听补丁接收、下载、合成的回调
*/
Beta.betaPatchListener = new BetaPatchListener() {
@Override
public void onPatchReceived(String patchFileUrl) {
//补丁包下载地址为patchFileUrl
}
@Override
public void onDownloadReceived(long savedLength, long totalLength) {
//热修复回调成功
}
@Override
public void onDownloadSuccess(String patchFilePath) {
//热修复下载成功
}
@Override
public void onDownloadFailure(String msg) {
//热修复下载失败
}
@Override
public void onApplySuccess(String msg) {
//补丁包应用成功
}
@Override
public void onApplyFailure(String msg) {
//补丁包应用失败
}
@Override
public void onPatchRollback() {
//补丁回滚
}
};
long start = System.currentTimeMillis();
Bugly.setUserId(this, "falue");
Bugly.setUserTag(this, 123456);
Bugly.putUserData(this, "key1", "123");
Bugly.setAppChannel(this, "bugly");
// 这里实现SDK初始化,appId替换成你的在Bugly平台申请的appId,调试时将第三个参数设置为true
Bugly.init(this, "4bf2a8d8eb", true);
long end = System.currentTimeMillis();
Log.e("init time--->", end - start + "ms");
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
// 热更新必须添加
MultiDex.install(base);
// 安装tinker
Beta.installTinker();
}
@TargetApi(9)
protected void setStrictMode() {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
}
}
别忘记在 AndroidManifest.xml 中使用:
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
需要注意的是,下面的代码中要换成你申请的 appId:
// 这里实现SDK初始化,appId替换成你的在Bugly平台申请的appId,调试时将第三个参数设置为true
Bugly.init(this, "4bf2a8d8eb", true);
AndroidManifest.xml 文件中添加下面代码:
<!-- Bugly升级SDK配置开始 -->
<activity
android:name="com.tencent.bugly.beta.ui.BetaActivity"
android:theme="@android:style/Theme.Translucent"/>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.tencent.bugly.hotfix.easy.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
<!-- Bugly升级SDK配置结束-->
参考下图:
加入权限:
<!--Bugly升级SDK权限配置开始-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- <uses-permission android:name="android.permission.GET_TASKS" /> -->
<!--Bugly升级SDK权限配置结束-->
在 xml 中的 provider_paths 加入代码,没有请创建:
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!-- /storage/emulated/0/Download/com.bugly.upgrade.demo/.beta/apk-->
<external-path name="beta_external_path" path="Download/"/>
<!--/storage/emulated/0/Android/data/com.bugly.upgrade.demo/files/apk/-->
<external-path name="beta_external_files_path" path="Android/data/"/>
</paths>
参考下图:
MainActivity 中加入代码:
public class MainActivity extends AppCompatActivity {
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "这是存在bug的", Toast.LENGTH_SHORT).show();
}
});
}
}
里面只有一个 button ;
最后加入混淆:
# Bugly混淆规则
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
# tinker
-dontwarn com.tencent.tinker.**
-keep class com.tencent.tinker.** { *; }
参考下图:
多渠道打包配置,暂时配置三个:
productFlavors {
huawei {
// applicationId "com.tencent.bugly.hotfix.huawei"
manifestPlaceholders = [CHANNEL_VALUE: "huawei"]
}
qihu360 {
// applicationId "com.tencent.bugly.hotfix.qihu360"
manifestPlaceholders = [CHANNEL_VALUE: "qihu360"]
}
oppo {
// applicationId "com.tencent.bugly.hotfix.oppo"
manifestPlaceholders = [CHANNEL_VALUE: "oppo"]
}
}
作为参考我给出我的 build.gradle 所有代码:
apply plugin: 'com.android.application'
// 依赖插件脚本
apply from: 'tinker-support.gradle'
android {
compileSdkVersion 26
buildToolsVersion '26.0.2'
defaultConfig {
applicationId "com.tencent.bugly.hotfix.easy"
minSdkVersion 14
targetSdkVersion 26
versionCode 3
versionName "1.0"
flavorDimensions "versionCode"
// 开启multidex
multiDexEnabled true
}
// 签名配置
signingConfigs {
release {
try {
storeFile file("./keystore/release.keystore")
storePassword "testres"
keyAlias "testres"
keyPassword "testres"
} catch (ex) {
throw new InvalidUserDataException(ex.toString())
}
}
debug {
storeFile file("./keystore/debug.keystore")
}
}
buildTypes {
release {
minifyEnabled true
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
debuggable true
minifyEnabled false
// signingConfig signingConfigs.debug
}
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
repositories {
flatDir {
dirs 'libs'
}
}
// productFlavors 示例
//flavorDimensions "tier"
productFlavors {
huawei {
// applicationId "com.tencent.bugly.hotfix.huawei"
manifestPlaceholders = [CHANNEL_VALUE: "huawei"]
}
qihu360 {
// applicationId "com.tencent.bugly.hotfix.qihu360"
manifestPlaceholders = [CHANNEL_VALUE: "qihu360"]
}
oppo {
// applicationId "com.tencent.bugly.hotfix.oppo"
manifestPlaceholders = [CHANNEL_VALUE: "oppo"]
}
}
lintOptions {
checkReleaseBuilds false
abortOnError false
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:26.1.0'
// 多dex配置
compile "com.android.support:multidex:1.0.2"
// 本地集成aar方式
// compile(name: 'bugly_crashreport_upgrade-1.3.6', ext: 'aar')
// 远程依赖集成方式(推荐)
compile "com.tencent.bugly:crashreport_upgrade:1.3.6"
// 指定tinker依赖版本(注:应用升级1.3.5版本起,不再内置tinker)
compile 'com.tencent.tinker:tinker-android-lib:1.9.9'
}
上面的签名配置是我从 BuglyHotfixEasyDemo 中直接复制过来的;
所有配置完毕,下面就测试打包,点击右侧 Gradle ==> app ==> Tasks ==> build ==>assembleDebug ,如下图:
打包完毕,在左侧 app ==> bakApk 可以看到躺着三个包,这就是基准包,如下图:
需要注意的是:一般在正式的情况下,需要将 bakApk 文件夹里面的内容保存到本地,方便以后打补丁包使用,如果不存在了,直接复制到 bakApk 文件夹下面就可以了。
记住上面的 app-1126-16-12-58,也就是 bakApk ==> app-1126-16-12-58 ,这个是随机生成的,下面会使用到这个;
打开 tinker-support.gradle,找到 baseApkDir 参数,将里面的内容替换为 app-1126-16-12-58 ;如下图:
需要注意的是:你生成的是什么就写什么,这个是每打一次基准包,需要填写一次,打补丁包不需要改变;
一定要保证该目录下面有对应的包,这也就是上面为什么让你把基准包保存到本地的原因,以防丢失;
为了方便测试,将 MainActivity 中的提示改为下面:
Toast.makeText(MainActivity.this, "修复了bug,以便测试", Toast.LENGTH_SHORT).show();
点击右侧 Gradle ==> app ==> Tasks ==> tinker-support ==>buildAllFlavorsTinkerPatchDebug ,如下图:
等待过后,补丁包就打完了,在左侧 app ==> outputs ==> patch 中可以看到三个补丁包,如下图:
上面以 7-zip 结尾就是我们的补丁包了,也就是 patch_signed_7zip.apk ,也就是把这个包放到 bugly 官网就可以了;
下面就可以测试了,测试的时候需要注意:一定先将基本包安装到手机上,保证有网络的情况下打开 app ,让热更新代码走完补丁包才可以上传到 bugly 官网,否则上传不上去;
下面将基本包放到手机上面运行,因为打了三个包,就拿华为的包测试;
安装完毕,点击按钮可以发现,提示:这是存在bug的;与基本包一致,emmm;
打开 bugly 官网,将补丁包上传到官网,点击应用升级==>热更新==>发布新补丁,如下图所示:
不要上传错了,我们测试的是华为的基本包,所以补丁包也应该上传华为下面的 patch_signed_7zip.apk ;
上传如果提示,改包不合法,那么刷新页面再试一下,或者重新打开基本包的 app ;因为我也遇到过,是 bugly 后台的问题;
上传完毕,会自动识别版本,选择全量更新,写上备注,然后点击立即下发就可以了,如下图:
稍等片刻,就可以出现了,如下图:
我上面有两个,因为我已经测试过一次了,如果你们第一次测试,应该是一个。
热更新官网说是重启 app 就可以修复了,经过我测试,大概需要5分钟的时间,补丁才会生效,所以不必着急;
经过几分钟的重启 app ,点击按钮测试,发现提示语变成了:修复了bug,以便测试;
说明补丁包应用成功了,打开 bugly 官网,刷新页面,可以看到已经下发了一个了,如下图所示:
可以看到激活的数量为0,但我们已经应用成功了,因为有延迟,所以再过大概10分钟就可以看到激活量为1了,所以不用着急;
看完这个简单集成方式以后,再看 BuglyHotfixDemo 或者官网文档就会感觉简单多了。
如果你有什么问题,可以看下面我写的 demo ,或者留言;
https://github.com/wuqingsen/BuglyDemo
到这里就结束了。
记录一个bug,测试上没问题,但是我在正式上面先发成功,但是激活数量一直为0,如下图所示:
解决方法,在项目的 bulid.gradle 中的配置改为:
classpath "com.tencent.bugly:tinker-support:latest.release"
就好了,我之前用的是:
// classpath 'com.tencent.bugly:tinker-support:1.1.5' //之前配置
classpath "com.tencent.bugly:tinker-support:latest.release" //修改后
做一下记录。