美文网首页
View的绘制原理&渲染机制

View的绘制原理&渲染机制

作者: Owen270 | 来源:发表于2024-10-10 19:08 被阅读0次

深入理解Android之View的绘制流程
Android View的绘制流程知识点总结
Android View的渲染过程
关于 Android 渲染你应该了解的知识点
Android屏幕刷新机制原理分析

1.绘制流程从哪里开始
v2-45fd36622d21e7acd47f9abfaec297cf_r.jpg image.png
  • Activity.setContentView() 其中getWindow()返回的是Activity所关联的PhoneWindow
public void setContentView(@LayoutRes int layoutResID) {    
  getWindow().setContentView(layoutResID);    
  . . .
}
  • PhoneWindow.setContentView() ,递归性的解析xml,构建ViewTree,生成DecorView。
@Override
public void setContentView(int layoutResID) {
  if (mContentParent == null) {
    // mContentParent即为上面提到的ContentView的父容器,若为空则调用installDecor()生成
    installDecor();
  } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
    // 具有FEATURE_CONTENT_TRANSITIONS特性表示开启了Transition
    // mContentParent不为null,则移除decorView的所有子View
    mContentParent.removeAllViews();
  }
  if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
    // 开启了Transition,做相应的处理,我们不讨论这种情况
    // 感兴趣的同学可以参考源码
    . . .
  } else {
    // 一般情况会来到这里,调用mLayoutInflater.inflate()方法来填充布局
    // 填充布局也就是把我们设置的ContentView加入到mContentParent中
    mLayoutInflater.inflate(layoutResID, mContentParent);
  }
  . . .
  // cb即为该Window所关联的Activity
  final Callback cb = getCallback();
  if (cb != null && !isDestroyed()) {
    // 调用onContentChanged()回调方法通知Activity窗口内容发生了改变
    cb.onContentChanged();
  }

  . . .
}
  • ActivityThread
public final class ActivityThread {
   //ApplicationThread中会调用sendMessage(ActivtyThread.H.LAUNCH_ACTIVITY)
       private class H extends Handler {
            public static final int LAUNCH_ACTIVITY         = 100;
             public static final int RESUME_ACTIVITY         = 107;
             public static final int STOP_ACTIVITY_SHOW      = 103;
              public static final int PAUSE_ACTIVITY          = 101;
            public static final int DESTROY_ACTIVITY        = 109;
            public void handleMessage(Message msg) {
                   switch(msg.what){
                      case  LAUNCH_ACTIVITY :
                                 handleLaunchActivity();
                            break;
                       case  RESUME_ACTIVITY :
                                 handleResumeActivity();
                            break;
                       case  STOP_ACTIVITY_SHOW :
                                 handleStopActivity();
                            break;
                      case  PAUSE_ACTIVITY :
                                 handlePauseActivity();
                            break;
                        case  DESTROY_ACTIVITY:
                                 handleDestroyActivity();
                            break;
                   }
             } 
       }
         public Activity handleLaunchActivity() {
           final Activity a = performLaunchActivity(r, customIntent);
           return a;
        }
        public  Activity performLaunchActivity() {
               //attach方法中创建PhoneWindow,设置WindowManager
               activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.activityConfigCallback,
                        r.assistToken, r.shareableActivityToken);
         /* onCreate方法setContentView,通过LayoutInflater把布局文件转换成一个java内存中ViewTree
             对象,主要的逻辑就是解析递归性的解析xml文件,把每个TAG,用反射的方式来生成一个View对 
             象,当Xml解析完成后,一颗View Tree就生成完了,但是需要注意的是,inflate之后,ViewTree 
            是创建好了,但是这仅仅是以单纯对象数据的形式存在,这时去获取View的GUI的相关属性,比如 
            大小,位置,渲染状态,是不存在的*/
             
                if (r.isPersistable()) {
                //>>>activity.performCreate()>>>Activity.onCreate() 
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
       }

