說到Android系統View的繪制流程,大家一定知道是分為測量(Measure)、布局(Layout)和繪制(Draw)三個階段,這篇文章主要聊一聊在這三個步驟之前的源碼執行流程,頁面啟動后是怎樣通過代碼執行這三個方法。
之前看過startActivity啟動一個Activity最終都會經過ActivityThread類來調用Activity的生命周期。所以我們就從ActivityThread中調用onResume的performResumeActivity方法開始。
1、ActivityThread類的performResumeActivity方法
首先通過performResumeActivity調用Activity的onResume方法,之前在“四大組件”專欄中分析過這個方法。
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
繼續向下看:
ViewManager wm = a.getWindowManager();
此處省略好多行。。。。
wm.addView(decor,l),
這里第一個參數就是DecorView的實例,執行順序在performResumeActivity后面.
補充一下,此處的wm類型是ViewManager,ViewManager是個接口:
/** Interface to let you add and remove child views to an Activity. To get an instance* of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.*/
public interface ViewManager
{/*** Assign the passed LayoutParams to the passed View and add the view to the window.* <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming* errors, such as adding a second view to a window without removing the first view.* <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a* secondary {@link Display} and the specified display can't be found* (see {@link android.app.Presentation}).* @param view The view to be added to this window.* @param params The LayoutParams to assign to view.*/public void addView(View view, ViewGroup.LayoutParams params);public void updateViewLayout(View view, ViewGroup.LayoutParams params);public void removeView(View view);
}
WindowManager接口繼承ViewManager,順著可以找到WindowManager的實現類WindowManagerImpl。
2、來到WindowManagerImpl類
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {applyDefaultToken(params);mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,mContext.getUserId());
}
3、進入WindowManagerGlobal類
addView方法最底下:
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, userId);
} catch (RuntimeException e) {// BadTokenException or InvalidDisplayException, clean up.if (index >= 0) {removeViewLocked(index, true);}throw e;
}
這里創建ViewRootImpl并且會調用ViewRootImpl的setView方法,這里傳入的view就是上面wm.addView(decor,l)時傳入的decorView他也會一直透傳到ViewRootImpl的setView方法。
4、ViewRootImpl的setView方法
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,int userId) {synchronized (this) {if (mView == null) {mView = view;
下面省略好多行。。。。
上面的view就是從ActivityThread傳遞過來的DecorView實例,因此mView 就是DecorView,add方法下面滑油三個比較重要的點:requestLayout()和mInputEventReceiver以及view.assignParent(this)。
4.1?requestLayout調用scheduleTraversals
@Override
public void requestLayout() {if (!mHandlingLayoutInLayoutRequest) {checkThread();mLayoutRequested = true;scheduleTraversals();}
}@UnsupportedAppUsage
void scheduleTraversals() {if (!mTraversalScheduled) {mTraversalScheduled = true;mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);notifyRendererOfFramePending();pokeDrawLockIfNeeded();}
}
4.2 進入Choreographer類
上面mChoreographer.postCallBack, 這里會調用Choreographer類的postCallBack - postCallBackDelayed - postCallbackDelayedInternal - scheduleFrameLocked
@UnsupportedAppUsage
@TestApi
public void postCallback(int callbackType, Runnable action, Object token) {postCallbackDelayed(callbackType, action, token, 0);
}@UnsupportedAppUsage
@TestApi
public void postCallbackDelayed(int callbackType,Runnable action, Object token, long delayMillis) {if (action == null) {throw new IllegalArgumentException("action must not be null");}if (callbackType < 0 || callbackType > CALLBACK_LAST) {throw new IllegalArgumentException("callbackType is invalid");}postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}private void postCallbackDelayedInternal(int callbackType,Object action, Object token, long delayMillis) {if (DEBUG_FRAMES) {Log.d(TAG, "PostCallback: type=" + callbackType+ ", action=" + action + ", token=" + token+ ", delayMillis=" + delayMillis);}synchronized (mLock) {final long now = SystemClock.uptimeMillis();final long dueTime = now + delayMillis;mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);if (dueTime <= now) {scheduleFrameLocked(now);} else {Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);msg.arg1 = callbackType;msg.setAsynchronous(true);mHandler.sendMessageAtTime(msg, dueTime);}}
}private void scheduleFrameLocked(long now) {if (!mFrameScheduled) {mFrameScheduled = true;if (USE_VSYNC) {if (DEBUG_FRAMES) {Log.d(TAG, "Scheduling next frame on vsync.");}// If running on the Looper thread, then schedule the vsync immediately,// otherwise post a message to schedule the vsync from the UI thread// as soon as possible.if (isRunningOnLooperThreadLocked()) {scheduleVsyncLocked();} else {Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);msg.setAsynchronous(true);mHandler.sendMessageAtFrontOfQueue(msg);}} else {final long nextFrameTime = Math.max(mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);if (DEBUG_FRAMES) {Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");}Message msg = mHandler.obtainMessage(MSG_DO_FRAME);msg.setAsynchronous(true);mHandler.sendMessageAtTime(msg, nextFrameTime);}}
}
上面scheduleFrameLocked方法通過handler發了MSG_DO_FRAME的msg消息。
private final class FrameHandler extends Handler {public FrameHandler(Looper looper) {super(looper);}@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_DO_FRAME:doFrame(System.nanoTime(), 0);break;case MSG_DO_SCHEDULE_VSYNC:doScheduleVsync();break;case MSG_DO_SCHEDULE_CALLBACK:doScheduleCallback(msg.arg1);break;}}
}
?handleMessage中doFrame方法調用?doCallbacks,然后調用CallbackRecord
類的run方法 c.run(frameTimeNanos);
5、進入CallbackRecord
private static final class CallbackRecord {public CallbackRecord next;public long dueTime;public Object action; // Runnable or FrameCallbackpublic Object token;@UnsupportedAppUsagepublic void run(long frameTimeNanos) {if (token == FRAME_CALLBACK_TOKEN) {((FrameCallback)action).doFrame(frameTimeNanos);} else {((Runnable)action).run();}}
}
通過((Runnable)action).run()看到是又回到了4.1中????????mChoreographer.postCallback(
????????????????Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);可以看到是返回了ViewRootImpl的TraversalRunnable的實例mTraversalRunnable。
6、返回ViewRootImpl?
((Runnable)action).run(); ?又調回到ViewRootImpl的TraversalRunnable的run方法調:
final class TraversalRunnable implements Runnable {@Overridepublic void run() {doTraversal();}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
?doTraversal方法調用performTraversals()方法
performTraversals()方法,measure測量、layout布局、draw繪制,都是在這個方法開啟的:
??預測量
windowSizeMayChange |= measureHierarchy(host, lp, res,
????????desiredWindowWidth, desiredWindowHeight);
布局窗口
?relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
控件樹測量
?performMeasure(childWidthMeasureSpec, childHeightMeasureSpec)
布局控件
?performLayout(lp,mWidth,mHeight) ?
繪制控件
?performDraw()
以measure控件樹測量為例看看怎樣調用到View的measure方法:
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {if (mView == null) {return;}Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");try {mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}
}
但是DecorView類沒有measure(int widthMeasureSpec, int heightMeasureSpec)方法,因此執行頂級父類View的measure方法:
onMeasure(widthMeasureSpec, heightMeasureSpec);
View的measure方法會調用onMeasure,DecorView中有此方法因此來到DecorView:
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
這里super又會調用父類FrameLayout中的onMeasure方法,方法很長只截取部分:
FrameLayout根據它的MeasureSpec調用measureChildWithMargin方法來對每一個子View進行測量:
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());if (measureMatchParentChildren) {if (lp.width == LayoutParams.MATCH_PARENT ||lp.height == LayoutParams.MATCH_PARENT) {mMatchParentChildren.add(child);}}}
}
DecorView委托父類對所有子view完成測量。
7、ViewRootImpl的setView方法
再說剩下的兩個重要的點,后面再細致的單獨分析:
?????res=mWindowSession.addToDisplayAsUser(),與requestLayout同在setView方法,負責將窗口添加到WMS上,主要通過mWindowSession與WMS進行系統通信
?????mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper()),與requestLayout同在setView方法,View事件輸入的接收器,接收來自于系統的事件;
?????view.assignParent(this),與requestLayout同在setView方法,assignParent方法在View類實現,將ViewRootImpl自己設置為DecorView的parent,所以ViewTree上的Root就是ViewRootImpl。
才疏學淺,如有錯誤,歡迎指正,多謝。