APP性能优化-Memory
APP性能优化-稳定性(crash率)
APP性能优化-包体压缩
APP性能优化-CPU
APP性能优化-UI
APP性能优化-流畅度
前言
稳定性是衡量APP最重要的指标之一,当DAU达到百万、千万级,就算crash率只有0.1%崩溃次数也是不能接受的,维持低崩溃率是每个工程师应有的职责。
异常分类
在code的过程中,引发程序crash的原因大体分为两类:Error
、Exception
,他们都继承Throwable
Error
无法处理的错误,表示程序碰到较为严重的问题。大多数错误与上层的应用程序无直接关系。而是底层系统或是JVM(Java虚拟机)出现的问题。例如Java虚拟机运行错误(VirtualMachineError),当JVM内存空间不足时,将出现OutOfMemoryError。当Error出现后,上层程序是无法控制的,所以,出现这类错误时,本质上也是不应该试图去处理它所发生的异常状况。
Exception
Exception主要分为两大类Unchecked Exception(Runtime Exception)
以及Checked Exception(非Runtime Exception)
Unchecked 与Checked 区别在于对于CheckedException,必须添加try…catch…捕获异常、或者throw 抛出异常并处理
//Checked Exception手动抛出异常
private void openFile() throws FileNotFoundException {
File file = new File("");
FileInputStream fileInputStream = new FileInputStream(file);
}
//Checked Exception通过try...catch捕获
private void openFile() {
File file = new File("");
try {
FileInputStream fileInputStream = new FileInputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
//处理异常
}
}
UncheckedException,非强制性处理,在多线程环境下建议处理该异常,不然容易出现crash
//选择性异常处理,不强制
try {
List<String> list = new ArrayList<>();
list.get(1);
} catch (IndexOutOfBoundsException e) {
//处理异常
}
public E get(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
return (E) elementData[index];
}
随着APP高速迭代,维持低crash率是一场持久战,需要坚持。总结一下个人经验主要通过以下方法来维持低crash率
1.预防为主
2.针对性处理,由点到面
3.兜底、止损策略
1.预防为主
保证每个版本在发布之前都严格进行以下步骤
Lint扫描
:Lint从多个维度以及可定义范围对工程进行扫描,可以对布局的合理性、代码结构、NPE、变量合理性等给出合理建议,如果能在每个版本发布之前都能处理掉Lint给出的扫描结果,那APP Crash率会有很大一部分提升
跑Monkey
:经常能跑出APP的性能瓶颈,比如OOM、ANR等,及时去处理这些东西以免上线之后带来不必要的麻烦
2.针对性处理,由点到面
服务端脏数据处理
:拿Rxjava来说,可以定义拦截器统一处理服务端数据格式、数据异常等,保证在回调给UI线程时是我们想要的数据。在使用服务端返回的数据时层级较多,在使用每一层时我们都要进行null判断,采取no believe server
方式,服务端是不靠谱的,每一层都可能出现数据错误,不能因为数据问题导致APP Crash,这是最基本的。
数据格式问题
:在页面功能复杂的时候,前端开发大部分不会手动去写数据结构,基本都是通过抓包工具抓取JSON然后再通过GsonFormat工具格式化得出数据结构。假设有个id字段是自增型的,在第一个版本数据量不多的时候后端返回的数据并未超过int范围,这时候Format出来的id将为int型(这里是个坑),当后续数据量变大之后id返回的值将超过int型,数据转换的时候必然会出现内存溢出
导致线上大批量crash。对于类似这种可变数据长度,一定要注意。
ROM兼容性问题
:Android是开源的,各个厂商都会修改源ROM变成有自己的ROM,这将导致平台兼容性问题。在某个版本,我们改变了APP日志投递策略为5分钟投递一次,通过Rxjava并发投递,上线之后在后台监控到很多华为手机上抛出的OOM问题,最终定位出是因为华为ROM对线程数量有限制
,因为RxJava线程池是自增、无上限的线程操作突破线程上线是情理之中,后来我们通过自定义RxJava的线程池
来解决这个问题
处理Unchecked Exception
:在使用系统API或者第三方SDK时,一定要熟知文档,处理API可能手动抛出的Unchecked Exception
多线程引发的Exception
:最常见的就是多线程同时操作List或者HashMap导致NPE问题,当确定数据结构会出现多个线程同时引用操作,尽量少用合理的数据结构去避免这个问题,比如说用Vector或者ConCurrentHashMap去替换保证线程安全性
3.兜底、止损策略
1、2两个步骤都不难,但贵在坚持,大部分APP是坚持不了的,还有就是 某些特定场景下才会导致的crash。有些crash会导致严重事故,这个不及时自损我们是承担不起的。两个方案:
热修复
:腾讯Tinker,阿里AndFix,都是不错的方案,功能效果不一样,各有利弊,选择合适自己业务的框架就行
拦截器、降级策略
:现在稍大一点的APP都会使用组件化,拿阿里的路由框架Aroute来说,我们可以在自定义拦截器中根据服务端模块配置策略对相应的模块进行降级处理,当用户点击跳转被降级的模块时可以采取跳转到H5
、RN
、或者直接提示模块降级
强制版本升级
:热修也有失灵的时候,Tinker在merge dex的时候有可能出现OOM可能导致热修失败,AndFix由于在JNI层改动由于ROM兼容性导致热修失败。这种时候就需要发小版本进行强制升级了。