         public void handleResumeActivity(ActivityClientRecord r) {
                   if (!performResumeActivity(r, finalStateRequest, reason)) {
                        return;
                  }
                 r.window = r.activity.getWindow();
                 View decor = r.window.getDecorView(); //通过后PhoneWindow获取DecorView
                 ViewManager wm = a.getWindowManager(); //获取WindowManagerImpl
                 wm.addView(decor, l);
       }
       public boolean performResumeActivity(){
           //>>>mInstrumentation.callActivityOnResume(this)>>>Activity.onResume()
             r.activity.performResume(r.startsNotResumed, reason);
       }
      public void handlePauseActivity(){
                   performPauseActivity();
      }
  
      private Bundle performPauseActivity(){
           //>>>activity.performPause()>>>onPause()
           mInstrumentation.callActivityOnPause(r.activity);
      }
      public void handleStopActivity(){
         performStopActivityInner();
     }
     private void  performStopActivityInner(){
         // >>>activity.onStop();
          mInstrumentation.callActivityOnStop(this);
    }
    public void handleDestroyActivity(){
         performDestroyActivity();
   }
   void performDestroyActivity(){
       //>>>activity.performDestroy()>>>onDestroy()
       mInstrumentation.callActivityOnDestroy(r.activity);
   }
}

  • WindowManagerImpl
public final class WindowManagerImpl implements WindowManager {
   public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,  
                mContext.getUserId());
    }
} 
  • WindowManagerGlobal
public final class WindowManagerGlobal {
     public void addView(View view, ViewGroup.LayoutParams params) {
          ViewRootImpl root;
           if (windowlessSession == null) {
                root = new ViewRootImpl(view.getContext(), display);
            } else {
                root = new ViewRootImpl(view.getContext(), display,
                        windowlessSession);
            }
            root.setView(view, wparams, panelParentView, userId); 
    }

}
  • ViewRootImpl (ViewGroup继承View)

(1).ViewRootImpl.performMeasure>>View.measure>>ViewGroup.onMeasure>>ViewGroup.measureChildWithMargins>>View.measure,如果当前view非ViewGroup,View.onMeasure>>View.setMeasuredDimension()测量View自身, 如果view是ViewGroup,ViewGroup会重写父类View.onMeasure()非空方法,会遍历子View,递归性的调用 View.measure()测量子View.

(2).ViewRootImpl.performLayout>>View.layout>>ViewGroup.onLayout>>ViewGroup.layoutChildren>>View.layout,如果当前view非ViewGroup,view.layout方法中会调用View.setFrame(l,t,r,b)定位自身, 如果view是ViewGroup,在ViewGroup会重写父类View.onLayout空方法,遍历子View,递归性的调用View.layout定位每个子View的大小.

(3).ViewRootImpl.pefromDraw>>ViewRootImpl.drawSoftware>>>>View.draw>>View.onDraw,ViewGroup.dispatchDraw>>ViewGroup.drawChild>>View.draw,如果当前view非ViewGroup,View.draw方法中会调用onDraw绘制自身(子View自己实现onDraw)),如果view是ViewGroup,在ViewGroup中会重写父类View中的diapatchDraw空方法,会遍历子View,,递归调用View.draw测量每个子View的大小.


public final class ViewRootImpl {
      public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
          //执行mTraversalRunnable接口,调用 doTraversal()
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); 
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
     final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
     final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
     }
    void doTraversal() {
         if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

             if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
             }

            performTraversals();

             if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
             }
           }
      }
    private void performTraversals() {
        measureHierarchy();

        performLayout(lp, mWidth, mHeight); //执行layout

        performDraw();//执行draw
    }
   private void measureHierarchy(){
       
         performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); //執行測量
   }

   //传入performMeasure()方法的MeasureSpec的SpecMode为EXACTLY,SpecSize为窗口尺寸。
    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
         try { 
          childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
          childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
          } finally {
           . . .
         }
      }
}

其中getRootMeasureSpec()方法来获取MeasureSpec,这个根MeasureSpec代表了对DecorView的宽度的约束信息,现在我们来简单介绍一下MeasureSpec的概念。
MeasureSpec是一个32位整数,由SpecMode和SpecSize二部分组成,其中高2位为SpecMode,低30位
为SpecSize,SpecMode为测量模式,SpecSize为相应测量模式下的测量尺寸。View(包括普通View和ViewGroup)的SpecMode由View的LayoutParams结合父View的MeasureSpec生成。
SpecMode的取值可以分为以下三种:

  • EXACTLY :父View期望子View的大小是(SpecSize).
  • AT_MOST:父View期望子View的大小不得超过SpecSize.
  • UNSPECIFIED:父View对子View的大小不作限制,通常用于系统内部.

