这是我参与「第四届青训营」笔记创作活动的第8天。
本笔记内容是第七课的Android性能优化。
1 为什么要做性能优化
1.1 从用户体验和业务来看
性能优化可以改善用户体验,有助于提升业务指标。
1.2 从更长时间范围来看
-
硬件性能提升速度变缓;
-
多核带来的提升取决于真正可以并行执行的部分;
-
移动处理器受电池容量限制;
-
软件还有优化空间。
2 性能优化是什么
性能优化的总目标是快、稳、省。
2.1 流畅性优化
2.1.1 了解Android线程结构
在Main Thread UI线程中包括:
System Events
Input Events
Application
Services
Alarm
UI Drawing (系统控制 与App代码无关)
SurfaceFlinger VSync信号 16ms一次或更小
肉眼感知到不卡顿的最低帧数是25。如果没有VSync信号,会导致画面撕裂。
2.1.2 卡顿感知产生原因示例
输入事件无法及时响应,“划不动”
输入事件耗时太长,丢帧
其它事务LameWork,丢帧
解决卡顿:将耗时操作放到其它线程操作
2.2 资源优化
资源:即Android手机的软硬件资源,通俗意义上应用依赖的移动终端的有限资源和系统设置的数值
资源优化方向:
- 端侧资源(功耗、内存、存储、CPU、GPU、网络、音量、亮度等)应用色系和应用亮度带来功耗差异
- 服务侧资源(CDN带宽、API流量)
2.3 稳定性优化
稳定性分为崩溃和超时。超时可导致卡顿掉帧和ANR。其中最关注的是ANR。
2.4 系统级优化
移动操作系统和硬件厂商的性能优化
2014年
ZRAM 无效内存压缩 合入Android主分支
Render Script GPU优化
2015年
推出Android Runtime(ART)虚拟机
HMP scheduler解决大小核问题
2016年
sdcardFS:针对SD卡的优化
CPUset group:前后台应用的限制
2017年
SKGL&Vulkan:图形处理
F2FS:新的文件系统
ART profile and image:计算更精确
Binder refactor
2018年
Cloud profile:Google Play运控
Odex relayout
EAS调度器:平衡功耗与性能
2019年
Generational Concurrent Copying GC:分代并行拷贝GC,提升GC性能
3 最佳工具选型
3.1 性能监控的价值
·监控和优化相生相伴
·监控有攻有防,攻是未来发现现有问题,指导优化方向;防是为了发现劣化问题及时止损
·线上监控发现问题并聚合排序,线下监控作为线上辅助,并在发版前置发现解决问题
3.2 GPU呈现模式
系统通过记录每一帧的相关数据,然后通过图形的形式呈现。
无需二次开发,简单易用,但并不完全准确,并且无法明确造成卡顿原因
3.3 Layertool
找到过度绘制的布局。
通过遍历ViewTree信息,输出View层级关系。
清楚明了,可以宏观感知ViewTree现状,也可以定制,帮助分析overdraw。但不能明确UI性能瓶颈。
3.4 CPU Profiler
Android Studio里有,基于JVMTI(只能在Debug版App执行)
优点:完整的方法调用栈输入、支持Java、C、C++方法耗时检测,上手简单,但性能损耗太大。
3.5 TraceView
Instrument:虚拟监听函数入口回调,Enter/Exit/Unwind
耗时点:读时间、写数据到buffer、加锁等
Sample:定时抓取多次堆栈diff,近似确定函数的进入和退出时间
耗时点:堆栈diff、同Instrument
间隔抓取堆栈的时间越长性能损耗越少,但是同时越容易导致短函数不能被检测到
3.6 Systrace
重要工具
ftrace:debugfs采集和读取trace数据,记录trace events
atrace:用户侧的trace跟踪,聚合所有的trace event。
系统级的Trace数据:锁监控等
3.7 btrace(aka rhea) (进阶)
rhea-systrace:全函数插桩,自动生成Trace代码,对层数做限制,性能损耗50%
rhea-mtrace:全函数插桩,抛弃systrace,自己统计函数耗时,数据展现与systrace相同
rhea-atrace:优化systrace性能,聚合更多性能数据,如类加载、Lock、IO等
3.8 Battery Historian
安装到电脑
手机充满电或者reset batterystats,并在手机上执行测试应用,抓取bugreport
电脑分析bugreport
4 案例
4.1 现状分析
CPU Time
循环,反射,序列化/反序列化,类解析
IO Wait
IO操作,等待IO返回结果
IPC
Binder调用耗时
Lock Wait
主线程是等锁状态,等待其他线程或自己超时唤醒
CPU Schedule
主线程是可执行状态,但是获取不到CPU时间片
运行环境归因
根据耗时成因归类。根据运行所在线程环境采用不同的策略。
Cold Start 目标:小于3秒
Create process
ContentProvider init
Application#Create
Warm Start 目标:小于1秒
Activity#Create
Inflate view hierarchy
Hot Start
Activity#onStart()
渲染分析
渲染瓶颈:
渲染耗时 inflater init bind measure/layout/draw overdraw
渲染频率 Animator FPS,Vsync Leak,requestlayout loop
4.2 优化策略及案例
UI创建
耗时成因:XML IO,Class反射,创建View,Asset资源大锁
官方解决方案:AsyncLayoutInflater
抖音解决方案:
AndInflater:解决xml性能问题-外界方案X2C
LegoInflate:高优先级的启动预加载方案
AsyncInflater:随时随地预加载,不与具体逻辑绑定,生命周期存活,自定义清理周期
数据请求+解析:
GSON解析优化 是否服用gson对象有较大差异
数据协议优化 json->protobuf
渲染耗时优化:
移除不必要的背景图;修改不合理布局;写高效合理的布局;移除默认的Window背景,绘制层级优化
异步渲染:
SurfaceView:采用独立的线程进行绘制和渲染,生命周期需要自己控制
Jetpack Compose:基于组合优于继承的思想,重新设计一套解耦的UI框架
Litho:复杂UI下的高性能渲染框架
5 总结
本节课介绍了Android性能优化的现状、工具,为Android客户端性能优化方向的进一步学习奠定了基础、提供了方向。