一切從setContentView方法開始
原始碼把佈局檔案設定給window物件
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}複製程式碼
那麼檢視window的setContentView方法
@Override
public void setContentView(int layoutResID) {
//mContentParent為null,執行installDecor方法。
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
//把佈局新增到mContentParent中
mLayoutInflater.inflate(layoutResID, mContentParent);
}
...
}複製程式碼
mContentParent又是什麼?
跟蹤installDecor方法
private void installDecor() {
//建立Decor物件
if (mDecor == null) {
mDecor = generateDecor(-1);
}
if (mContentParent == null) {
//獲取mContentParent物件
mContentParent = generateLayout(mDecor);
}
...
}複製程式碼
接著跟蹤generateLayout方法
protected ViewGroup generateLayout(DecorView decor) {
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
return contentParent;
}複製程式碼
至此,我們初始化了DecorView佈局,並且把我們佈局檔案載入到了DecorView中。
同時例項化了Window
我們回到開始,去查詢Window物件在哪裡建立的,通過原始碼可以發現在Activity#attach中對其進行了賦值操作(在Activity啟動過程中,會執行ActivityThread#performLaunchActivity方法,在這個方法中會呼叫Activity#attach方法)。
在Activity在啟動流程中當執行了ActivityThread#performLaunchActivity方法後還會執行ActivityThread#handleResumeActivity方法,在這個方法中首先會呼叫Activity的onResume方法,接著呼叫Activity的makeVisible方法。
void makeVisible() {
if (!mWindowAdded) {
//獲取WindowManager
ViewManager wm = getWindowManager();
//通過WindowManager完成window和DecorView的關聯
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);//然後讓佈局顯示
}複製程式碼
那麼WindowManager是怎麼完成window和DecorView的關聯的呢?
繼續跟蹤原始碼,找windowManager類中的addView方法,windowManager是一個介面,我們找他的實現類WindowManagerImpl
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}複製程式碼
發現WindowManagerImpl#addView並沒有實現具體細節,而是交WindowManagerGlobal中的addView去處理。
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
ViewRootImpl root;
synchronized (mLock) {
// 例項化ViewRootImpl,它是WindowManager和DecorView的處理類
root = new ViewRootImpl(view.getContext(), display);
//這是佈局引數
view.setLayoutParams(wparams);
mViews.add(view);//儲存所有Window對應的View
mRoots.add(root);//儲存所有Window對應的ViewRootImpl
mParams.add(wparams);//儲存所有Window對應的佈局引數
}
try {
//接著呼叫ViewRootImpl中的setView方法
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
}
}複製程式碼
注意:WindowManagerGlobal類裡邊有所有View的物件。
接著來看ViewRootImpl#setView方法
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
//內部最終會呼叫performTraversals方法開啟View的繪製流程。
requestLayout();
try {
//通過IWindowSession來完成Window的新增過程,這是和系統的IPC過程,我分析不出來了
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {
} finally {
}
}
}
}複製程式碼
看怎麼開啟繪製的還是可以分析出來的
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
//呼叫這個方法
scheduleTraversals();
}
}複製程式碼
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//傳送runnable
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}複製程式碼
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}複製程式碼
private void performTraversals() {
...
//執行測量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
//執行佈局
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
...
//執行繪製
performDraw();
}複製程式碼
在performTraversals方法依次會執行performMeasure、performLayout以及performDraw來完成對頂級View的測量、佈局、繪製。
- 把佈局掛載到DecorView中
- 通過WindowManager新增到window到螢幕上(ipc過程不會分析)
- 通過ViewRootImpl完成window和DecorView的關聯(Ipc過程不會分析)
- ViewRootImpl中去觸發performTraversals去執行測量,佈局,和繪製的執行
那麼我們就可以說activity中的setContentView方法出發了ViewRootImpl類中的performTraversals去執行測量佈局和繪製。