2.View的整个绘制流程可以分为以下三个阶段:

  • measure:判断是否需要重新计算View的大小,如果需要的话就重新计算。
  • layout:判断是否需要重新计算View的位置,如果需要的话就重新计算。
  • draw:判断是否需要重新绘制View,如果需要的话就重新绘制


    image.png

2.1.measure阶段

如果是普通View(非ViewGroup)来说,只需完成自身的测量工作即可,setMeasureDimension(),具体来说是以getDefaultSize()方法的返回值来作为测量结果。
public class View{
        public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
               onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
        public final void layout(int widthMeasureSpec, int heightMeasureSpec) {
               onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
        public final void draw(int widthMeasureSpec, int heightMeasureSpec) {
                 if (!verticalEdges && !horizontalEdges) {
                        drawBackground(canvas);//绘制背景
                       onDraw(canvas);//绘制自己
                      dispatchDraw(canvas);//绘制子View
                 }           
       }
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
             setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
             getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
     } 
     // 在View中个空方法,在ViewGroup中是含有abstract关键字的空方法
       protected void onLayout(boolean changed, int left, int top, int right, int bottom) {

      }
     //在View中是个空方法
       protected void onDraw(Canvas canvas) {

       }
     //在View中是个空方法,在ViewGroup中具体实现
       protected void dispatchDraw(Canvas canvas) {

      }
 
        public static int getDefaultSize(int size, int measureSpec) {
            int result = size;
            int specMode = MeasureSpec.getMode(measureSpec);
            int specSize = MeasureSpec.getSize(measureSpec);
            switch (specMode) {
            case MeasureSpec.UNSPECIFIED:
                 result = size;
               break;
/*View的getDefaultSize()方法对于AT_MOST和EXACTLY这两种情况都返回了SpecSize作为result。
所以若我们的自定义View直接继承了View类,我们就要自己对wrap_content (对应了AT_MOST)
这种情况进行处理,否则对自定义View指定wrap_content就和match_parent效果一样了。
*/
            case MeasureSpec.AT_MOST:
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
          }
           return result;
      }
}



如果是ViewGroup,比如DecorView就是FrameLayout,遍历FrameLayout所有子View,逐一通过MeasureChildWithMargins()方法对子View进行测量,取所有子View中,宽度最大和高度最大的子View的宽度和高度作为maxWidth和maxHeight,而后将得到的maxWidth和maxHeight加上padding值,setMeasuredDimension(maxWidth,maxHeight)来确定FrameLayout的大小.
//重写的是View中的onMeasure方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  int count = getChildCount();
  int maxHeight = 0;
  int maxWidth = 0;

  int childState = 0;
  for (int i = 0; i < count; i++) {
    final View child = getChildAt(i);
    if (mMeasureAllChildren || child.getVisibility() != GONE) {
      measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
      final LayoutParams lp = (LayoutParams) child.getLayoutParams();
      maxWidth = Math.max(maxWidth,
          child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
      maxHeight = Math.max(maxHeight,
          child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
      childState = combineMeasuredStates(childState, child.getMeasuredState());
    }
  }
  maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
  maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();

  // Check against our minimum height and width
  maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
  maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

  // Check against our foreground's minimum height and width
  final Drawable drawable = getForeground();
  if (drawable != null) {
    maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
    maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
  }

  setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
        resolveSizeAndState(maxHeight, heightMeasureSpec,
        childState << MEASURED_HEIGHT_STATE_SHIFT));
  . . . 
}

容器View通过measureChildWithMargins()方法对所有子View进行测量后,才能得到自身的测量结果。也就是说,对于ViewGroup及其子类来说,要先完成子View的测量,再进行自身的测量(考虑进padding等)
protected void measureChildWithMargins(View child,
  int parentWidthMeasureSpec, int widthUsed,
  int parentHeightMeasureSpec, int heightUsed) {
  final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

  final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
      mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width);
  final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec
      mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height);
   //调用子View的measure()方法进行测量
  child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

