前言
作为一个开发者,我想最烦恼的事莫过于每一轮新需求开发完毕,需求提测后,测试列出的一系列bug清单了吧,哈哈,想想就头疼,然后就是排查bug,然后还得兼顾新的需求,最郁闷的是可能产品及测试把一个你完全没有思路的问题作为比解的bug时,可能心中瞬间数十万头马拉戈壁神兽奔腾而过吧。头疼归头疼,问题还是的解决不是,不然问题堆积越来越多,新需求越来越难开发,特别是对于小公司,往往只有一两个开发,遇到问题往往也讨论不出更有价值的解决方案。
对此我深有体会,前面有两年都在小公司做开发,都是那种规模特别小的公司,开发只有自己一个人。遇到不能解决的问题,公司虽然偶尔能请到那么一两个外援来帮忙辅助分析问题。但是往往并非有了解决方法就一定能解决问题,也有可能会引入一个更深层次的问题。总所周知,一栋大楼的高度往往是由其基础决定的,基础越好越牢固,上层建筑就越容易实现,同理想要开发一个好的程序,也需要牢固的基础,而这个基础正是程序的架构,而程序架构越好越容易扩展新的功能,对此作为一个android程序员,我觉得这正式我目前所奋斗的目标——架构师,我认为一个架构师不但要提供程序开发过程中遇到各种问题的解决方案,而且更应该能够预料,并阻止程序在各个阶段可能会出现的问题。而对于一个已经开发完成的app,我想不管是开发,测试,产品经理甚至客户,我想app的稳定性及各种性能的问题,都是大家关注的焦点。对此,这边文章主要就这两个问题就行讨论,对于其他的优化,如apk大小,安全等后续总结
Android稳定性优化
我想,在一个老年人都开始玩微信的年代,大家应该都在使用智能手机了,而且可能每天都有四五个小时以上的时间都在盯着手机屏幕,可能你在玩游戏,看视频,也可能你正在使用某款软件购物,如果你的游戏或者视频正在非常激情的一个片段,也或者购物马上活动时间结束了,但是你的app突然出现了闪退的象限,此时可能砸手机的冲动都有了。冷静下来一想,和我手机有毛关系呀,都是那靠谱的软件都问题,然后一气之下就把软件给卸载了。因此,崩溃是一个非常严重的问题,一个崩溃可能导致损失大量的客户。也因此,崩溃率的问题,是一个好的应用必须严正以待的问题,在android中导致应用崩溃主要有三种情况,首先是java代码抛出的各种异常,然后是native方法逻辑出错时抛出的异常,最后是ANR
java代码的异常处理
java程序的异常处理相对于native异常及anr相对来说容易许多,其关键在于我们的开发同学能够复现该问题,并且抓到崩溃时的堆栈信息,有了崩溃的堆栈信息,剩余的就是分析处理异常了,此时即使没有分析出问题原因,我们也可以通过try catch来吧异常给处理掉,虽然这种方法治标不治本,但是却能及时解决问题。
综合以上分析,java代码的崩溃优化困难在于如何复现崩溃场景、收集崩溃日志及如何暴露更多的crash
1.如何复现崩溃场景
如果你是在小公司做开发,你可能听过测试说过这样的话,我之前在某某页面出现了一个bug,你处理一下,但是你让他复现一下场景,他却说我忘记当时怎么操作出现这个问题的了。场景一度十分尴尬,而此时你的老版或者产品经理也来附和,我也遇到过这个问题,但是就是没法复现,开发通过心中十万只羊驼奔腾而过。对于小公司的测试来说,我想发现问题是他们工作主要目标,但是如何帮忙开发解决问题,有时好像并不那么重要,特别是你的领导也这么认为的时候,我认为身为开发已经被孤立了。在大公司待了两年后的我,深刻的明白了,测试用例的重要性,产品经理没下发一个需求,开发梳理需求并进行开发工作,而测试则梳理后整理相应的测试用例问题,然后每次提测的时候,测试对照着完善的测试用例进行测试,这样即保证了测试质量,也能及时记录异常场景,使得开发复现场景变得简单
2.如何收集崩溃日志
对于崩溃日子的收集,现在有很多线上统计方案,不过可以自己收集然后上传至自己的服务器,收集方法较为简单可参考 文章
3.如何暴露更多可能崩溃的点
对于如何暴露出程序未知的crash,我想monkey是一个比较好的工具,它是Android内置的一个自动化测试的工具,往往能为我们发现很多不容易发现的问题,而且只需要一句脚本就可以实现测试和日志的收集,使用起来非常的方便,但是他的优点和他的缺点一样的明显,因为它是通过发送系统的随机事件来实现自动化的,因此有非常大的不确定性,因此只能作为测试的一个辅助工具。
native方法的异常处理
ANR
什么是anr?
anr全称Application Not Responding,即应用无响应,android一般在如下情况下会发生ANR
InputDispatching Timeout:5秒内无法响应屏幕触摸事件或键盘输入事件。
BroadcastQueue Timeout :在执行前台广播(BroadcastReceiver)的onReceive()函数时10秒没有处理完成,后台为60秒。
Service Timeout :前台服务20秒内,后台服务在200秒内没有执行完毕。
ContentProvider Timeout :ContentProvider的publish在10s内没进行完。
如何处理anr?
anr发生的更原因是因为主线程的阻塞到达一定时间阈值导致,因此其分析方法可参考卡顿的分析方法,主要是对其cpu数据已经现场的堆栈信息进行分析处理。我们可以通过anr日志来进行分析处理,anr日志路径为/data/anr/traces.txt
android性能优化
android性能的问题,在客户体验上主要有几个方面:启动速度,反应速度,流畅性,耗电性等,而除了这些会在使用中对客户有一定程度影响外apk大小也是客户比较关注的问题,对此我们后面再进行总结,这里我们先总结android各种性能优化的方向。
1.启动速度
启动速度的问题,可能不是对于每一个开发者都会遇到,但是它的重要性也是毋庸置疑的,曾经我接手了一个别人开发好的项目,我负责修复bug及开发新需求,就遇到过一个启动app时长达3s灰屏问题,现在想想也是非常的可怕,居然能达到如此程度。当时对于启动速度还没有什么概念,甚至一度被老板怼的无言以对,所以为了以后能在老板面前理直气壮的分析问题,启动速度优化也是一门android开发者的必修课呀。
在对启动速度进行优化前,我们首先的知道影响启动速度的因素有哪些以及启动过程中系统都干了啥,明白了这两个问题,我想优化启动速度方案各位就已经了然于胸了。首先Android中的动分为冷启动和热启动,其中最主要的区别在于冷启动的过程中需要先创建application,而热启动表示application已经存在的情况下启动的时候只需要创建activity对象,冷启动和热启动完成的标志是启动的activity的onCreate方法回调结束。因此application的onCreate及Activity的onCreate方法都不能进行耗时操作,否则都会印象启动速度,而activity的绘制效率以及Activity的主题也都是影响启动速度的愿意,具体如何影响我们后续在讨论。
2.反应速度
打开一个页面后好长事件才能显示出内容,我想大家可能也遇到过这样的问题,而对于这个问题,我觉得在我们开发者看来就因该是一个网络优化和线程优化的问题,我们都知道线程的开启时很浪费资源的,因此线程的管理都应该优先考虑线程池,而对于网络,也往往是优化其请求时的线程策略。
3.流畅性
对于app的流畅性,这是一个错综复杂的问题了,可能导致app卡顿不流畅的问题有代码,内存,IO,CPU等。
当然作为一个开发者,有时可能并不会有人理解需要解决这么一个问题,到底有多困难,不写出这样问题的人到底存不存在。当然又能改进提高的地方,我相信作为一个开发者,大家都是很愿意去改进的,因此又在大的困难我们不也还得继续前进,去努力掌握哪些我们之前不曾掌握的东西,我也坚信也只有这样我们才会变得越来越好,不然和咸鱼又有什么区别呢,哈哈。
虽然卡顿的问题错综复杂,但是最终都会体现到CPU时间里,因此学会分析CPU时间才是我们能进行卡顿优化的第一步呢。
CPU的问题体现在什么地方呢?
1.CPU资源浪费
主要体现使用有不合理的数据结构,不合理的算法,选择不合理的基础类型(能使用int就不要使用long,能使用float不要使用double),图片没使用缓存策列,增加了解码次数等
2.CPU资源抢占
CPU的资源那么一点,需要不断的去轮询给不同的线程使用,因此线程越多越烦忙,主线程能够分配到的资源就越少,特别在android6.0前(没有renderThread),就更容易导致卡顿,因此有一个良好的线程策略永远也都是一个好程序的基础。特别注意的是在音视频相关的app中,解码一般都是有硬性要求的,因此解码线程也不能被抢占资源,是我们需要保障其资源优先分配的对象。
3.CPU资源利用率低
IO操作,sleep和锁都是导致cpu使用率低下的问题。对于IO和sleep等有时我们无法避免,但是对于锁,我们需要尽量保持锁的粒度,从而减小锁等待的时间。
4.耗电性
使用过iPhone的人应该都知道,iPhone相比于android手机,有一个致命弱点,那就是续航能力,这可能也是一部分更喜欢iPhone但是却不愿再次入手新iPhone的原因吧。因此对于一个Android开发者,耗电性能的优化,也是App性能优化的一个重点。对此我们也应该先了解什么情况下会导致电量的快速消耗,耗电的根本原因,就是直接或间接的过量使用各种硬件资源,如:GPS,屏幕,移动网络,蓝牙,摄像头,CPU等。这些资源如果是在客户打开app的情况下使用,客户对于消耗有个预期值,也不至于客户对于消耗的不满,但是即使是前台使用也不能过于泛滥,否则即使有再好的脾气的人也会暴走呢,不过对于大部分情况下,我们更关注后台情况下的消耗。因此在后台情况下我们能不使用的硬件资源尽量都要释放掉,如不能释放掉也一定的限制使用。关于更多耗电优化内容,请参考博客