?本篇文章主要從activity的setContentView()方法著手,講解我們的布局文件是如何添加到根布局,并最終顯示到屏幕上的。
一、View添加到DecorView的過程
首先,我們先從Activity的setContentView()方法開始分析:
public void setContentView(@LayoutRes int layoutResID) {
? ? ? ? getWindow().setContentView(layoutResID);
? ? ?
? initWindowDecorActionBar();}
getWindow()獲取Window對象。我們知道在Android中,PhoneWindow是Window唯一的實(shí)現(xiàn)類,所以我們看一下PhoneWindow中的setContentView()方法
public void setContentView(int layoutResID) {
? ? ????
if (mContentParent ==null) {? ? ? ? ? ? ? ? // 1、首先調(diào)用installDecor()
????????????????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 {? ? ? ? ? ? ? ? // 2、將系統(tǒng)layoutResId對應(yīng)的布局文件添加到DecorView后,再將開發(fā)者設(shè)置的布局文件添加到mContentParent中
?????????????????mLayoutInflater.inflate(layoutResID, mContentParent);
????????}
}
首次進(jìn)入,mContentParent一定為空,所以會調(diào)用installDecor()方法:(第2步,我們稍后再說)
private void installDecor() {
????????if (mDecor == null) {
? ? ? ? ? ? ? ? // 1、創(chuàng)建mDecor
?????????????????mDecor = generateDecor(-1);?
? ? ? ? }
? ??????if (mContentParent == null) {? ? ? ? ? ? ? ? // 2、創(chuàng)建mContentParent
?????????????????mContentParent = generateLayout(mDecor);
? ? ? ? }
}
我們先來看一下第1個(gè)步驟:生成mDecor:
protected DecorViewgenerateDecor(int featureId) {
? ? ? ? // DecorView繼承自FrameLayout
????????return new DecorView(context, featureId, this, getAttributes());
}
直接new創(chuàng)建對象,沒什么好說的。接著看第2步,生成mContentParent:
protected ViewGroupgenerateLayout(DecorView decor) {
? ? ? ? // 1、通過讀取配置文件,設(shè)置Window屬性
? ??????if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
?????????????????requestFeature(FEATURE_NO_TITLE);
????????} else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
????????????????requestFeature(FEATURE_ACTION_BAR);
????????}
? ??????if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) {
?????????????????requestFeature(FEATURE_SWIPE_TO_DISMISS);
????????}
????????if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
?????????????????setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
????????}
? ? ? ? ......
? ? ? ? //2、 通過features,來為layoutResource賦值
? ??????int layoutResource;
? ??????if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
?????????????????layoutResource = R.layout.screen_swipe_dismiss;
?????????????????setCloseOnSwipeEnabled(true);
????????} else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
????????????????if (mIsFloating) {
?????????????????????????TypedValue res = new TypedValue();
?????????????????????????getContext().getTheme().resolveAttribute( R.attr.dialogTitleIconsDecorLayout, res, true);
?????????????????????????layoutResource = res.resourceId;
????????????????} else {
?????????????????????????layoutResource = R.layout.screen_title_icons;
?????????????????}
????????}????????......
????????else {
????????????????layoutResource = R.layout.screen_simple;
????????}
? ??????3、mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
}
這段代碼主要有三個(gè)步驟:
? ? 1、通過Window Style樣式,設(shè)置Window風(fēng)格
? ? 2、加載系統(tǒng)中對應(yīng)的layoutResource,作為基礎(chǔ)布局。(開發(fā)者自己通過setContentView(int layoutId)設(shè)置的布局文件就是添加到其中的)
? ? 3、當(dāng)資源加載完畢,會調(diào)用DecorView的onresourcesLoaded()方法,進(jìn)而將上述基礎(chǔ)布局添加到頂層布局mDecorView中:
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
????????if (mDecorCaptionView != null) {
?????????????????if (mDecorCaptionView.getParent() == null) {
?????????????????????????addView(mDecorCaptionView, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
?????????????????}
?????????????????mDecorCaptionView.addView(root, new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
????????} else {
????????????????addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
????????}
}
通過addView(),最終將layoutResources添加到DecorView中。
最終的布局層次,我們以R.layout.screen_simple為例來分析,請看下圖:

說到這里,通過setContentView()設(shè)置的布局文件就已經(jīng)添加到了DecorView中,View的樹形結(jié)構(gòu)就已經(jīng)存在了。
接下來只需要將該View樹添加到Window中,并執(zhí)行View的測量流程完畢,該View就會顯示到屏幕上。
二、將View樹添加到Window中
ActivityThread是Activity的啟動入口, 在它內(nèi)部有一個(gè)內(nèi)部類H extends Handler,專門用來處理主線程的消息。
class H extends Handler {
????public void handleMessage(Message msg) {
? ? ????switch (msg.what) {
????????????case LAUNCH_ACTIVITY: {
? ? ? ? ? ? ???
?final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
? ? ? ? ? ????? r.packageInfo = getPackageInfoNoCheck(
????????????????r.activityInfo.applicationInfo, r.compatInfo);? ? ? ? ? ? ????handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
? ? ? ? }
????????break;
當(dāng)啟動一個(gè)新的Activity,會觸發(fā)handleLaunchActivity()方法:
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
......
// 1、創(chuàng)建Activity
Activity a = performLaunchActivity(r, customIntent);
......
// 2、調(diào)用activity的onResume方法
handleResumeActivity(r.token, false, r.isForward, !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
我們先看一下performLaunchActivity()方法的源碼:
private ActivityperformLaunchActivity(ActivityClientRecord r, Intent customIntent) {
????Activity activity =null;
????try {
????????????java.lang.ClassLoader cl = appContext.getClassLoader();
? ? ? ? ? ? // 1、創(chuàng)建Activity
? ? ????????activity =mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
? ????? ?} catch (Exception e) {
?????????}
? ? // 2、創(chuàng)建Application
????Application app = r.packageInfo.makeApplication(false, mInstrumentation);
? ? // 3、創(chuàng)建Window
? ??Window window =null;
????if (r.mPendingRemoveWindow !=null && r.mPreserveWindow) {
????????????window = r.mPendingRemoveWindow;
? ? ????????r.mPendingRemoveWindow =null;
? ? ????????r.mPendingRemoveWindowManager =null;
????}
? ? // 4、關(guān)聯(lián)activity和window
????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.configCallback);
通過源碼,可以看到,performLaunchActivity()方法主要是創(chuàng)建Activity、Application、Window對象,并關(guān)聯(lián)到一起。緊接著,調(diào)用activity的onCreate()方法,進(jìn)而調(diào)用setContentView()方法,構(gòu)建View樹。
然后我們再看一下handleResumeActivity()方法的源碼:
final void handleResumeActivity(IBinder token,? boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) { ? ? ? ? ? ? // 1、創(chuàng)建 ViewManager?對象
????????ViewManager wm = a.getWindowManager();
????????WindowManager.LayoutParams l = r.window.getAttributes();
????????if (a.mVisibleFromClient) {
????????????if (!a.mWindowAdded) {
????????????????????a.mWindowAdded =true;
? ? ? ? ? ? ? ? ? ? // 2、將DecorView添加到Window中
? ? ? ? ????????????wm.addView(decor, l);
????????}else {
? ? ? ? ????a.onWindowAttributesChanged(l);
? ? ????}
}
在該方法內(nèi)部,創(chuàng)建ViewManager對象,并調(diào)用addView()方法,將DecorView添加到Window中。(通過查看源碼,ViewManager.addView() --> WindowManager.addView() --> WindowManagerGlobal.addView(),這不是本文的重點(diǎn),就不一一列舉了)
至此,View樹就已經(jīng)添加到Window中,但是此時(shí)用戶還看不到界面,因?yàn)檫€沒有進(jìn)行繪制。所以接下來就需要將該View繪制出來
三、View的繪制流程:
我們看一下WindowManagerGlobal.addView(),在該方法內(nèi)部會調(diào)用ViewRootImpl的setView()方法,在該方法內(nèi)部,會調(diào)用requestLayout()方法,進(jìn)而出發(fā)View的繪制流程:
public void requestLayout() {
????if (!mHandlingLayoutInLayoutRequest) {
? ? ? ? ? ? // 1、檢查是否在主線程
????????????checkThread();
? ? ? ? ? ? // 2、執(zhí)行traversal
? ? ? ????? scheduleTraversals();
? ? }
}
void scheduleTraversals() {
????????if (!mTraversalScheduled) {
? ? ? ????????? mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
? ? ????}
}
final TraversalRunnablemTraversalRunnable =new TraversalRunnable();
final class TraversalRunnableimplements Runnable {
????@Override
? ? public void run() {
????????doTraversal();
? ? }
}
void doTraversal() {
????if (mTraversalScheduled) {
????????????performTraversals();
? ? ? ? }
????}
}
private void performTraversals() {
????????performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
????????performLayout(lp, mWidth, mHeight);
????????performDraw();
}
從源碼中,可以看到,最終在performTraversals()方法中,執(zhí)行了View的測量、布局和繪制過程。
至此,View繪制完畢后,就可以顯示到屏幕上了。
