Android基础 硬件加速和软件绘制 invalidate和RequestLayout流程

前言

本文介绍了硬件加速和软件绘制的区别;
并梳理了最简单的情况,在子View(非ViewGroup)调用invalidate的流程,且忽略绘图缓存,即mLayerType == LAYER_TYPE_NONE
和requestLayout流程梳理。

硬件加速和软件绘制

  1. Software-based drawing model
    In the software drawing model, views are drawn with the following two steps:

    • Invalidate the hierarchy
    • Draw the hierarchy
      The Android system then draws any view in the hierarchy that intersects with the dirty region.
  2. Hardware accelerated drawing model
    Instead of executing the drawing commands immediately, the Android system records them inside display lists, which contain the output of the view hierarchy’s drawing code.
    Another optimization is that the Android system only needs to record and update display lists for views marked dirty by an invalidate.
    The new drawing model contains three stages:

    • Invalidate the hierarchy
    • Record and update display lists
    • Draw the display lists

硬件加速相比软件绘制的几个优点:

  1. 硬件加速收集绘制指令后,统一发送给RenderThread进行GPU绘制,分担UI线程压力
  2. 硬件加速只绘制标记为PFLAG_INVALIDATED的View,不会绘制与该View相交的区域

参考:
官方文档
理解Android硬件加速的小白文

invalidate软件绘制流程

软件绘制

从代码可知:

  1. View如果不可见&&无动画,ViewGroup不可见 && 无动画 && 无过渡动画不会执行invalidate
  2. 软件绘制会触发与dirty区域相交的所有View(硬件加速优化点)
  3. 根据第一点,ViewGroup#invalidate时会触发其包括自身和所有子View重绘
  4. 同一个draw时序内连续调用同一View的invalidate时,会被Flag阻挡,不再向下走
  5. 同一个draw时序内不同View调用invalidate时只会调为一个,不会重复执行

invalidate硬件加速流程

硬件加速

  1. View如果不可见&&无动画,ViewGroup不可见 && 无动画 && 无过渡动画不会执行invalidate
  2. 硬件绘制只会触发标记了PFLAG_INVALIDATED的View的draw()或dispatchDraw()
  3. View#invalidate时(非ViewGroup),因为被dispatchGetDisplayList接管了,不会调用dispatchDraw
  4. 同一个draw时序内连续调用同一View的invalidate时,会被Flag阻挡,不再向下走
  5. 同一个draw时序内不同View调用invalidate时只会调为一个,不会重复执行

requestLayout流程

requestLayout

  1. 清除measureCache,标记PFLAG_FORCE_LAYOUT,递归向上调用,整条路径都被标记,整条路径都会重薪measure、layout
  2. 可能会重新触发draw(),layout时大小变化后会触发invalidate
  3. 同一个layout时序内连续调用同一View的requestLayout时,会被isLayoutRequested阻挡,不再向上走
  4. 同一个layout时序内不同View调用requestLayout时只会调为一个,不会重复执行



相关代码如下:

View#invalidate

  void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) {
   
      if (skipInvalidate()) {
   
          // View不可见 && 无动画,ViewGroup不可见 && 无动画 && 无过渡动画
           return;
      }
      
      /*
PFLAG_DRAWN:表示绘制好了,fullInvalidate后表示没绘制好,已经要重绘了,在child.draw()时置为1
PFLAG_HAS_BOUNDS: 已经layout完成,onLayout#setFrame中赋值
PFLAG_DRAWING_CACHE_VALID:buildDrawingCache()中赋值,表示此View对象的cache是否也需要被invalidate
PFLAG_INVALIDATED:是否需要重建View的display list
      */
      if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
                || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
                || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
                || (fullInvalidate && isOpaque() != mLastIsOpaque)) {
   
            if (fullInvalidate) {
   
                mLastIsOpaque = isOpaque();
                mPrivateFlags &= ~PFLAG_DRAWN; // 已经在流程中了
            }

            mPrivateFlags |= PFLAG_DIRTY;

            if (invalidateCache) {
   
                mPrivateFlags |= PFLAG_INVALIDATED; // 标记要被重绘,硬件加速以此为标识
                mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
            }

            // Propagate the damage rectangle to the parent view.
            final AttachInfo ai = mAttachInfo;
            final ViewParent p = mParent;
            if (p != null && ai != null && l < r && t < b) {
   
                final Rect damage = ai.mTmpInvalRect;
                damage.set(l, t, r, b);
                p.invalidateChild(this, damage); // 父控件重绘dirty区域
            }

            // Damage the entire projection receiver, if necessary.
            if (mBackground != null && mBackground.isProjected()) {
   
                final View receiver = getProjectionReceiver();
                if (receiver != null) {
   
                    receiver.damageInParent();
                }
            }
        }
}

ViewGroup#invalidateChild

public final void invalidateChild(View child, final Rect dirty) {
   
        final AttachInfo attachInfo =
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

baiiu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值