android 丢帧分析_Android 丢帧原理以及办法

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

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年希望可以发觉更多的东西给大家,并且希望大家可以积极执政文章中的错误。祝大家新年快乐!😄

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值