APP上线后,如何定位问题呢?接入Bugly啊!
定位了问题后,如何修复呢?接入Bugly啊!
众所周知,Bugly使用的热更新框架是Tinker,毕竟是一家人嘛,接入Bugly定位问题及热更新都没什么好说的,也都不难,只需跟着官方文档一步步接入就OK,而且Bugly已经提供了相当清晰简单的管理后台,操作起来方便快捷,对开发者非常友好,下面我们来说说期间遇到的相关问题:
1、开发运行的问题
注意!这个真是属于开发中的问题,打包上线却不存在什么问题,我们先来看一个截图感受一下: 测试运行时生成的文件夹这个是什么玩意呢?这个就是接入了Bugly的热更新后,你每运行一次就生成一个这样的文件夹,里面有一个debug的apk包,我们写代码的一天运行个几十次很正常的,这样下去那还得了,当然,你可以没过段时间去删一次,只是这样会不会笨了一点呢?
不行,不能忍!必须找到解决的方法!
我们最后在官方文档找到了解决办法,相关说明如下:
Q: 日常调试需要使用instant run,怎么关闭tinker
A:这里分两种情况:
使用反射Application方式接入:可以直接在build.gradle中将apply from: 'tinker-support.gradle'注释掉。
改造Application方式接入:先将tinkerSupport中overrideTinkerPatchConfiguration设置为false 修改成将tinkerSupport中enable设置为false。
上面这一段是来着Bugly的官方文档,我直接copy过来的,我用的是第二种方式:改造Application方式(说句实话,官方文档后面这句话我怎么读都不通顺,有点尬,幸好意思还是知道的)。
如果按上面这么改能解决问题的话,我就也不会写这一点了O(∩_∩)O
你按上面进行设置后,会发现gradle直接报了下面这样的错误:
Cause: tinkerId is not set!!!
这就奇怪了,明明tinkerSupport中有设置tinkerId的,怎么还会报这个错误呢?
后来,找了一圈,发现下来tinkerPatch中也有一个tinkerId,而且被注释掉的(官网copy的gradle配置),那么应该是这里了,把这一行注释放开,重新刷新gradle文件,这次总算没报错了,运行了一下代码,发现不再生成上面的文件夹了,至此问题算是解决。
我们总结一下,在开发中暂时关闭tinker的步骤为:
1)tinkerSupport中overrideTinkerPatchConfiguration设置为false
2)tinkerSupport中enable设置为false
3)tinkerPatch中tinkerId放开注释
2、Application之相关问题
我们都知道接入Tinker有两种方式:
1)使用反射Application方式接入
2)改造Application方式接入(官方推荐,兼容性,接入相对麻烦一点)
笔者当然选择的是官方推荐的,改造Application方式,按照文档进行接入,然后修改了部分资源文件,发布了补丁,进行了多次测试,知道了相应的操作流程及规则,觉得这玩意也忒简单,没什么好说的,然而,打脸总是来得很快的···
那天我对部分功能做了一些优化,既然接入了热更新,当然想试试水咯,就打了补丁包发布上线,结果补丁下发成功,但在模拟器上却崩得一塌糊涂,在夜神模拟器上直接崩溃,在AndroidStudio内置的谷歌模拟器上却停在启动图页面不动了,这可吓死宝宝了,兄die,What's wrong?
查看了后台的bugly日志,报的是个空指针异常,但查了代码,不应该啊,补丁包都没更改过那里!我怀疑是模拟器的原因,毕竟模拟器的CPU架构是x86,而一般手机则不是,于是又拿起自己的手机和公司的测试机进行补丁测试,这里最坑的却是测试通过,没有任何异常现象,补丁也生效了,我就是这样被坑的,理所当然的把问题归根于模拟器的原因,自以为是的把后台bugly的崩溃信息状态改了,觉得问题已解决,然而,打脸随后就到···
第二天早上,我看到后台100%的崩溃率,报错的地方就是之前被我关闭的崩溃信息,而且崩溃的机型显示为:系统Android7.0,CPU架构arm64-v8a,这可是真实的手机了,查看补丁下发情况,全部都是补丁下发成功,但没有激活成功,怎一个尴尬聊得?这脸打得啪啪啪响,吓得我赶紧把所有补丁包都撤回了,所幸影响用户极少极少,不然真够我喝一壶的。
既然有了问题,当然得解决了,我仔细查看了崩溃日志,进行相关代码的追踪,最后确定问题在于Application初始化时就埋下的坑,我在Application中保存了一个单例的context,之后报的空指针就是因为context为null引起的。既然确定了是Application的问题,我又仔细的把bugly的官方文档看了一遍,发现原来这个坑真是我自己粗心大意埋下的,我们看一下官方文档的说明:
自定义Application.png注意上面红圈,我们以前自定义了Application后,总会在里面做一些初始化操作啊,甚至提供一些全局变量和方法的获取啊,但上面官方文档已经明确说明了,不能再在Application中做任何操作了,相应的操作得转移到代理类ApplicationLike中去,我们再看一下相关说明:
注意:tinker需要你开启MultiDex,你需要在dependencies中进行配置compile "com.android.support:multidex:1.0.1"才可以使用MultiDex.install方法; SampleApplicationLike这个类是Application的代理类,以前所有在Application的实现必须要全部拷贝到这里,在onCreate方法调用SDK的初始化方法,在onBaseContextAttached中调用Beta.installTinker(this);。
这段说明是写在“自定义ApplicationLike”下面的,我们都知道,使用改造Application方式,必须创建一个ApplicationLike作为一个代理类,注意上面官方说明中笔者用粗体显示的地方:“以前所有在Application的实现必须要全部拷贝到这里”。
好啦,现在问题及解决方案已经很清楚了,笔者按官方说明对代码进行更改,心想再打个补丁包进行测试,结果打补丁失败,报错如下:
Warning:ignoreWarning is false, but we found some loader class has been changed in new primary dex. Such these changes will not take effect!! related classes: {Lxxx/xxx/xxx/MyApplication;}
意思很明显了,也就是说更改了Application中的内容,补丁无法生效了,这应该是Tinker做的限制吧,这样一来,可验证不了啦,但为了使下次上线补丁靠谱点(毕竟已经打脸一次了),还是应该做相应的测试的:
1)fork一个分支,将代码回滚到上次发版之后
2)按上面要求修改自定义的Application和ApplicationLike
3)打基础包,进行APP测试
4)copy本次相关的优化代码进行新增和覆盖
5)打对应基础包的补丁包
6)上传补丁包,进行APP测试
麻烦是麻烦点,但想要验证结果,就不应该怕麻烦!
这里还有一点需要说明的:bugly支持补丁下发范围的选择:开发设备、全量设备和自定义
按理来说,我以为开发设备是可以通过管理后台进行添加和管理的,结果却不是,而是通过代码进行控制的:
Bugly.setIsDevelopmentDevice(getApplicationContext(), true);//开发设备
Bugly.setIsDevelopmentDevice(getApplicationContext(), false);//非开发设备
换句话说,是否为开发设备,得自己在代码进行区分并调用上面相应的接口,尬!
考虑到开发设备区分的不易和测试的方便,我决定用第三种:自定义
自定义下发范围允许我们设置下发补丁的个数及系统版本,这就很nice了,为了不影响线上的用户,笔者在一个夜黑风高的晚上,设置了下发补丁数为2个,进行了补丁下发,分别在夜神模拟器和谷歌模拟器进行测试,毕竟这两家伙之前都出问题的,结果如下:
至此,该问题总算解决了!
3、补丁生效及撤回的问题
1)测试过的都知道,补丁包要正式生效,需要一到两次完全退出APP才可以,这算是一个硬伤吧!如果真的有一个崩溃的bug需要紧急修复,也不知用户何时能完全退出APP两次,还有如果真的崩了两次的话,你敢保证用户不会卸载了你家的APP吗?
2)补丁是支持撤回的,你会不会好奇Tinker是如何做到的?请看下图:
当补丁下发成功后,你可以在这个路径下查看到下发的补丁包:data/data/你的包名/app_dex/
当补丁被撤回后,你打开相应的目录,发现补丁包消失了,很显然是被删除了,这也相当符合热更新的原理。
综上所述,个人认为APP使用Tinker的定位在于:小调整、优化、小功能新增、小bug修复等等,但如果是致命bug或大功能模块的新增、修改的话,建议还是老老实实发版吧!
最后,补两张日常用到的打包方法:
打基础包 打补丁包
网友评论