工作中偶尔会遇到的场景:当一个App发布之后,突然发现了一个严重bug需要进行紧急修复,这时候公司各方就会忙得焦头烂额:重新打包App、测试、向各个应用市场和渠道换包、提示用户升级、用户下载、覆盖安装。

重点是还会有原来的版本遗留,无论你怎么提示都有人放弃治疗,不愿意升级。

如果这是一个影响公司收入或者体验影响极其不好的Bug,那去求了,老板脾气不好的话扣钱是小事,严重的就可以回家种地了。

最后最致命的是:

有时候仅仅是因为不小心写错了一行代码,就让所有部门的加班成果都付之东流,屈不屈。

还有一种剧情是研发总监把锅甩给测试团队,测试不过关,测试摊摊手说我也不是神啊,总会有漏网之鱼。锅甩过来甩过去就造成了部门、同事之间的不和谐,以后测试会提bug提到你欲哭无泪。

那能不能神不知鬼不觉在没有产生较大影响前把bug快速修复了呢?

答案肯定是可以的。

1:热更新介绍

1.1:什么是热更新?

百度百科:热更新就是动态下发代码,它可以使开发者在不发布新版本的情况下,修复bug和发布功能,避免长时间的审核等待以及多次被拒造成的成本。

热修复提出于2014年,兴起于2016年,尤其是在Instant run 问世以后,各种热修复技术相继涌出。

是一种摆脱传统发版方案直接使用补丁来更新app内容,不需要重新下载安装apk等略过一系列繁琐过程的新兴技术,目前国内部分成熟App都拥有自己的热修复技术,如:手淘、QQ、微信、阿里、美团、饿了么等。

1.2:热更新的优势。

· 无需重新发版,简单高效

· 用户无感知,无需下载新应用,代价小

· 修复成功率高,挽回用户群体

1.3:有哪些比较火的热更新方案?

· 手机淘宝:Sophix(前身是HotFix)

· 微信:Tinker

· 饿了么:Amigo

· 美团:Robust

· 阿里:AndFix

· QQ:QZone

两个表格可以不看,没什么卵用,是在网上查阅的资料,第一张图是讲Sophix热更新的,所以他说Sophix是最好的。第二张图是讲bugly热更新的,自然说Tinker好(bugly是基于Tinker的)。但是还是能看出来手机淘宝的Sophix和微信的Tinker更加好一些,两个的用户都是数亿级别。

微信的用户更加活跃频繁一些,那我们就用Tinker,也就是bugly。

2:集成官方sdk

bugly官方网站:点击打开链接

第一步:添加插件依赖

工程目录下“build.gradle”文件中添加:

dependencies标签中添加一行:

classpath "com.tencent.bugly:tinker-support:latest.release"

添加完成后如下:

第二步:集成SDK

gradle配置

步骤1

在app module的“build.gradle”文件里面的defaultConfig标签中添加:

ndk {//设置支持的SO库架构,模拟器需要‘x86’,‘armeabi-v7a’abiFilters 'armeabi' //, 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'}

步骤2

dependencies标签中添加:

compile "com.android.support:multidex:1.0.1" // 多dex配置//注释掉原有bugly的仓库
//compile 'com.tencent.bugly:crashreport:latest.release'//其中latest.release指代最新版本号,也可以指定明确的版本号,例如2.3.2
compile 'com.tencent.bugly:crashreport_upgrade:latest.release'
compile 'com.tencent.bugly:nativecrashreport:latest.release' //其中latest.release指代最新版本号,也可以指定明确的版本号,例如2.2.0

步骤3

signingConfigs标签中添加:

release {try {storeFile file("E:/jksdemo/demo.jks")//根据自己项目的密钥具体地址更改storePassword "123456"//根据自己项目的密钥密码更改keyAlias "key0"keyPassword "123456"//根据自己项目的密钥密码更改} catch (ex) {throw new InvalidUserDataException(ex.toString())}}

步骤4

buildTypes标签内release标签里面添加:

signingConfig signingConfigs.release

以上步骤操作完成后如下:

步骤5

把项目从Android视图样式切换到Project视图:

鼠标放在src文件夹上右键

New-->File

注意后缀是gradle,点击OK

就生成了名称为tinker-support.gradle的文件

然后打开它  将下面的代码全部复制进去:

apply plugin: 'com.tencent.bugly.tinker-support'def bakPath = file("${buildDir}/bakApk/")/*** 此处填写每次构建生成的基准包目录,基准包就是你第一次打的正式包*/def baseApkDir = " app-0208-15-10-00"/*** 对于插件各参数的详细解析请参考*/
tinkerSupport {// 开启tinker-support插件,默认值trueenable = true// 指定归档目录,默认值当前module的子目录tinkerautoBackupApkDir = "${bakPath}"// 是否启用覆盖tinkerPatch配置功能,默认值false// 开启后tinkerPatch配置不生效,即无需添加tinkerPatchoverrideTinkerPatchConfiguration = true// 编译补丁包时,必需指定基线版本的apk,默认值为空// 如果为空,则表示不是进行补丁包的编译// @{link tinkerPatch.oldApk }baseApk = "${bakPath}/${baseApkDir}/app-release.apk"// 对应tinker插件applyMappingbaseApkProguardMapping = "${bakPath}/${baseApkDir}/app-release-mapping.txt"// 对应tinker插件applyResourceMappingbaseApkResourceMapping = "${bakPath}/${baseApkDir}/app-release-R.txt"// 构建基准包和补丁包都要指定不同的tinkerId,并且必须保证唯一性tinkerId = "base-1.0.1"// 构建多渠道补丁时使用// buildAllFlavorsDir = "${bakPath}/${baseApkDir}"// 是否启用加固模式,默认为false.(tinker-spport 1.0.7起支持),使用加固必须打开// isProtectedApp = true// 是否开启反射Application模式enableProxyApplication = false}/*** 一般来说,我们无需对下面的参数做任何的修改* 对于各参数的详细介绍请参考:* 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 = falseuseSign = truedex {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的分配}
}

步骤6

切换到Android视图

打开build.gradle(Module:app)在里面添加依赖刚才建的插件脚本:

// 依赖插件脚本
apply from: 'tinker-support.gradle'

操作完成是这样的:

步骤7

上面的配置先告一段落,来到bugly官方网站进行登录账户

创建完成:

复制App ID

步骤8

来到项目新建一个类SampleApplicationLike如下:(别忘记更换为自己app的ID)

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) {super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);}@Overridepublic void onCreate() {super.onCreate();// 这里实现SDK初始化,appId替换成你的在Bugly平台申请的appId// 这第二个参数是Bugly平台申请的appId// 调试时,将第三个参数改为trueBugly.init(getApplication(), "5e61a00cb0", false);}@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)@Overridepublic 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);}}

然后再建个类SampleApplication代码如下:(别忘记更换为自己的SampleApplicationLike类的包名)

public class SampleApplication extends TinkerApplication {public SampleApplication() {super(ShareConstants.TINKER_ENABLE_ALL, "com.example.administrator.buglytestdemo.SampleApplicationLike","com.tencent.tinker.loader.TinkerLoader", false);}
}

步骤9

然后打开AndroidManifest.xml

application标签中添加:

android:name="com.example.administrator.buglytestdemo.SampleApplication"

(别忘记改为自己的包名)

接下来添加权限:

<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" />

然后activity配置:(必配,框架里自带的Activity,无需新建)

<activityandroid:name="com.tencent.bugly.beta.ui.BetaActivity"android:configChanges="keyboardHidden|orientation|screenSize|locale"android:theme="@android:style/Theme.Translucent" />

然后配置FileProvider

<providerandroid:name="android.support.v4.content.FileProvider"android:authorities="${applicationId}.fileProvider"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/provider_paths"/></provider>

配置完成如下:

如果你使用的第三方库也配置了同样的FileProvider, 可以通过继承FileProvider类来解决合并冲突的问题,示例如下:

<provider  android:name=".utils.BuglyFileProvider"  android:authorities="${applicationId}.fileProvider"  android:exported="false"  android:grantUriPermissions="true"  tools:replace="name,authorities,exported,grantUriPermissions">  <meta-data  android:name="android.support.FILE_PROVIDER_PATHS"  android:resource="@xml/provider_paths"  tools:replace="name,resource"/>
</provider>

这里要注意一下,FileProvider类是在support-v4包中的,检查你的工程是否引入该类库。

在res目录新建xml文件夹,创建provider_paths.xml文件如下:

provider_paths.xml代码如下:

<?xml version="1.0" encoding="utf-8"?><paths xmlns:android="http://schemas.android.com/apk/res/android"><!-- /storage/emulated/0/Download/${applicationId}/.beta/apk--><external-path name="beta_external_path" path="Download/"/><!--/storage/emulated/0/Android/data/${applicationId}/files/apk/-->
<external-path name="beta_external_files_path" path="Android/data/"/></paths>

这里配置的两个外部存储路径是升级SDK下载的文件可能存在的路径,一定要按照上面格式配置,不然可能会出现错误。

步骤10

为了避免混淆SDK,在Proguard混淆文件中增加以下配置:

-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}

如果你使用了support-v4包,你还需要配置以下混淆规则:

-keep class android.support.**{*;}

配置完成如下:

经过上面的步骤到这里配置就基本完成了。

接下来是打基准包(就是apk,每个版本的基准包要和后面的补丁一一对应的话补丁才能用)。

基准包的版本就是在下图箭头地方指定和设置的

tinkerId最好是一个唯一标识,例如git版本号、versionName等等。 如果你要测试热更新,你需要对基线版本进行联网上报。

这里强调一下,基线版本配置一个唯一的tinkerId,而这个基线版本能够应用补丁的前提是集成过热更新SDK,并启动上报过联网,这样我们后台会将这个tinkerId对应到一个目标版本,例如tinkerId = "bugly_1.0.0" 对应了一个目标版本是1.0.0,基于这个版本打的补丁包就能匹配到目标版本。

备注:tinkerId的版本号其实是根据build里面的versionName+versionCode拼接而成的。也就是

所以升级包的时候要versionCode加1,versionName根据实际项目情况决定要不要增大。

tinkerId的版本号尽量每次改为versionName+versionCode的拼接一致,如1.0.1

接下来打基准包:

完成后会是这样的:

实际应用中,请注意保存线上发布版本的基准apk包、R.txt文件,如果线上版本有bug,就可以借助我们tinker-support插件进行补丁包的生成。

基准包打成功后来到项目的路径\app\build\bakApk\最后一个文件夹里面app-release.apk就是这个基准包。然后安装在手机或者模拟器上。

3:热修复、热更新使用

接下来使用补丁修复:

根据实际情况修改自己项目中的代码或者解决一个bug。

这个地方改为和这次要打补丁的基准包一样的名称

然后Gradle如下:

完成后是这样的

项目路径\app\build\outputs\patch

这个就是补丁包。

接下来来到官网

点击创建的这个项目

点击应用升级

点击热更新-->发布新补丁

选择刚才的补丁,选择为全量设备,立即下发。

然后手机上的该版本的app会自动神不知鬼不觉的把bug修复了。

接下来试下升级apk版本:

versionCode增加1

versionName根据实际情况改变,这里就先不变了

tinkerId最好改为和versionName+versionCode拼接一样的版本号。

按照前面步骤生成基准包。

来到官网

填写标题、更新说明。点击创建策略

然后搞定。

上个之前测试的效果图片:

android热更新bugly相关推荐

  1. bugly android8.1加固,2020-09-27 Bugly Android热更新使用指南

    戳我查看 DEMO Bugly Android热更新使用指南 官方文档 视频教程 第一步:添加插件依赖 工程根目录下"build.gradle"文件中添加: buildscript ...

  2. Android热更新初探,Bugly热更新的集成和使用(让你的应用轻松具备热更新能力)

    介绍   在介绍Bugly之前,需要先向大家简单介绍下一些热更新的相关内容.当前市面的热补丁方案有很多,其中比较出名的有阿里的AndFix.美团的Robust以及QZone的超级补丁方案.但它们都存在 ...

  3. Android热更新技术的研究与实现Sophix

    所以阿里爸爸一直在进步着呢,知道技术存在问题就要去解决问题,这不,从Dexposed-->AndFix-->HotFix-->Sophix,技术是越来越成熟了. Android热更新 ...

  4. Android热更新研究与实现

    第一部分重点是将当下热门的热更新方案实现之后再研究,第二部分则是自己动手实现一个自己的热更新框架. Android热更新技术的研究与实现之研究篇 ---概念讲解--– 热更新 相关概念 这个词出现的时 ...

  5. android热更新插件,与Android热更新方案Amigo的再次接触

    Amigo 作为一个"过气"的的热修复框架,用来学习和了解一下热修复的基本原理还是很好的.本文是本系列的第三篇. 前两篇: 与Android 热更新方案Amigo的初次接触 原作者 ...

  6. android 上下偏差怎么写_详解 Android 热更新升级如何突破底层结构差异?

    知道了 native 替换方式兼容性问题的原因,我们是否有办法寻求一种新的方式,不依赖于 ROM 底层方法结构的实现而达到替换效果呢? 我们发现,这样 native 层面替换思路,其实就是替换 Art ...

  7. Android热更新

    Android热更新 组件化 组件化和模块化其实一回事,都是拆分多个 module 进行开发,组件化的叫法更偏向封装系统功能,比如统一对话框封装,网络封装等,而模块化叫法更偏向业务方面,比如登录模块等 ...

  8. android 热更新 方案,热更新-热更新app开发的两种系统方案!

    针对app开发工作人员来讲,除开要会编码,热更新也是一定要学好和把握的方法,从技术性视角而言,热更新对Android和iOS各自有不一样的系统软件方案,为了更好地让大伙儿掌握这二种系统方案的差别,今日 ...

  9. Android热更新十:自己写一个Android热修复

    很早之前就想深入的研究和学习一下热修复,由于时间的原因一直拖着,现在才执笔弄起来. Android而更新系列: Android热更新一:JAVA的类加载机制 Android热更新二:理解Java反射 ...

最新文章

  1. 【Qt】一个使用QEventLoop时,遇到的教训
  2. libcudart.so.6.5 cannot open shared object file: no such file or directory
  3. 实战2--应用EL表达式显示投票结果
  4. Linux下的samba服务配置详解
  5. InnoDB Monitors
  6. .NetCore Cap 结合 RabbitMQ 实现消息订阅
  7. [NOI2014] 起床困难综合症
  8. 函数计算搭建 Serverless Web 应用(二)- 自定义域名
  9. 夜读源码,带你探究 Go 语言的iota
  10. python round_python round()
  11. mysql和memcache 查询_使用Memcache缓存MySQL查询(转载)
  12. 【原创】CPU 100%+磁盘写满 问题排查
  13. 富士施乐m115b怎么连接电脑_富士施乐m115b驱动|富士施乐DocuPrint M115b一体机驱动下载 V1.01.00 官方版 - 比克尔下载...
  14. 闲聊历史上的配角之赵高
  15. 概率论与数理统计学习笔记——第十八讲——二元随机变量分布函数、边际分布函数及条件分布函数
  16. matlab命令行窗口显示长度设置_设置命令行窗口输出显示格式 | MATLAB format| MathWork...
  17. SPL工业智能:发现时序数据的异常
  18. 计算机毕业设计Java在线选课系统设计(系统+程序+mysql数据库+Lw文档)
  19. cron表达式实现40分钟执行一次的解决方案
  20. shader 处理cad线型 其中一小段的思路

热门文章

  1. QQ远程系统权限原因,暂时无法操作
  2. 微信小程-截取小数点的位数
  3. scp与sftp命令
  4. 南昌不翻车 Codeforces Round #571 (Div. 2) C,D
  5. 【数据分析】使用numpy实现多项式的求导以及可视化
  6. 电脑值得收藏的10个网站,知乎超100万人推荐,让你办公事半功倍,还请低调使用!!!
  7. PreScan快速入门到精通第二十八讲PreScan中常用传感器之TIS传感器
  8. 腾讯校园招聘会笔试题 难题解析
  9. Android双卡终端默认SIM卡选择流程
  10. ascii码和数字间的转换