getChildMeasureSpec()此方法展现了根据父View的MeasureSpec和子View的LayoutParams生成子View的MeasureSpec的过程。
当父View的SpecMode为EXACTLY时,表示父View对子View指定了确切的宽高限制。

  • childDimension>=0:表示子View指定了具体大小,那么子View的SpceMode就是EXACTLY, SpceSize=childDemension。
  • childDimension==match_parent :此时表示子View想和父View一样大,那么子View的SpecMode就是EXACTLY,SpecSize=size;
  • childDimension==wrap_content:表示子View想自己决定自己的尺寸,那么子View的SpecMode就是AT_MOST,SpecSize=size;

当父View的SpecMode为AT_MOST时,表示父View对子View没有指定确切的宽高限制.

  • childDimension>=0:表示子View指定了具体的大小,那么子View的SpecMode 就是EXACTLY,SpecSize=childDemension;
  • childDimension==match_parent:表示子View的大小和父View一样大,但是父View还无法确定自身的大小,所以子View的SpceMode就是 AT_MOST,SpecSize=size;
  • childDimension==wrap_content:表示子View想自己决定自己的大小,但是父View还无法确定自身的大小,所以子View的SpecMode就是AT_MOST,SpaceSize=size;
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
  // spec为父View的MeasureSpec
  // padding为父View在相应方向的已用尺寸加上父View的padding和子View的margin
  // childDimension为子View的LayoutParams的值
  int specMode = MeasureSpec.getMode(spec);
  int specSize = MeasureSpec.getSize(spec);

  // 现在size的值为父View相应方向上的可用大小
  int size = Math.max(0, specSize - padding);

  int resultSize = 0;
  int resultMode = 0;

  switch (specMode) {
    // Parent has imposed an exact size on us
    case MeasureSpec.EXACTLY:
      if (childDimension >= 0) {
        // 表示子View的LayoutParams指定了具体大小值(xx dp)
        resultSize = childDimension;
        resultMode = MeasureSpec.EXACTLY;
      } else if (childDimension == LayoutParams.MATCH_PARENT) {//MATCH_PARENT=-1
        // 子View想和父View一样大
        resultSize = size;
        resultMode = MeasureSpec.EXACTLY;
      } else if (childDimension == LayoutParams.WRAP_CONTENT) {//WRAP_CONTENT=-2
        // 子View想自己决定其尺寸,但不能比父View大 
        resultSize = size;
        resultMode = MeasureSpec.AT_MOST;
      }
      break;

    // Parent has imposed a maximum size on us
    case MeasureSpec.AT_MOST:
      if (childDimension >= 0) {
        // 子View指定了具体大小
        resultSize = childDimension;
        resultMode = MeasureSpec.EXACTLY;
      } else if (childDimension == LayoutParams.MATCH_PARENT) {
        // 子View想跟父View一样大,但是父View的大小未固定下来
        // 所以指定约束子View不能比父View大
        resultSize = size;
        resultMode = MeasureSpec.AT_MOST;
      } else if (childDimension == LayoutParams.WRAP_CONTENT) {
        // 子View想要自己决定尺寸,但不能比父View大
        resultSize = size;
        resultMode = MeasureSpec.AT_MOST;
      }
      break;
      . . .
  }

  //noinspection ResourceType
  return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

2.2.Layout阶段(基本思想也是由根View开始,递归地完成整个控件树的布局(layout)工作)

  • View.layout() 这个方法会调用setFrame()方法来设置View的mLeft、mTop、mRight和mBottom四个参数,这四个参数描述了View相对其父View的位置(分别赋值为l, t, r, b),在setFrame()方法中会判断View的位置是否发生了改变,若发生了改变,则需要对子View进行重新布局,对子View的局部是通过onLayout()方法实现了。由于普通View( 非ViewGroup)不含子View,所以View类的onLayout()方法为空。因此接下来,我们看看ViewGroup类的onLayout()方法的实现。
