集成腾讯bugly的热修复功能sdk步骤
首先为什么要集成bugly热修复。市面上有其他的热修复框架,为什么就用bugly?这里给出2张图大家就明白了。
引用腾讯bugly官网的一段话:
- 无需关注Tinker是如何合成补丁的
- 无需自己搭建补丁管理后台
- 无需考虑后台下发补丁策略的任何事情
- 无需考虑补丁下载合成的时机,处理后台下发的策略
- 我们提供了更加方便集成Tinker的方式
- 我们提供应用升级一站式解决方案
进入正题:接入流程主要是以下几个步骤:
- 打基准包安装并上报联网(注:填写唯一的tinkerId)
- 对基准包的bug修复(可以是Java代码变更,资源的变更)
- 修改基准包路径、填写补丁包tinkerId、mapping文件路径、resId文件路径
- 执行tinkerPatchRelease打Release版本补丁包
- 选择app/build/outputs/patch目录下的补丁包并上传(注:不要选择tinkerPatch目录下的补丁包,不然上传会有问题)
- 编辑下发补丁规则,点击立即下发
- 重启基准包,请求补丁策略(SDK会自动下载补丁并合成)
- 再次重启基准包,检验补丁应用结果
1:新建基准包工程项目(人为制造有BUG的app版本)
![](https://code.csdn.net/assets/CODE_ico.png)
- btn.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- / String str = LoadBugClass.getBugString();
- String str = BugClass.bug();
- Toast.makeText(MainActivity.this,str,Toast.LENGTH_SHORT).show();;
- }
- });
![](https://code.csdn.net/assets/CODE_ico.png)
- public class BugClass {
- public static String bug(){
- String str = null;
- int str_length = str.length();
- return "this is bug class";
- }
- }
这个可以看出点击一个按钮会报空指针异常。
2:接着就是配置相关属性和添加一个插件依赖了。
官方教程地址:点击打开链接
下面也给出我自己配置的过程。
首先在最外层的build.gradle文件中添加依赖,看下图:
其次新建sampleapplication和sampleapplicationLike两个Java类
![](https://code.csdn.net/assets/CODE_ico.png)
- package com.henry.testappbugly;
- import android.annotation.TargetApi;
- import android.app.Application;
- import android.content.Context;
- import android.content.Intent;
- import android.content.res.AssetManager;
- import android.content.res.Resources;
- import android.os.Build;
- import android.support.multidex.MultiDex;
- import com.tencent.bugly.Bugly;
- import com.tencent.bugly.beta.Beta;
- import com.tencent.tinker.loader.app.DefaultApplicationLike;
- /**
- * Created by W61 on 2016/11/29.
- */
- public class SampleApplicationLike extends DefaultApplicationLike {
- public static final String TAG = "Tinker.SampleApplicationLike";
- public SampleApplicationLike(Application application, int tinkerFlags,
- boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime,
- long applicationStartMillisTime, Intent tinkerResultIntent, Resources[] resources,
- ClassLoader[] classLoader, AssetManager[] assetManager) {
- super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime,
- applicationStartMillisTime, tinkerResultIntent, resources, classLoader,
- assetManager);
- }
- @Override
- public void onCreate() {
- super.onCreate();
- // 这里实现SDK初始化,appId替换成你的在Bugly平台申请的appId
- Bugly.init(getApplication(), "", true);
- }
- @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
- @Override
- public void onBaseContextAttached(Context base) {
- super.onBaseContextAttached(base);
- // you must install multiDex whatever tinker is installed!
- MultiDex.install(base);
- // 安装tinker
- // TinkerManager.installTinker(this); 替换成下面Bugly提供的方法
- Beta.installTinker(this);
- }
- @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
- public void registerActivityLifecycleCallback(Application.ActivityLifecycleCallbacks callbacks) {
- getApplication().registerActivityLifecycleCallbacks(callbacks);
- }
- }
![](https://code.csdn.net/assets/CODE_ico.png)
- package com.henry.testappbugly;
- import com.tencent.tinker.loader.app.TinkerApplication;
- import com.tencent.tinker.loader.shareutil.ShareConstants;
- /**
- * Created by W61 on 2016/11/29.
- */
- public class SampleApplication extends TinkerApplication {
- public SampleApplication() {
- super(ShareConstants.TINKER_ENABLE_ALL, "SampleApplicationLike所在的包名路径",
- "com.tencent.tinker.loader.TinkerLoader", false);
- }
- }
在在Androidmanifest.xml文件中配置权限及application类名
![](https://code.csdn.net/assets/CODE_ico.png)
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.henry.testappbugly">
- <application
- android:name=".SampleApplication"
- android:allowBackup="true"
- android:icon="@mipmap/ic_launcher"
- android:label="@string/app_name"
- android:supportsRtl="true"
- android:theme="@style/AppTheme">
- <activity android:name=".MainActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <!--API 24以上配置-->
- <provider
- android:name="android.support.v4.content.FileProvider"
- android:authorities="com.tencent.bugly.hotfix.fileProvider"
- android:exported="false"
- android:grantUriPermissions="true">
- <meta-data
- android:name="android.support.FILE_PROVIDER_PATHS"
- android:resource="@xml/provider_paths"/>
- </provider>
- </application>
- <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" />
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
- </manifest>
在到res目录下:
![](https://code.csdn.net/assets/CODE_ico.png)
- <?xml version="1.0" encoding="utf-8"?>
- <paths xmlns:android="http://schemas.android.com/apk/res/android">
- <!-- 这里配置的两个外部存储路径是升级SDK下载的文件可能存在的路径 -->
- <!-- /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>
在到app目录下新建:
![](https://code.csdn.net/assets/CODE_ico.png)
- # you can copy the tinker keep rule at
- # build/intermediates/tinker_intermediates/tinker_multidexkeep.pro
- -keep class com.tencent.tinker.loader.** {
- *;
- }
- -keep class com.tencent.bugly.hotfix.SampleApplication {
- *;
- }
- -keep public class * implements com.tencent.tinker.loader.app.ApplicationLifeCycle {
- *;
- }
- -keep public class * extends com.tencent.tinker.loader.TinkerLoader {
- *;
- }
- -keep public class * extends com.tencent.tinker.loader.app.TinkerApplication {
- *;
- }
- # here, it is your own keep rules.
- # you must be careful that the class name you write won't be proguard
- # but the tinker class above is OK, we have already keep for you!
然后在混淆文件.pro中添加这几句代码(bugly都有说明解释)
![](https://code.csdn.net/assets/CODE_ico.png)
- -dontwarn com.tencent.bugly.**
- -keep public class com.tencent.bugly.**{*;}
最后就是app目录下的build.gradle文件配置了:
![](https://code.csdn.net/assets/CODE_ico.png)
- apply plugin: 'com.android.application'
- dependencies {
- compile fileTree(include: ['*.jar'], dir: 'libs')
- compile 'com.android.support:appcompat-v7:24.1.1'
- // 多dex配置
- compile "com.android.support:multidex:1.0.1"
- // 集成Bugly热更新aar(灰度时使用方式)
- // compile(name: 'bugly_crashreport_upgrade-1.2.0', ext: 'aar')
- compile "com.tencent.bugly:crashreport_upgrade:1.2.0"
- }
- android {
- compileSdkVersion 23
- buildToolsVersion "23.0.2"
- // 编译选项
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_7
- targetCompatibility JavaVersion.VERSION_1_7
- }
- // recommend
- dexOptions {
- jumboMode = true
- }
- // 签名配置
- signingConfigs {
- // 签名配置
- 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")
- }
- }
- }
- defaultConfig {
- applicationId "com.henry.testappbugly"
- minSdkVersion 14
- targetSdkVersion 23
- versionCode 2
- versionName "2.0"
- // 开启multidex
- multiDexEnabled true
- // 以Proguard的方式手动加入要放到Main.dex中的类
- multiDexKeepProguard file("keep_in_main_dex.txt")
- }
- 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'
- }
- }
- lintOptions {
- checkReleaseBuilds false
- abortOnError false
- }
- }
- def gitSha() {
- try {
- String gitRev = 'git rev-parse --short HEAD'.execute(null, project.rootDir).text.trim()
- if (gitRev == null) {
- throw new GradleException("can't get git rev, you should add git to system path or just input test value, such as 'testTinkerId'")
- }
- return gitRev
- } catch (Exception e) {
- throw new GradleException("can't get git rev, you should add git to system path or just input test value, such as 'testTinkerId'")
- }
- }
- def bakPath = file("${buildDir}/bakApk/")
- /**
- * you can use assembleRelease to build you base apk
- * use tinkerPatchRelease -POLD_APK= -PAPPLY_MAPPING= -PAPPLY_RESOURCE= to build patch
- * add apk from the build/bakApk
- */
- ext {
- // for some reason, you may want to ignore tinkerBuild, such as instant run debug build?
- tinkerEnabled = true
- // for normal build
- // old apk file to build patch apk
- tinkerOldApkPath = "${bakPath}/app-release-1201-09-46-25.apk"
- // proguard mapping file to build patch apk
- tinkerApplyMappingPath = "${bakPath}/app-release-1201-09-46-25-mapping.txt"
- // resource R.txt to build patch apk, must input if there is resource changed
- tinkerApplyResourcePath = "${bakPath}/app-release-1201-09-46-25-R.txt"
- // only use for build all flavor, if not, just ignore this field
- tinkerBuildFlavorDirectory = "${bakPath}/app-release-1201-09-46-25"
- }
- def getOldApkPath() {
- return hasProperty("OLD_APK") ? OLD_APK : ext.tinkerOldApkPath
- }
- def getApplyMappingPath() {
- return hasProperty("APPLY_MAPPING") ? APPLY_MAPPING : ext.tinkerApplyMappingPath
- }
- def getApplyResourceMappingPath() {
- return hasProperty("APPLY_RESOURCE") ? APPLY_RESOURCE : ext.tinkerApplyResourcePath
- }
- def getTinkerIdValue() {
- return hasProperty("TINKER_ID") ? TINKER_ID : gitSha()
- }
- def buildWithTinker() {
- return hasProperty("TINKER_ENABLE") ? TINKER_ENABLE : ext.tinkerEnabled
- }
- def getTinkerBuildFlavorDirectory() {
- return ext.tinkerBuildFlavorDirectory
- }
- /**
- * 更多Tinker插件详细的配置,参考:https://github.com/Tencent/tinker/wiki
- */
- if (buildWithTinker()) {
- // 依赖tinker插件
- apply plugin: 'com.tencent.tinker.patch'
- apply plugin: 'com.tencent.bugly.tinker-support'
- tinkerSupport {
- }
- // 全局信息相关配置项
- tinkerPatch {
- oldApk = getOldApkPath() //必选, 基准包路径
- ignoreWarning = false // 可选,默认false
- useSign = true // 可选,默认true, 验证基准apk和patch签名是否一致
- // 编译相关配置项
- buildConfig {
- applyMapping = getApplyMappingPath() // 可选,设置mapping文件,建议保持旧apk的proguard混淆方式
- applyResourceMapping = getApplyResourceMappingPath() // 可选,设置R.txt文件,通过旧apk文件保持ResId的分配
- tinkerId = "可以是签名版本号字符串等等比如:assdhfkdshfksdhfuksfhuk" // 必选,默认为null
- }
- // dex相关配置项
- dex {
- dexMode = "jar" // 可选,默认为jar
- usePreGeneratedPatchDex = true // 可选,默认为false
- pattern = ["classes*.dex",
- "assets/secondary-dex-?.jar"]
- // 必选
- loader = ["com.tencent.tinker.loader.*",
- "SampleApplication所在的全路径",
- ]
- }
- // lib相关的配置项
- lib {
- pattern = ["lib/armeabi/*.so"]
- }
- // res相关的配置项
- res {
- pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
- ignoreChange = ["assets/sample_meta.txt"]
- largeModSize = 100
- }
- // 用于生成补丁包中的'package_meta.txt'文件
- packageConfig {
- configField("patchMessage", "tinker is sample to use")
- configField("platform", "all")
- configField("patchVersion", "1.0")
- }
- // 7zip路径配置项,执行前提是useSign为true
- sevenZip {
- zipArtifact = "com.tencent.mm:SevenZip:1.1.10" // optional
- // path = "/usr/local/bin/7za" // optional
- }
- }
- List<String> flavors = new ArrayList<>();
- project.android.productFlavors.each { flavor ->
- flavors.add(flavor.name)
- }
- boolean hasFlavors = flavors.size() > 0
- /**
- * bak apk and mapping
- */
- android.applicationVariants.all { variant ->
- /**
- * task type, you want to bak
- */
- def taskName = variant.name
- def date = new Date().format("MMdd-HH-mm-ss")
- tasks.all {
- if ("assemble${taskName.capitalize()}".equalsIgnoreCase(it.name)) {
- it.doLast {
- copy {
- def fileNamePrefix = "${project.name}-${variant.baseName}"
- def newFileNamePrefix = hasFlavors ? "${fileNamePrefix}" : "${fileNamePrefix}-${date}"
- def destPath = hasFlavors ? file("${bakPath}/${project.name}-${date}/${variant.flavorName}") : bakPath
- from variant.outputs.outputFile
- into destPath
- rename { String fileName ->
- fileName.replace("${fileNamePrefix}.apk", "${newFileNamePrefix}.apk")
- }
- from "${buildDir}/outputs/mapping/${variant.dirName}/mapping.txt"
- into destPath
- rename { String fileName ->
- fileName.replace("mapping.txt", "${newFileNamePrefix}-mapping.txt")
- }
- from "${buildDir}/intermediates/symbols/${variant.dirName}/R.txt"
- into destPath
- rename { String fileName ->
- fileName.replace("R.txt", "${newFileNamePrefix}-R.txt")
- }
- }
- }
- }
- }
- }
- project.afterEvaluate {
- //sample use for build all flavor for one time
- if (hasFlavors) {
- task(tinkerPatchAllFlavorRelease) {
- group = 'tinker'
- def originOldPath = getTinkerBuildFlavorDirectory()
- for (String flavor : flavors) {
- def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Release")
- dependsOn tinkerTask
- def preAssembleTask = tasks.getByName("process${flavor.capitalize()}ReleaseManifest")
- preAssembleTask.doFirst {
- String flavorName = preAssembleTask.name.substring(7, 8).toLowerCase() + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 15)
- project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release.apk"
- project.tinkerPatch.buildConfig.applyMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release-mapping.txt"
- project.tinkerPatch.buildConfig.applyResourceMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release-R.txt"
- }
- }
- }
- task(tinkerPatchAllFlavorDebug) {
- group = 'tinker'
- def originOldPath = getTinkerBuildFlavorDirectory()
- for (String flavor : flavors) {
- def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Debug")
- dependsOn tinkerTask
- def preAssembleTask = tasks.getByName("process${flavor.capitalize()}DebugManifest")
- preAssembleTask.doFirst {
- String flavorName = preAssembleTask.name.substring(7, 8).toLowerCase() + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 13)
- project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug.apk"
- project.tinkerPatch.buildConfig.applyMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug-mapping.txt"
- project.tinkerPatch.buildConfig.applyResourceMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug-R.txt"
- }
- }
- }
- }
- }
- }
最后run as生成有bug的基准包app
在去腾讯bugly官网将这个基准包上传上去即可。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
接下来,制作补丁包。
由于刚才点击按钮报空指针,下面将代码稍做改动如下:
![](https://code.csdn.net/assets/CODE_ico.png)
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- btn = (Button) findViewById(R.id.btn);
- btn.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- String str = LoadBugClass.getBugString();
- // String str = BugClass.bug();
- Toast.makeText(MainActivity.this,str,Toast.LENGTH_SHORT).show();;
- }
- });
- }
![](https://code.csdn.net/assets/CODE_ico.png)
- public class LoadBugClass {
- /**
- * 获取bug字符串.
- *
- * @return 返回bug字符串
- */
- public static String getBugString() {
- // BugClass bugClass = new BugClass();
- return "iS OK";
- }
- }
这样点击按钮就会弹出is ok了不会报错。
修改配置文件:
这里注意点,补丁包是基于基准包所生成的patch文件并不是版本升级,所以此处补丁包不需要修改versioncode,versionname
然后双击下图中所指地方:
稍等片刻就会出现下图中类容:
其中的patch_signed_7zip.apk就是补丁包了。将这个补丁包上传到腾讯bugly即可。
注意:上传完补丁包点击了立即下发,就需要重新启动基准包策略。从有bug版本的app到修复有一个时间差的。估计1到2分钟左右才能看到效果。
附上集成过程中可能遇到的坑解决办法地址:点击打开链接
最后附上自己写的demo地址:点击打开链接
集成腾讯bugly的热修复功能sdk步骤相关推荐
- 热修复——Bugly让热修复变得如此简单
一.简述 在上一篇<热修复--Tinker的集成与使用>中,根据Tinker官方Wiki集成了Tinker,但那仅仅只是本地集成,有一个重要的问题没有解决,那就是补丁从服务器下发到用户手机 ...
- Android-第三方开源框架:Bugly让热修复变得如此简单
作者:GitLqr 纸上说来终觉浅,时间比较充裕的小伙伴建议去B站观看视频讲解:Android第三方开源库系列-热修复框架使用.原理及项目实战(已完结) 一.简述 在上一篇<热修复--Tinke ...
- 如何使用阿里百川hotfix热修复功能(三)
这章我们进行patch补丁操作,参考 : 如何使用阿里百川hotfix热修复功能(一) 如何使用阿里百川hotfix热修复功能(二) 1.下载打包工具 patch补丁包生成需要使用到打补丁工具BCF ...
- Java集成腾讯云音视频录制功能
Java集成腾讯云音视频录制功能 为什么要实现音视频录制功能 因为我们做的是一个医院的项目,医生和患者可能进行视频通话和语音通话,为了保证通话的质量以及后续的问题, 我们就需要进行音视频录制,以便后续 ...
- Bugly 之热修复学习
bugly 官网:https://bugly.qq.com/v2/index 注册流程我就不写了,直接写集成步骤: 1) 在 Projet 的build.gradle 里面 导入classPtah: ...
- 安卓开发腾讯Bugly热修复集成和使用思路
文章目录 一,官方集成 一.获取App ID 二.添加插件依赖 三.集成SDK 四.配置Tinker 1.overrideTinkerPatchConfiguration 2.baseApkDir 3 ...
- 一步步手动实现热修复(一)-dex文件的生成与加载
*本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 热修复技术自从QQ空间团队搞出来之后便渐渐趋于成熟. 我们这个系列主要介绍如何一步步手动实现基本的热修复功能,无需使用第三方框架. ...
- 深入解析阿里Android热修复技术原理
前言:本文框架 什么是热修复? 热修复框架分类 技术原理及特点 Tinker框架解析 各框架对比图 总结 通过阅读本文,你会对热修复技术有更深的认知,本文会列出各类框架的优缺点以及技术原理,文章末尾简 ...
- Android 热修复之DexPatch 介绍
简介:Android 热修复之DexPatch 介绍 1. 方案介绍 为了解决Native模块上线后的问题,mPaas[1] 提供了热修复功能,实现不发布客户端apk场景下的热修复.目前Android ...
最新文章
- 最全Pycharm教程(43)——Pycharm扩展功能之UML类图使用 代码结构
- 设置VSCode自动保存
- Linux进程管理: 多进程编程
- 代码详解 | 用Pytorch训练快速神经网络的9个技巧
- mysql unix 安装教程_在UNIX系统下安装MySQL_MySQL
- 经典php代码,10个非常经典的php代码片段.doc
- 64 bit Ubuntu support 32 bit binary
- stp协议c语言,STP(生成树协议)
- find命令---Linux学习笔记
- 外刊评终极平板电脑十大功能:防眩目屏幕在列
- 探讨C#中字符串的加密
- 第一季5:Hi3518EV200的环境搭建
- Html Picture
- 关于ng-class中添加多个样式类的解决方案
- [Swust OJ 632]--集合运算(set容器)
- [转载]VHDL的testbench的编写
- webWMS开发过程记录(三)- 需求分析(略)
- Layer 提示框tips使用(批量提示)
- 北斗导航 | BDS RTK高精度定位算法在形变检测中的应用(算法原理讲解)
- 华为EC6108V9C/ E6108V9强刷固件及教程