android高级ui绘制,高级UI<第九篇>:源码分析UI的绘制流程(Android 9.0)

本文其实是第七篇的续篇,第七篇主要讲解Activity的绘制流程,本文主要讲解View本身是如何绘制的,即UI的绘制流程。

花了几个小时的源码分析,最终确认,UI实际上从Activity的onResume之后才真正开始绘制,或者说,UI绘制的入口是ActivityThread类中的handleResumeActivity方法。

源码如下:

@Override

public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,

String reason) {

//...(省略)

if (r.window == null && !a.mFinished && willBeVisible) {

r.window = r.activity.getWindow();

View decor = r.window.getDecorView();

decor.setVisibility(View.INVISIBLE);

ViewManager wm = a.getWindowManager();

WindowManager.LayoutParams l = r.window.getAttributes();

a.mDecor = decor;

l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;

l.softInputMode |= forwardBit;

if (r.mPreserveWindow) {

a.mWindowAdded = true;

r.mPreserveWindow = false;

// Normally the ViewRoot sets up callbacks with the Activity

// in addView->ViewRootImpl#setView. If we are instead reusing

// the decor view we have to notify the view root that the

// callbacks may have changed.

ViewRootImpl impl = decor.getViewRootImpl();

if (impl != null) {

impl.notifyChildRebuilt();

}

}

if (a.mVisibleFromClient) {

if (!a.mWindowAdded) {

a.mWindowAdded = true;

wm.addView(decor, l);

} else {

// The activity will get a callback for this {@link LayoutParams} change

// earlier. However, at that time the decor will not be set (this is set

// in this method), so no action will be taken. This call ensures the

// callback occurs with the decor set.

a.onWindowAttributesChanged(l);

}

}

//...(省略)

}

以上代码给我们的信息如下:

【通过Activity获取Window对象】

r.window = r.activity.getWindow();

Window对象是UI的顶级视图,一般所谓的Window就是PhoneWindow。

【通过Window获取DecorView对象】

View decor = r.window.getDecorView();

DecorView是显示Activity的根视图,其作用是现实状态栏、显示ActionBar、显示setContentView设置的布局以及底部导航栏。

【通过DecorView获取ViewRootImpl对象】

ViewRootImpl impl = decor.getViewRootImpl();

ViewRootImpl对象主要负责View的绘制,DecorView和ViewRootImpl 互相绑定,一个DecorView只能有一个ViewRootImpl 实例。

【将DecorView添加到Window中】

wm.addView(decor, l);

那么,ViewRootImpl是如何绘制View的呢?

当DecorView添加到Window中时,View就开始了绘制,那么就跟踪以下其过程吧,请看下面的跟踪流程:

【第一步】

wm.addView(decor, l);

【第二步】执行WindowManagerImpl中的addView方法

@Override

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {

applyDefaultToken(params);

mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);

}

【第三步】执行WindowManagerGlobal的addView方法

public void addView(View view, ViewGroup.LayoutParams params,

Display display, Window parentWindow) {

//...(省略)

root = new ViewRootImpl(view.getContext(), display);

view.setLayoutParams(wparams);

mViews.add(view);

mRoots.add(root);

mParams.add(wparams);

// do this last because it fires off messages to start doing things

try {

root.setView(view, wparams, panelParentView);

} catch (RuntimeException e) {

// BadTokenException or InvalidDisplayException, clean up.

if (index >= 0) {

removeViewLocked(index, true);

}

throw e;

}

}

}

代码中最核心代码是:

root.setView(view, wparams, panelParentView);

这里的root就是ViewRootImpl对象。

【第四步】执行ViewRootImpl的setView方法

/**

* We have one child

*/

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {

//...(省略)

// Schedule the first layout -before- adding to the window

// manager, to make sure we do the relayout before receiving

// any other events from the system.

requestLayout();

//...(省略)

}

requestLayout:在添加到窗口管理器之前安排第一个布局,以确保在从系统接收任何其他事件之前执行重新布局。

【第五步】执行ViewRootImpl的requestLayout方法,以完成初步布局

@Override

public void requestLayout() {

if (!mHandlingLayoutInLayoutRequest) {

checkThread();

mLayoutRequested = true;

scheduleTraversals();

}

}

【第六步】执行ViewRootImpl的scheduleTraversals方法,向主线程发送消息请求遍历。

void scheduleTraversals() {

if (!mTraversalScheduled) {

mTraversalScheduled = true;

mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();

mChoreographer.postCallback(

Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

if (!mUnbufferedInputDispatch) {

scheduleConsumeBatchedInput();

}

notifyRendererOfFramePending();

pokeDrawLockIfNeeded();

}

}

核心代码如下:

mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

所以,执行TraversalRunnable的run方法

final class TraversalRunnable implements Runnable {

@Override

public void run() {

doTraversal();

}

}

【第七步】执行ViewRootImpl的doTraversal方法

void doTraversal() {

if (mTraversalScheduled) {

mTraversalScheduled = false;

mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

if (mProfile) {

Debug.startMethodTracing("ViewAncestor");

}

performTraversals();

if (mProfile) {

Debug.stopMethodTracing();

mProfile = false;

}

}

}

【第八步】执行ViewRootImpl的performTraversals方法,开始进入UI的绘制

private void performTraversals() {

//...(省略)

performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

//...(省略)

performLayout(lp, mWidth, mHeight);

//...(省略)

performDraw();

//...(省略)

}

最终会依次执行performMeasure、performLayout、performDraw这三个方法。

查看这三个方法的源码之后发现,绘制UI,其实就是分别执行了以下三个方法:

//布局的测量

mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);

//布局的摆放

host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

//布局的绘制

mAttachInfo.mTreeObserver.dispatchOnDraw()

listeners.get(i).onDraw();

最终执行View的以下三个方法:

//布局的测量

onMeasure(widthMeasureSpec, heightMeasureSpec);

//布局的摆放

onLayout(changed, l, t, r, b);

//布局的绘制

onDraw()

总结:

当执行到handleResumeActivity时,Activity的onResume方法被调用,然后WindowManager会将DecorView设置给ViewRootImpl,这样,DecorView就被加载到Window中了,此时界面还没有显示出来,还需要经过View的measure,layout和draw方法,才能完成View的工作流程。我们需要知道View的绘制是由ViewRoot来负责的,每一个DecorView都有一个与之关联的ViewRoot,这种关联关系是由WindowManager维护的,将DecorView和ViewRoot关联之后,ViewRootImpl的requestLayout会被调用以完成初步布局,通过scheduleTraversals方法向主线程发送消息请求遍历,最终调用ViewRootImpl的performTraversals方法,这个方法会执行View的measure、layout和draw流程。

[本章完...]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值