第一步:
https://bugly.qq.com/v2/upgrade/
注册账号,进入主页时创建产品,实际上在创建产品时所输入的一些数据并是很重要,因为我们要的只是里面的APP ID,通过一波操作后,会出现你所创建的产品,
点击设置。
第二步:
导入SDK和项目配置 https://bugly.qq.com/docs/user-guide/instruction-manual-android-hotfix/?v=20180709165613
在APP的build.gradle中添加
// 多dex配置
implementation "com.android.support:multidex:1.0.2"
// 本地集成aar方式
// compile(name: 'bugly_crashreport_upgrade-1.3.6', ext: 'aar')
// 远程依赖集成方式(推荐)
implementation "com.tencent.bugly:crashreport_upgrade:1.3.6"
// 指定tinker依赖版本(注:应用升级1.3.5版本起,不再内置tinker)
implementation 'com.tencent.tinker:tinker-android-lib:1.9.9'
并且在顶部添加
apply from: 'tinker-support.gradle'
添加后会报错,原因是我们还没创建 tinker-support.gradle,创建 这个文件是一定要在app文件下第一级中,这里很容易出错(创建的位置不正确)
tinker-support.gradle内容:(代码自己复制,其他不改)
apply plugin: 'com.tencent.bugly.tinker-support'
def bakPath = file("${buildDir}/bakApk/")
/**
* 此处填写每次构建生成的基准包目录 这里再出包时要注意修改,详细修改方式下面讲解
*/
def baseApkDir = "app-0924-17-22-04"
/**
* 对于插件各参数的详细解析请参考
*/
tinkerSupport {
// 开启tinker-support插件,默认值true
enable = true
// 自动生成tinkerId, 你无须关注tinkerId,默认为false 建议改为true让其自动,不然自己搞容易出错
autoGenerateTinkerId = true
// 指定归档目录,默认值当前module的子目录tinker
autoBackupApkDir = "${bakPath}"
// 是否启用覆盖tinkerPatch配置功能,默认值false
// 开启后tinkerPatch配置不生效,即无需添加tinkerPatch
overrideTinkerPatchConfiguration = true
// 编译补丁包时,必需指定基线版本的apk,默认值为空
// 如果为空,则表示不是进行补丁包的编译
// @{link tinkerPatch.oldApk }
baseApk = "${bakPath}/${baseApkDir}/app-release.apk"
// baseApk = "${bakPath}/${baseApkDir}/app-debug.apk"
// 对应tinker插件applyMapping
baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-release-mapping.txt"
// baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-debug-mapping.txt"
// 对应tinker插件applyResourceMapping
baseApkResourceMapping = "${bakPath}/${baseApkDir}/app-release-R.txt"
// baseApkResourceMapping = "${bakPath}/${baseApkDir}/app-debug-R.txt"
// 构建基准包跟补丁包都要修改tinkerId,主要用于区分
// tinkerId = "1.0.3-ccc"
// 打多渠道补丁时指定目录
// buildAllFlavorsDir = "${bakPath}/${baseApkDir}"
// 是否使用加固模式,默认为false
// isProtectedApp = true
// 是否采用反射Application的方式集成,无须改造Application,这里的ture或false决定Application的使用方式,建议用true
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"
}
}
第三步:
Application的代码内容要根据tinker-support.gradle中的enableProxyApplication 的状态决定(二选一,个人建议使用第一种)
enableProxyApplication = true 时
public class BuglyTinkersApplication extends Application {
//当bugly.tinker-support.gradle中的enableProxyApplication 为 true时
@Override
public void onCreate() {
super.onCreate();
setStrictMode();
// 设置是否开启热更新能力,默认为true
Beta.enableHotfix = true;
// 设置是否自动下载补丁
Beta.canAutoDownloadPatch = true;
// 设置是否提示用户重启
Beta.canNotifyUserRestart = true;
// 设置是否自动合成补丁
Beta.canAutoPatch = true;
/**
* 全量升级状态回调
*/
Beta.upgradeStateListener = new UpgradeStateListener() {
@Override
public void onUpgradeFailed(boolean b) {
}
@Override
public void onUpgradeSuccess(boolean b) {
}
@Override
public void onUpgradeNoVersion(boolean b) {
Toast.makeText(getApplicationContext(), "最新版本", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrading(boolean b) {
Toast.makeText(getApplicationContext(), "onUpgrading", Toast.LENGTH_SHORT).show();
}
@Override
public void onDownloadCompleted(boolean b) {
}
};
/**
* 补丁回调接口,可以监听补丁接收、下载、合成的回调
*/
Beta.betaPatchListener = new BetaPatchListener() {
@Override
public void onPatchReceived(String patchFileUrl) {
Toast.makeText(getApplicationContext(), patchFileUrl, Toast.LENGTH_SHORT).show();
}
@Override
public void onDownloadReceived(long savedLength, long totalLength) {
Toast.makeText(getApplicationContext(), String.format(Locale.getDefault(),
"%s %d%%",
Beta.strNotificationDownloading,
(int) (totalLength == 0 ? 0 : savedLength * 100 / totalLength)), Toast.LENGTH_SHORT).show();
}
@Override
public void onDownloadSuccess(String patchFilePath) {
Toast.makeText(getApplicationContext(), patchFilePath, Toast.LENGTH_SHORT).show();
// Beta.applyDownloadedPatch();
}
@Override
public void onDownloadFailure(String msg) {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
@Override
public void onApplySuccess(String msg) {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
@Override
public void onApplyFailure(String msg) {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
@Override
public void onPatchRollback() {
Toast.makeText(getApplicationContext(), "onPatchRollback", Toast.LENGTH_SHORT).show();
}
};
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, "**********", true);
long end = System.currentTimeMillis();
Log.e("init time--->", end - start + "ms");
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
// you must install multiDex whatever tinker is installed!
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());
}
}
enableProxyApplication = false 时
public class BuglyTinkerApplication extends TinkerApplication {
/**
* 注意:这个类集成TinkerApplication类,这里面不做任何操作,所有Application的代码都会放到ApplicationLike继承类当中
* 参数解析
* 参数1:tinkerFlags 表示Tinker支持的类型 dex only、library only or all suuport,default: TINKER_ENABLE_ALL
* 参数2:delegateClassName Application代理类 这里填写你自定义的ApplicationLike
* 参数3:loaderClassName Tinker的加载器,使用默认即可
* 参数4:tinkerLoadVerifyFlag 加载dex或者lib是否验证md5,默认为false
*/
//当bugly.tinker-support.gradle中的enableProxyApplication 为 false时
public BuglyTinkerApplication() {
super(ShareConstants.TINKER_ENABLE_ALL, "com.example.buglytinker.BuglyTinkeApplicationLike",
"com.tencent.tinker.loader.TinkerLoader", false);
}
BuglyTinkeApplicationLike需要自己建,该类要继承DefaultApplicationLike(此类即便上面映射调用了,但是显示时还是灰色的)
到这里集成和代码就写的差不多了,其余的BugActivity就得由你自己去想要让其如何报错。
第四步:
打包也是很容易出错的,小编搞了三天才彻底玩明白。
APK签名这部分很重要,不然即便生成了包,也是不行的。注意build.gradle中一定要显示你签名相对应的东西,不然生成了.jks文件,但是build.gradle没也是不行。
以上步骤弄完后打基础包有2种方法:
1.在Terminal中直接输入gradlew assembleRelease
2.在AS右边Gradle中 :app - Tasks - build - assemble
运行后在app - build 中会生成 bakApk文件夹,(我这里才用的第二种方式打包所以,会把debug的也生成了,注意:应为我这里没有混淆,所以没用mapping.txt文件,若用混淆自然会生成相对应的mapping.txt文件。若是用第一种打包就没有debug)
到这里会发现生成的文件名跟tinker-support.gradle中(下面划红线名字一样,应为补丁包是根据这些为基础生成的)
其中这里的app-release.apk,是可以上架到各各平台上面去的,相当于我们Build APK是一样的
补丁包生成:
在生成前一定要先配置 baseApkDir 的值,指定用的是那个基础包。
生产方式也有两种:
1.在Terminal中直接输入gradlew tinkerPatchRelease
2.在AS右边Gradle中 :app - Tasks - tinker-support - buildTinkerPatchRelease
生成完成后outputs中会多来个patch,补丁包就在这里面,其中patch_signed_7zip.apk就是补丁包
双击补丁包你可以查看YAPATCH.MF打的补丁包是否是你指定的基础包From看看是否跟baseApkDir一样
同时在bakApk这里也会生成一个跟To一样的包文件夹。
第五步:
就是上传到Bugly中去,这时候有一个地方要注意,目标版本是自动识别的。如果上传后提示没有改版本时,是因为你那个报错的基础包安装后至少要跑一次,不然Bugly异常上报接收不到该版本的错误。
然后重启就可以。(不过到这里你们以为就完成了,其实坑才刚开始。我就是在这里跌倒了2天的。如果你们是用电脑虚拟运行的话上面的提示都有了,但是还是会报错。这喵的,我想哭啊。小编有了Mumu,AS自带的模拟器,都不行。后来我拿到真机上面一试,开发现可以了。所以小编总结这东西得到真机上面搞才可以。“若用大牛知道原因告知小弟一下”。这时一定很多人心里一定很不算吧。后来我又发现了另外一个东西叫做“全量更新”。划重点,这是每年高考都要考的。因为工作上用的热更新几乎是指这个)
补坑(全量更新):
versionCode和versionName这里一定要比之前的版本高,然后跟第四步一样打基础包的方式一样二选一
将再次生成的app-release.apk上传到全量更新上,弹框的更新说明一定要写
创建策略后,回到主界面也要去手动点击启动。启动往后并不会立刻生效,需等待几分钟然后App就能收到了
这样就搞定了在虚拟机和真机都可以更新了。(问什么这里不写代码呢,因为在上面的Application中就已经写好了全量升级状态回调
)
再讲一个知识点吧,04是一开始得基础包,58是打补丁包后再生成得,20是后来全量更新的。后面2个的baseApkDir指定的都是第一个,如果你再Bugly中即上传的了补丁包,又上传了全量更新。而你安装的是04的基础包。这时后如何你热更新了补丁包你的APP baseApkDir就变成0925-14-22-58,而你的全量更新就没效了,因为指定的baseApkDir不同了,反正同理。(实验后总结的)