从Activity启动到view绘制

677 阅读3分钟
PhoneWindow extends Window

DecorView extends FrameLayout

Activity.setContentView(R.layout.activity_main);
->getWindow().setContentView(layoutResID);
->PhoneWindow.setContentView(layoutResID)
->PhoneWindow.installDecor();
->ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
-> viewGroup.addView(view, params);

首先我们来了解一下我们熟悉的setContentView做了什么?,如上流程,

可以看到我们平常用的setContentView主要是为Window设置DecorView,设置ContentView的子View,至此View Tree构建完毕, 并不是我们通常认为的触发绘制的地方(ContentView,它是一个FrameLayout(android.R.id.content))

我们先来看下面这张图:

1、Activity通过Window来展示内容
2、Window通过ViewRootImpl管理View Tree

下面我们看看是如何触发view绘制的?AMS(ActivityManagerService)

Activity启动时,ActivityThread.handleResumeActivity()方法中建立了decorView与ViewRoot的关联关系,ViewRootImpl.performTraversals()触发了measure,layout,draw流程:

Activity启动
-> ActivityThread.main()
-> ActivityThread.attach(false, startSeq);
-> AMS.attachApplication(mAppThread, startSeq);
-> ActivityThread.bindApplication()
//创建application
-> ActivityThread.handleBindApplication(AppBindData data); 
     -->Instrumentation.newApplication()
        --> Instrumentation.callApplicationOnCreate(app);
            -->Application.onCreate()
//创建Activity
-> ActivityThread.handleLaunchActivity()
      -->Instrumentation.newActivity()
          --> Instrumentation.callActivityOnCreate()
            --> Activity.onCreate(icicle, persistentState);
                -->Activity.setContentView

//开始绘制View流程
-> ActivityThread.handleResumeActivity()
-> ViewRootImpl.requestLayout()
-> ViewRootImpl的performTraversals()
-> ViewRootImpl.performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
-> ViewRootImpl.performLayout(lp, mWidth, mHeight);
-> ViewRootImpl.performDraw();

1.Measure

measure:
-> ViewRootImpl.performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);//传入performMeasure()方法的MeasureSpec的SpecMode为EXACTLY,SpecSize为窗口尺寸
-> DecorView.measure()  -->View.measure(childWidthMeasureSpec, childHeightMeasureSpec)
-> DecorView.onMeasure() -->FrameLayout.onMeasure(int widthMeasureSpec, int heightMeasureSpec)
-> FrameLayout.measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
-> child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
....
->FrameLayout.setMeasuredDimension()
//对于普通View,会调用View类的onMeasure()方法来进行实际的测量工作,该方法的源码如下:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
        getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

//View的getDefaultSize()方法对于AT_MOST和EXACTLY这两种情况都返回了SpecSize作为result。
//所以若我们的自定义View直接继承了View类,我们就要自己对wrap_content (对应了AT_MOST)
//这种情况进行处理,否则对自定义View指定wrap_content就和match_parent效果一样了。
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;
    case MeasureSpec.AT_MOST:
    case MeasureSpec.EXACTLY:
      result = specSize;
      break;
  }
  return result;
}

2.Layout

layout:
->ViewRootImpl.performLayout(lp, mWidth, mHeight);
->DecorView.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); -->View.layout()
        ->View.setFrame(l, t, r, b);
->DecorView.onLayout(changed, l, t, r, b); -->FrameLayout.onLayout(changed, l, t, r, b);
->FrameLayout.layoutChildren(left, top, right, bottom, false /* no force left gravity */);
->child.layout(childLeft, childTop, childLeft + width, childTop + height);
       ->View.setFrame(l, t, r, b);

View.layout()

 public void layout(int l, int t, int r, int b) {
      ...
        int oldL = mLeft;
        int oldT = mTop;
        int oldB = mBottom;
        int oldR = mRight;

        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);

            ...
    }
setFrame()方法来设置View的mLeft、mTop、mRight和mBottom四个参数,这四个参数描述了View相对其父View的位置(分别赋值为l, t, r, b),在setFrame()方法中会判断View的位置是否发生了改变,若发生了改变,则需要对子View进行重新布局,对子View的布局是通过onLayout()方法实现的。由于普通View( 非ViewGroup)不含子View,所以View类的onLayout()方法为空。

3.Draw

在onDraw()方法中进行自身的绘制

draw:
-> ViewRootImpl.performDraw();
-> DecorView.draw(Canvas canvas) -->View.draw(Canvas canvas)
-> View.drawBackground(canvas);
-> View.onDraw(canvas);
-> View.dispatchDraw(canvas);
    ->child.draw(canvas, this, drawingTime);
-> View.onDrawForeground(canvas);

View.draw(Canvas canvas):

View类的onDraw()方法为空,因为每个View绘制自身的方式都不尽相同,对于decorView来说,由于它是容器View,所以它本身并没有什么要绘制的。dispatchDraw()方法用于绘制子View,显然普通View(非ViewGroup)并不能包含子View,所以View类中这个方法的实现为空。
public void draw(Canvas canvas) {
  . . . 
  // 绘制背景,只有dirtyOpaque为false时才进行绘制,下同
  int saveCount;
  if (!dirtyOpaque) {
    drawBackground(canvas);
  }

  . . . 

  // 绘制自身内容
  if (!dirtyOpaque) onDraw(canvas);

  // 绘制子View
  dispatchDraw(canvas);

   . . .
  // 绘制滚动条等
  onDrawForeground(canvas);

}

ViewGroup类的dispatchDraw()方法中会依次调用drawChild()方法来绘制子View,drawChild()方法的源码如下:

protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
  return child.draw(canvas, this, drawingTime);
}

参考: 深入理解Android之View的绘制流程