public void layout(int l, int t, int r, int b) {
    // l为本View左边缘与父View左边缘的距离
    // t为本View上边缘与父View上边缘的距离
    // r为本View右边缘与父View左边缘的距离
    // b为本View下边缘与父View上边缘的距离
    . . .
    boolean changed = isLayoutModeOptical(mParent) ?
   setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {

        onLayout(changed, l, t, r, b);//onLayout在View中是个空方法
        . . .
            
    }
    . . .
}

  • ViewGroup.onLayout实际上该方法是abstract,这是因为不同的布局管理器有着不同的布局方式,这里我们还是以decorView,也就是FrameLayout的onLayout为例子,分析ViewGroup的布局过程:
    遍历ViewGroup中的所有子View,拿到每个View对应的左上右下的值,然后调用child.layout(l,t,r,b)进行定位设置,如果子View是容器View,则会递归的对其子View进行布局。
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
  layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}

void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
  final int count = getChildCount();
  final int parentLeft = getPaddingLeftWithForeground();
  final int parentRight = right - left - getPaddingRightWithForeground();
  final int parentTop = getPaddingTopWithForeground();
  final int parentBottom = bottom - top - getPaddingBottomWithForeground();

  for (int i = 0; i < count; i++) {
    final View child = getChildAt(i);
    if (child.getVisibility() != GONE) {
      final LayoutParams lp = (LayoutParams) child.getLayoutParams();
      final int width = child.getMeasuredWidth();
      final int height = child.getMeasuredHeight();
      int childLeft;
      int childTop;
      int gravity = lp.gravity;

      if (gravity == -1) {
        gravity = DEFAULT_CHILD_GRAVITY;
      }

      final int layoutDirection = getLayoutDirection();
      final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
      final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;

      switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
        case Gravity.CENTER_HORIZONTAL:
          childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
          lp.leftMargin - lp.rightMargin;
          break;
        case Gravity.RIGHT:
          if (!forceLeftGravity) {
            childLeft = parentRight - width - lp.rightMargin;
            break;
          }
        case Gravity.LEFT:
        default:
          childLeft = parentLeft + lp.leftMargin;
      }
      switch (verticalGravity) {
        case Gravity.TOP:
          childTop = parentTop + lp.topMargin;
          break;

        case Gravity.CENTER_VERTICAL:
          childTop = parentTop + (parentBottom - parentTop - height) / 2 +
           lp.topMargin - lp.bottomMargin;
          break;
        case Gravity.BOTTOM:
          childTop = parentBottom - height - lp.bottomMargin;
          break;
        default:
          childTop = parentTop + lp.topMargin;
      }
      child.layout(childLeft, childTop, childLeft + width, childTop + height);
    }
  }
}

2.3.draw阶段(perfromDraw>>draw>>drawSoftWare>>mView.draw())

  • View.draw()实际上,View类的onDraw()方法为空,因为每个View绘制自身的方式都不尽相同,对于decorView来说,由于他是FrameLayout,他是容器View,所以他本身并没有什么需要绘制的。dispatchDraw()方法来绘制子View
  //canvas = mSurface.lockCanvas(dirty);//canvas是通过Surface获取的
public void draw(Canvas canvas) {
  // 绘制背景,只有dirtyOpaque为false时才进行绘制,下同
  int saveCount;
  if (!dirtyOpaque) {
    drawBackground(canvas);
  }
  // 绘制自身内容
  if (!dirtyOpaque) onDraw(canvas);

  // 绘制子View
  dispatchDraw(canvas);//在View中是个空方法,在ViewGroup中具体实现
  // 绘制滚动条等
  onDrawForeground(canvas);
}
  • ViewGroup.dispatchDraw() 遍历ViewGroup中的所有子View,然后调用View.draw方法绘制自身
  @Override
    protected void dispatchDraw(Canvas canvas) {
         for (int i = 0; i < childrenCount; i++) {
            while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
                final View transientChild = mTransientViews.get(transientIndex);
                if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
                        transientChild.getAnimation() != null) {
                    more |= drawChild(canvas, transientChild, drawingTime);  
                }
                transientIndex++;
                if (transientIndex >= transientCount) {
                    transientIndex = -1;
                }
            }

            final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
            final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                more |= drawChild(canvas, child, drawingTime);
            }
        }
    }

  protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        return child.draw(canvas, this, drawingTime); //调用View自身的draw()方法绘制自身.
    }

