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软件绘制流程

Android基础 硬件加速和软件绘制 invalidate和RequestLayout流程_第1张图片

从代码可知:

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

invalidate硬件加速流程

Android基础 硬件加速和软件绘制 invalidate和RequestLayout流程_第2张图片

  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流程

Android基础 硬件加速和软件绘制 invalidate和RequestLayout流程_第3张图片

  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 =

你可能感兴趣的:(Android,Java,性能优化等,android,invalidate,requestLayout,绘制)