消除卡顿
前言
官方文档
什么是卡顿?
卡顿是一种视觉感受,一般我们衡量卡顿用FPS频率(每秒显示帧数)。12FPS类似于手动翻书,一般游戏底线30FPS,当到达60FPS时,手机交互感和逼真感大幅提升,在提高对视觉影响不明显。到达75FPS时,VR没有眩晕的感觉。开发app性能保持在60fps。
60fps表示60帧率,1s完成60帧的动画效果,16ms=1000/60 ,即每一帧把所有任务处理完。
对于Android系统每隔16ms发出VSYNC信号对UI进行渲染。
某个操作花费24ms,严重丢帧,导致卡顿产生。如果32ms才能处理,则可能有一个画面不显示产生停顿现象。
卡顿的衡量标准
丢帧情况 | 卡顿情况 |
---|---|
0-10帧 | 流畅 |
10-20帧 | 较卡 |
20-40帧 | 很卡 |
40-60帧 | 卡死了 |
所以,当丢帧大于10帧时需要优化。
产生卡顿原因
策略:消除或减弱卡顿
在Android系统中,16ms中我们应用做了什么工作,那么那些工作阻止我们在16ms更新界面。
16ms中我们要处理什么内容?
以XML布局绘制来说明:
- CPU负责把UI组件计算成多边形和纹理
- OpenGL负责绘制图像(DisplayList)
- GPU栅格化需要显示内容并渲染到屏幕
Profile GPU Rendering
官方文档
判断工作占用了多少时间
手机开发者选项中—>GPU呈现模式分析(条形图)
大致分三种:CPU工作,OpenGL工作,GPU工作
颜色对应着不同工作:
- 绿色:用户处理,动画,测量及摆放
- 蓝色:画(OpenGL)
- 黄色:GPU渲染
通用优化流程
第一步:UI层优化
- UI问题比较容易查找
- 一旦出现问题影响范围广(XML,measure,layout,draw,DisplayList)
工具:过渡绘制查看功能,Hiserarchy Viewer等
常见问题:过渡绘制,布局复杂,层级过深等等。。。
过渡绘制
减少过渡绘制
在屏幕绘制多次(超过一次或二次)
如:文本框 设置了背景颜色(黑色),那么显示的文字(白色),就需要在背景后再次绘制。
颜色 | 次数 |
---|---|
蓝色 | 2 |
绿色 | 3 |
粉色 | 4 |
红色 | 5及以上 |
如何查看打开手机开发者的—>GPU过渡绘制
- 大面积都是蓝色,属于正常情况
- 重点关注,绿色以后。
优化1:
多层背景颜色嵌套(background)
优化2:
自定义控件的过渡绘制(区域是否可见)不可见:不用绘制
例如:扑克牌----不用画所有
canvas.clicRect()裁剪矩形区域
canvas.save();
canvas.restore();
这两个方法必须同时出现,当平移,放缩,旋转,错切。。才能起作用。
布局复杂 层级过深
性能与视图层次结构
用工具来查Hirerarchy Viewer(层级查看器),7.0以后去除,由LayoutInspcector代替。
我们通过红,黄,绿三种颜色来区分布局的Measure,Layout,Executive的相对性能表现如何。
LayoutInspector不能查看每一View具体onMeasure,onLayout,onDraw(可以用Window.onFrameMetricsAvailableListener来计算View方法耗时)
可用开源项目ActivityFrameMetrics来设置全部Application统一设置
界面优化常用做法
- 没有用父布局(没有背景绘制或没大小的限制,不会对界面效果产生影响),(特别是进来布局,,标签替换。)
- 在布局层次一样的情况下(建议使用Linearlayout代替Relativelayout)
- 使用LinerLayout导致的层次变深(用Relativelayout代替)
- 不常用的UI设置成GONE,尝试用代替(布局的懒加载)
- 去掉多余的背景颜色(减少过渡绘制),(对于多层背景色布局来说,留最上面一层即可)(谨慎使用alpha,由于会和屏幕上已渲染好的元素做blend处理)
- 对于使用selector当背景的布局,可以将normal状态color设置为透明。
- 我们不能因提高性能而忽略了界面需达到效果(design and Performance)
第二步:代码问题查找
工具 Lint
常见问题:我们重点关注Performance和xml一些建议
- 在绘制时,实例化对象(ondraw方法不要new对象 画笔Paint)
- 手机不能进入休眠状态(Wakelock)(视频看完不回收)
- 资源忘记回收(定位没关)
- Handler使用不当倒置内存泄漏
- 没有使用SparseArray代替HashMap(1000以内)
- 未被使用的资源(APK瘦身)
- 布局中无用参数
- 可优化布局(如ImageView与TextView的组合是否可用TextView独立完成)
- 效率低下的weight
- 无用的命名空间等
Lint
使用:菜单栏----Moudle—Analyze中的InspectCode选项。
重点关注:性能问题(Performance,Performance issues)
不断关注Lint中提到问题,将公司中命名规范中没有内容逐一补全
Lint不是万能的。
第三步:优化App的逻辑层
TraceVIew工具使用(3.2以后已弃用,改为CPU性能剖析器)
CPU 性能剖析器
常见问题:
- 主线程耗时大的函数
- 滑动过程中的CPU工作问题
工具可提供每个函数的耗时和调用次数
两种耗时大的函数:
- 主线程里占用CPU时间很长的函数(特别时IO操作(文件IO,网络IO),数据库操作等,这些操作在子线程中完成)
- 主线程调用次数多函数(占用时间不多,调用次数多)
使用TraceView找出卡住主线程的地方(直接通过CPU profiler中点击record按钮后,任意时间后点击stop按钮生成 .trace文件)
重点关注InclCpuTime,Call+Recur,Calls/Total,RealTime/Call
减少Cpu主线程占用的常规做法
- 不要阻塞UI线程,占用Cpu较多工作尽可能放在子线程执行
- 需要结合使用场景选择不同线程处理方案
- AsynTask:为了UI线程与工作线程(进行快速切换)提供一种便捷机制(适用于 当下立即需启动,但生命周期短暂)
- HandlerThread:为了某些回调方法或等待某些任务执行设置线程(线程调度)
- ThreadPool:把任务分解不同单元,分发到各不同线程(并发处理)(创建线程消耗资源)
- IntentService:适用于执行由UI触发后台Service任务并可以把后台任务执行情况(子线程操作)
- 如果大量操作数据时建议使用批量处理(批量添加数据,充电,wifi情况下获缓存数据库,操作数据库)
定位卡顿原因
综合案例
应用启动优化案例
分析:应用在启动过程中我们的代码能够影响启动速度地方:Application的onCreate首屏Activity的渲染(缩短时间并提高用户体验)
大致步骤:
- TraceView工具观察启动过程方法耗时情况,重点关注onCreate方法(自定义Application和首页Activity)(TraceView工具如何在应用启动监控数据),由于application无法进行Debug,通过代码进行Debug
Debug.startMethodTracing("");
super();
Debug.stopMethodTracing(); - 分析自定义Application耗时操作,判断onCreate方法内容。(第三方工具是否可以不占用主线程进行初始化)
- 查看app是否由过渡渲染(background,自定义控件过渡绘制)
- 利用LayoutInspect查看是否层级过深
- 启动过程中的白屏优化
(第三方工具初始化会耗时很长时间)Application当中。
IntentService:耗时操作移动到子线程,(会自动释放资源)(将My Application中的onCreate方法内容耗时初始化工作移动到该类中)
由于使用第三方工具一般要初始化,没初始化不能够使用。(我们可以通过在要使用该工具的地方判断是否初始化完成在进行操作该工具类方法)
例如:
- 标志位(boolean)—》使用该工具的地方每个一段时间判断一下。
- 当初始化完成后,发出一个通知如有观察者进行后续工作处理。
使用Handler每隔一段时间进行一次判断,如果初始化完成,则获取网络数据
if(Boolean){网络请求}else{this.sendEmptyMessageDelayed(100,5)}
优化层级布局:LayoutInspect工具(看层级)找到ContentFrameLayout(看层级)有没有无用层级,可优化层级
优化白屏问题:
Launch screen设置
在resource下样式
方案一:设置成透明的界面,制造延时启动效果
ture
ture
方案二:设置一个背景图
@drawable/splash
ture
注:界面加载完成后需要将背景改成白色(或者设置成其他颜色)
public void onWindowFoucsChange(boolean hasFocus){
super();
getWindow().setBackgroundDrawable(new ColorDrawable(Color.WHITE));
}