为什么要做性能优化
- 体验差影响核心指标
- 线上问题追查困难
- 降低性能优化的长期开销
介绍性能平台
- 交代背景
- 具体讲解
为什么要自建APM
- 需求层面(APP启动时间,可以分为Application、Activity耗时商业APM不满足需求)
- 效率层面
- 数据安全
平台化
- crash管理平台(bugly:数据采集上报成功率高,包含java和native崩溃)
- 商业级APM平台(听云:通用性能解决方案数据采集完善,方便接入但不满足个性化要求数据存在隐患)
- 自建APM(美团、携程:贴合自身业务特点满足定制化需求,数据安全)
APP启动优化
冷启动
耗时最多,衡量标准
- Click Event 用户点击事件
- IPC 触发IPC操作
- Process.start 进程创建
- ActivityThread 单独进程APP的入口类,消息和handler的相关创建
- bindApplication 通过反射创建Application调用与之相关的生命周期
- LifeCycle Activity生命周期
- ViewRootImpl 界面绘制
冷启动之前,系统的行为
- 启动App
- 加载空白window
- 创建进程
随后任务
- 创建Application
- 启动主线程
- 创建Activity
- 加载布局
- 布置屏幕
- 首帧绘制
热启动
最快
- 后台
- 前台
温启动
较快
- LifeCycle Activity生命周期
启动时间测量方式
adb命令
方法:adb shell am start -W packagename/首屏Activity
- ThisTime:最后一个Activity启动耗时
- TotalTime:所有Activity启动耗时
- WaitTime:AMS启动Activity的总耗时
优缺点:线下使用方便,不能带到线上。非严谨、精确时间
手动打点
启动时埋点,启动结束埋点,二者差值
注意:
- 误解:onWindowFocusChanged只是首帧时间,不算启动结束
- 正解:真实数据展示,Feed第一条展示(addOnPreDrawListener)
- 启动的时间写在Application中的attachBaseContext方法中
优点:精确,可带到线上,推荐使用
启动优化工具选择
traceview
- 图形的形式展示执行时间、调用栈等
- 信息全面,包含所有线程
使用方式
- ‘Debug.startMethodTracing()’
- ‘Debug.stopMethodTracing()’
- 生成文件在sd卡:Android/data/packagename/files
优缺点:
- 运行时开销严重,整体都会变慢
- 可能会带偏优化方向
- traceview与cpu profiler
systrace
- 结合Android内核的数据,生成Html报告
- API18以上使用,推荐TraceCompat
使用方式
- python systrace.py -t 10 [other-options] [categories]
- https://developer.android.com/studio/command-line/systrace#command_options
优缺点:
- 轻量级,开销小
- 直观反应cpu利用率
- cputime与walltime区别(举例:锁冲突)
优雅获取方法耗时
常规方式
手动埋点 (SystemClock.currentThreadTimeMillis())
侵入性强、工作量大
AOP介绍
Aspect Oriented Programming,面向切面编程
- 针对同一类问题的统一处理
- 无侵入添加代码
AspectJ使用
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0'
implementation 'org.aspectj:aspectjrt:1.8.+'
Join Points
程序运行时的执行点,可以作为切面的地方
- 函数调用、执行
- 获取、设置变量
- 类初始化
PointCut
带条件的JoinPints
Advice
一种Hook,要插入代码的位置
- Before: PointCut之前执行
- After: PointCut之后执行
- Around: PointCut之前、之后分别执行
语法简介
- Before: Advice,具体插入位置
- execution:处理Join Point的类型,call、execution
- (“execution(android.app.Activity.on*(..))"):匹配规则
- onActivityCalled:要插入的代码
|
|
AOP实战
实例:
|
|
优点:无侵入性修改方便
异步优化
常规优化
核心思想:子线程分担主线程任务,并行减少时间
CountDownLatch解决异步时某些初始化工作必须等待完成后才会初始化完成
注意事项:
- 不符合异步要求
- 需要在某阶段完成
- 区分cpu密集型和io密集型
常规异步痛点:
- 代码不够优雅
- 场景不好处理(依赖关系)
- 维护成本高
启动器优化
核心思想:充分利用CPU多核,自动梳理任务顺序
- 代码Task化,启动逻辑抽象为Task
- 根据所有任务依赖关系排序生成一个有向无环图
- 多线程按照排序后的顺序依次执行
流程图
延迟初始化
常规方案
- new Handler().postDelayed
- Feed展示后调用
缺点:时机不便控制、导致Feed卡顿
更优方案
核心思想:对延迟任务进行分批初始化->IdleHandler特性,空闲执行
- 执行时机明确
- 缓解Feed卡顿
优化总方针
- 异步、延迟、懒加载
- 技术、业务相结合
其它方案
- 启动阶段抑制GC
- CPU锁频(会增加耗电量)
- 提前异步SharedPreferences
- 启动阶段不启动子进程
- 提前异步类加载
App内存优化
- 内存泄漏
- 内存抖动
- Bitmap
- 谨慎使用SharedPrence
- 谨慎使用外部库
- 业务架构设计合理
App布局优化
工具
- Aop、Hook
- Systrace、layout inspector
优化
- IO、反射、遍历
- 异步inflate、x2c、减少层级、重绘
- Aop、监控
结果产出
- 体系化监控手段:线下&线上
- 指标:FPS、加载时间、布局层级
App线程优化
- 线程收敛
- 统一线程池:任务区分
- 其他细节
App网络优化
网络切换、弱网、无网测试
服务端
- 服务端监控请求耗时(地域、时段、版本、机型)
- 服务端监控失败率(业务失败、请求失败)
- 服务端监控Top失败和异常接口
客户端
- 接口的每一步详细信息(DNS、链接、请求)
- 请求次数、网络包大小、失败原因
- 图片监控
异常监控体系
- 服务器防刷:超限拒绝访问
- 客户端:大文件预警、异常兜底策略
- 单点问题追查
App电量优化
CPU时间片
- 获取运行过程CPU消耗,定位CPU占有率异常方法
- 减少后台应用主动运行
网络请求
- 请求时机及次数限制
- 数据压缩减少时间
- 避免使用轮询功能
定位相关
- 根据场景谨慎选择定位模式
- 考虑网络定位代替GPS
- 使用后务必及时关闭,减少更新频率
界面相关
- 离开界面后停止相关活动
- 耗电操作判断前后台
weaklock相关
- 注意成对出现:acquire与release
- 使用带参数的acquire
- finally确保一定会被释放
- 常亮场景使用keepScreenOn即可
JobSchedular
- 在符合某些条件时创建执行在后台的任务
- 把不紧急的任务放到更合适的时机进行批量处理
App瘦身优化
-
代码瘦身
代码混淆:Proguard
基础库统一,选择更小的库,引入仅需的部分代码移除无用代码,AOP统计使用情况
-
资源瘦身
自带工具
图片压缩
资源混淆:AndResGuard -
so瘦身
都放在armeabi目录,根据CPU类型加载对应架构so
so动态下载或插件化