博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
android 丢帧分析_Android 丢帧原理以及办法
阅读量:4318 次
发布时间:2019-06-06

本文共 2389 字,大约阅读时间需要 7 分钟。

接近年底,想分享点儿东西给大家。

Android UI绘制过程

开发中的卡顿我想没跟人都遇到过,之前也是搜博客看看怎么个解决办法,没有认真研究过,今天我打算跟大家聊一聊。

先从View 说吧。相信大家应该都知道View的绘制过程,measure,layout,draw。丢帧一定是在16ms内没有把这些事儿干完就对了,这里我们简单的分一下,主要是计算时间,以及绘图时间。

计算时间:这里的measure,layout的过程,都是会向下递归计算的,学过数据结构的话,应该知道,深搜的代价是很大的。所以尽量让树的高度降低,这里就引出扁平化布局。

绘图时间:这里需要着重讲一下,因为有时候这才是我们UI卡顿的主要原因。在这里我们要把android的试图看成是三维的,就像photoshop的图层一样。android在绘制的时候就会一层一层的“粉刷”,好了,那么造成卡顿,也就是丢帧,说白了最后没有在16ms内做完。好了,让我们剖析一下:

1.invalidate():

我们知道invalidate 是用来请求View 重绘的,

619ad652fc1f?from=timeline&isappinstalled=0

invalidateInternal

这里可以看出来draw的过程其实就是拿到AttachInfo 里面包含着绘制信息,以及将绘制区域拿到,通过parent去绘制。让我们跟进去。

619ad652fc1f?from=timeline&isappinstalled=0

invalidateChildInParent

这里的dirty代表你绘制的这块区域是否透明。

619ad652fc1f?from=timeline&isappinstalled=0

invalidate

这里我们看到了个关键函数 scheduleTraversals ,为什么说神奇。我们看一下

619ad652fc1f?from=timeline&isappinstalled=0

scheduleTraversals

这里最重要的是Choreographer 这个,我们最终算出来的绘制信息都要通过它回调,开始他会注册一个广播用来接收时钟信息,然后他会在内部建立一个UI绘制队列:CallbackQueue,我们在外部CallBack的时候,会将我们的绘制信息作为CallbackRecord 然后会在接收到一个时钟信号的时候进行doFrame操作,并打印Traces信息,从而来绘制一帧。

619ad652fc1f?from=timeline&isappinstalled=0

CallbackQueue and CallbackRecord

619ad652fc1f?from=timeline&isappinstalled=0

postCallbackDelayedInternal

可以看到这里我们把我们的绘制内容扔到队列里,等待轮训。

619ad652fc1f?from=timeline&isappinstalled=0

FrameDisplayEventReceiver

接收时钟脉冲信号的广播,16ms一次,我们的目的就是在这个时钟脉冲里搞定整个 view

2.Android 动画

Animator,ScrollTo,offsetLeftAndRight,这里面我们先单列这几项,都是同一个原理。这里我们可以大胆的猜想,一定是频繁执行我们的 Choreographer.CallBack 来绘制,因为只要在16ms内绘制成功,那就是流畅的动画。下面我们验证一下

ScrollTo:

我们先看一下 View 中这个方法

619ad652fc1f?from=timeline&isappinstalled=0

scrollTo

很简单,我们都可以看懂,开始位置,结束位置,这里我们重点关注 postInvalidateOnAnimation()  这个方法

619ad652fc1f?from=timeline&isappinstalled=0

postInvalidateOnAnimation

我们可以看到,这里的动画过程绘制他还是扔到了ViewRootImpl 代理做这件事。

619ad652fc1f?from=timeline&isappinstalled=0

dispatchInvalidateOnAnimation

这里我们看到他开了个线程 mInvalidateOnAnimationRunnable 去添加我们这个将要绘制的 view,接下来我们继续庖丁解牛

619ad652fc1f?from=timeline&isappinstalled=0

619ad652fc1f?from=timeline&isappinstalled=0

InvalidateOnAnimationRunnable

终于,应了我们的猜想,ViewRootImpl 有一个专门执行动画绘制操作的线程,我们可以看到 run() 里面不断地CallBack,然后回收,当然里面有些线程锁啥的不涉及本文就不细说了。

3.ValueAnimator:

这里我们有个 AnimationHandler 来执行动画操作,这其中我们可以看到

619ad652fc1f?from=timeline&isappinstalled=0

doAnimationFrame

这里在不断循环我们所有的anim,并在不断执行 scheduleAnimation 方法

619ad652fc1f?from=timeline&isappinstalled=0

scheduleAnimation

剩下的大家自己翻阅源码把。

这里总结一下。我们所有界面上视图的变化都是都是 ViewRootImpl 把需要重绘的东西填充 Choreographer 中的 mCallbackQueues 队列,然后在时钟脉冲的广播下进行轮训执行。

既然提到队列,假如我们在16ms内大量的填充 AttachInfo 之类的绘制OBJ,就会导致无法再一次时钟脉冲内绘制完毕,就会在造成丢帧,UI阻塞。

避免 Android UI 卡顿解决办法

解决办法:分析了好多,这里说两个方法。

1.避免重绘,这里避免图层(View)迭代。这里我们可以去开发者模式中对“显示GPU视图更新”打钩

619ad652fc1f?from=timeline&isappinstalled=0

过度绘制

619ad652fc1f?from=timeline&isappinstalled=0

优化以后

这里引用 http://hukai.me/android-performance-render 这篇博客的作者,盗个图。😂

这里可以进行,选择制定画布绘制,而不是整个view去绘制。可以在onDraw中进行限制,去限制绘制区域,例如

canvas.clipRect(100,100,350,600, Region.Op.INTERSECT);

2.扁平化布局,归根结底也是减少 mCallbackQueues 队列大小。保证尽量在16ms内绘制完毕,再有就是可以减少视图 ViewTree 的高度,减少时间复杂度,从而优化计算过程

619ad652fc1f?from=timeline&isappinstalled=0

xml代码

619ad652fc1f?from=timeline&isappinstalled=0

优化后的xml代码

*附:

619ad652fc1f?from=timeline&isappinstalled=0

绘制层级

通过打开刚才说的开发者选项,来根据颜色来判断页面绘制情况。

距离回家还有8 个小时,17年希望可以发觉更多的东西给大家,并且希望大家可以积极执政文章中的错误。祝大家新年快乐!😄

转载地址:http://cgrzs.baihongyu.com/

你可能感兴趣的文章
[js高手之路] es6系列教程 - 对象功能扩展详解
查看>>
java算法-选择排序
查看>>
python把字典写入excel之一例
查看>>
ROS配置C++14环境
查看>>
实现简单的登录界面
查看>>
项目管理有感之需求调研
查看>>
安装xampp之后如何建立远程登录用户并修改登录方式和密码
查看>>
linux下部署jenkins
查看>>
atitit.软件guibuttonand面板---os区-----软链接,快捷方式
查看>>
38平衡二叉树
查看>>
2替换空格
查看>>
如何修改settings.xml的镜像
查看>>
用IDEA/WebStrom 提交本地项目到Git/码云等
查看>>
JAVA笔记1__基本数据类型/输入输出/随机数/数组
查看>>
Linux 安装及配置 Nginx + ftp 服务器
查看>>
this 函数执行上下文
查看>>
Three.js 3D打印数据模型文件(.STL)载入中
查看>>
HOG特征-理解篇
查看>>
晨会的重要性
查看>>
poj 3735 大数量反复操作问题(矩阵高速幂)
查看>>