3.View的渲染机制

3.1. 基本概念
  • CPU:执行应用层面的measure,layout,draw等绘制操作,绘制完成后交由OpenGL,Skia(底层图像渲染库)
  • GPU:OpenGL,Skia库使用GPU处理数据,将数据发送到缓冲区
  • 屏幕:由一个个像素组成,以固定频率(1000ms,60次 即16.66ms)从缓冲区里读取数据填充像素点。
image.png
3.2. Android渲染机制就是一个生产者和消费者的模型
image.png image.png
  • 图像生成者 Choreographer ,每隔16.66ms收到SufaceFlinger的垂直同步信号,调用doFrame进行下一帧图形数据准备(Cpu对ViewTree进行pefromMeasure,perfomLayout,performDraw绘制,如果之前测量过,或者layout过,或者draw过,就不会再次递归性调用View.measure,View.layout,View.draw了),通过Surface获取canvas,canvas会把绘制指令传递到FrameWork层的RenderThread线程,RenderThread线程通过Surface.dequeue从BufferQueue中申请一块GraphicBuffer(图形缓冲区),通过OpenGL或者Skia等底层图形渲染库调用GUP进行绘制填充,最后在把缓冲区交还给BufferQueue队列中.

  • 消费者surfaceFlinger从队列中获取数据,对数据进行合成(DecorView和系统StatusBar),合成了一帧完整的数据之后,就会和HAL通信,替换下一帧数据,等到收到Vsyn垂直同步信号的时候,下一帧数据就会替换当前帧数据,显示到屏幕上了.

  • 注意SurfaceFlinger每隔16.66ms就会给HAL(Hardware Abstraction Layer)和应用层(Choreographer)发送一个Vsyn,Choreographer负责在主线程中完成绘制流程(如果主线程ViewTree发生变化,导致重绘,就会重新走View.measure,View.layout,View.draw 此时如果主线程被阻塞(STW,非主线程耗时操作),可能会导致掉帧。

3.2. Choreograher绘制UI的流程
VIewRootImpl.requestLayout >>VIewRootImpl.scheduleTraversal>> Handler.Looper.MessageQueue.postSyncBarrier在队列中插入入一个同步屏障消息)>>ChoreoGrapher.postCallback(CALLBACK_TRAVERSAL)>>scheduleVsyncLocked(向Native层申请Vsyn)>>nativateScheduleVsync(向SurfaceFlinger注册了一个观察者)>> SurfaceFlinger每隔16.66ms之后,回回调观察者的onSync方法>>onSync>>Handler.sendMessageAtTime>>run>>doFrame>>doTraversal>>performMeasure>>performLayout>>performDraw.

3.3. 屏障消息的处理机制
(1).什么是屏障消息?
屏障消息就是没有Target的消息,不需要回调handleMessage,消息队列中的消息是按照时间顺序排列的,因此屏障消息会被插入到所有在其之前应该被处理的消息之后(队尾).
(2).为什么绘制消息的优先级最高,会被首先处理。

    // 向Handler发送一个消息,指定的时间执行,并且是异步消息
       Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, 
       action);
      msg.arg1 = callbackType;
      msg.setAsynchronous(true);
      mHandler.sendMessageAtTime(msg, dueTime);

原理:主线程的 Looper 会一直循环调用 MessageQueue 的 next() 来取出队头的 Message 执行,当 Message 执行完后再去取下一个。当 next() 方法在取 Message 时发现队头是一个同步屏障的消息时,就会去遍历整个队列,只寻找设置了异步标志(setAsynChronous(true))的消息,如果有找到异步消息,那么就取出这个异步消息来执行,否则就让 next() 方法陷入阻塞状态。如果 next() 方法陷入阻塞状态,那么主线程此时就是处于空闲状态的,也就是没在干任何事。所以,如果队头是一个同步屏障的消息的话,那么在它后面的所有同步消息就都被拦截住了,直到这个同步屏障消息被移除,否则主线程就一直不会去处理同步屏障后面的同步消息.

image.png 1732099432146.png

4.View.invalidate分析

1732444078(1).png
1732445053(1).png
4.1.View.invalidate
public class View {

    public void invalidate(boolean invalidateCache) {
        invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
    }

   void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,boolean fullInvalidate) {
          //fullInvalidate是true,  mPrivateFlags &= ~PFLAG_DRAWN 打上标记
           if (fullInvalidate) {
                mLastIsOpaque = isOpaque();
                mPrivateFlags &= ~PFLAG_DRAWN;
            }

            final AttachInfo ai = mAttachInfo;
            final ViewParent p = mParent;
            if (p != null && ai != null && l < r && t < b) {
                final Rect damage = ai.mTmpInvalRect; //把自身的位置传递给父View
                damage.set(l, t, r, b);
                p.invalidateChild(this, damage);
            }
   }
}
4.2.ViewGroup.invalidateChild
public class ViewGroup  implement ViewParent{
         public final void invalidateChild(View child, final Rect dirty) {
               ViewParent parent = this;
           //递归循环调用,最终Parent会变成ViewRootImpl,最终ViewRootImpl.invalidateChildParent
                do {
                      //location是父View的位置int[left,top],dirty是子View的位置(Rect(l,t,r,b))
                      parent = parent.invalidateChildInParent(location, dirty);
                  } while (parent != null);
        }
       public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
              //子View和父View的大小求交集
               if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
                    dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
                } else {
                    //子View和父View的大小求交集
                    dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
                }
                location[CHILD_LEFT_INDEX] = mLeft; //当前View距离屏幕左边的距离
                location[CHILD_TOP_INDEX] = mTop;//当前View距离屏幕顶部的距离
                mPrivateFlags &= ~PFLAG_DRAWN; //打上绘制标记
               return parent;//返回父View
       }
}
4.3.ViewRootImpl.invalidateChildInParent
public class ViewRootImpl implement ViewParent{
     invalidateRectOnScreen(dirty);
}
 private void invalidateRectOnScreen(Rect dirty) {
       final Rect localDirty = mDirty;
       //父View和子View的大小求交集
       localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);
       if (!mWillDrawSoon && (intersected || mIsAnimating)) {
 //重新执行 doTraversal>>performMeasure>>performLayout>>performDraw
            scheduleTraversals(); 
        }
 }

相关文章

  • Android知识点基础篇(二)

    索引:Binder机制,共享内存实现原理ActivityThread工作原理嵌套滑动实现原理View的绘制原理,自...

  • View绘制及事件机制原理

    一、View绘制流程机制 1、View绘制起点 performTraversals()方法触发了View 的绘制。...

  • 复习

    RecyclerView的缓存机制bitmap 优化性能优化view 的绘制流程浅析LRUCache原理(Andr...

  • iOS面试必看

    01UI视图 事件传递机制UI绘制原理异步绘制原理流式页面的性能优化离屏渲染 02OC语言 KVOKVC分类关联对...

  • Android 知识框架

    IPC机制,Binder和匿名共享内存等 四大组件启动,工作原理 View系统,绘制原理,事件分发 动画框架,原理...

  • Android 面试题8

    事件分发流程Carson_Ho Android事件分发机制详解:史上最全面、最易懂View的渲染机制动画的原理,...

  • 学习进阶

    Android 框架原理和源码流程1.app启动流程2.activity启动流程3.view绘制机制4.view事...

  • 4.1View相关-View绘制详解

    View的绘制机制 view树的绘制流程 measure方法 layout方法 draw方法 1.view树的绘制...

  • 探索SwipeDelMenuLayout-对View知识的实践

    前言 前面对View的基础知识,View的分发机制,View绘制原理进行了系统探究.书上的例子有现成的答案,且一直...

  • 硬件加速

    android 6.0似乎在view中绘制.9图片硬件加速导致渲染出错!关闭可以但是机制有问题还是其他?!

网友评论

      本文标题:View的绘制原理&渲染机制

      本文链接:https://www.haomeiwen.com/subject/yugwrjtx.html