起因
昨天被人問起Activity的啟動(dòng)過程竞慢,我一陣心虛汤纸,說實(shí)話胰锌,N年前看過一回別人寫的文章骗绕,但是自己從來沒有跟著源碼去研究過Activity的啟動(dòng)過程,所以別人問到后资昧,我只能把從PhoneWindow到DecorView到SetContentView到ViewRootImpl調(diào)用performTraversals()方法酬土,再調(diào)用其內(nèi)部的performMeasure()、performLayout()格带、performDraw()撤缴,從而將布局文件繪制并加載到父類為FrameLayout的DecorView上刹枉,這個(gè)過程雖然沒什么打錯(cuò),但是這個(gè)其實(shí)只是View的繪制流程分支屈呕,與Activity的界面加載有部分重合微宝,真正的Activity啟動(dòng)后,界面加載的流程是要比這個(gè)復(fù)雜的虎眨,懷著慚愧的心情蟋软,今天趕緊打開AndroidStudio,從源碼開始嗽桩,一點(diǎn)一點(diǎn)的扣整個(gè)啟動(dòng)過程岳守。
不過這里先說一下,我無法從Activity的StartActivity()方法開始講碌冶,因?yàn)檫@部分內(nèi)容特別多湿痢,而且相當(dāng)復(fù)雜,我還沒有完全吃透扑庞,所以我的源碼分析過程是從ActivityThread的StartActivityNow開始講解并分析的譬重,如果這個(gè)過程很熟悉,只是為了看前半部分的朋友罐氨,可以轉(zhuǎn)戰(zhàn)到這篇文章下:
(Android 9.0)Activity啟動(dòng)流程源碼分析
過程
現(xiàn)在開始分析臀规,首先打開android.app.ActivityThread類,找到startActivityNow()
package android.app.ActivityThread
public final Activity startActivityNow(Activity parent, String id,
Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state,
Activity.NonConfigurationInstances lastNonConfigurationInstances) {
...
return performLaunchActivity(r, null /* customIntent */);
}
這個(gè)方法主要的作用就是初始化一些參數(shù)后,并調(diào)用同類的performLaunchActivity()
package android.app.ActivityThread
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null;
try{
...
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
...
}
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);
...
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
...
}
首先第7行岂昭,先創(chuàng)建一個(gè)activity的實(shí)例以现;
然后第10行調(diào)用這個(gè)activiy實(shí)例的attach方法狠怨;
然后第16行開始约啊,通過判斷是否啟用了PersistableBundle,來判斷Instrumentation對(duì)象mInstrumentation調(diào)用哪個(gè)Activity的onCreate()方法佣赖,不了解PersistableBundle的可以看這篇文章:
android-1.0-四大組件-PersistableBundle
這里主要看一下attach方法:
package android.app.Activity;
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback) {
...
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
...
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
...
}
要了解這里恰矩,首先要了解一下Activity的組成結(jié)構(gòu):
一個(gè)Activity的內(nèi)部包含一個(gè)PhoneWindow,PhoneWindow有包含一個(gè)DecorView,DecorView其實(shí)就是一個(gè)FrameLayout的子類憎蛤,它的內(nèi)部又包含了TitleActionBar和ContentView,而attach這個(gè)方法外傅,其中一個(gè)重要目的就是初始化PhoneWindow對(duì)象。
現(xiàn)在回到源碼部分俩檬,上面這個(gè)方法萎胰,我羅列出的代碼主要做了三件事:
1.將Activity.mWindow對(duì)象初始化
2.給mWindow設(shè)置WindowManager
3.給mWindowManager賦值。
好了棚辽,attach方法看完后技竟,我們回到performLaunchActivity方法里,現(xiàn)在該mInstrumentation調(diào)用callActivityOnCreate方法了:
package com.app.Instrumentation;
public void callActivityOnCreate(Activity activity, Bundle icicle) {
prePerformCreate(activity);
activity.performCreate(icicle);
postPerformCreate(activity);
}
這里我們主要看activity.performCreate(icicle);這行代碼屈藐,進(jìn)入performCreate方法:
package com.app.Activity;
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
if (persistentState != null) {
onCreate(icicle, persistentState);
} else {
onCreate(icicle);
}
}
最后發(fā)現(xiàn)榔组,執(zhí)行的就是onCreate方法熙尉,而我們寫Activity方法的時(shí)候,一般都會(huì)寫一個(gè)setContentView(layoutId)來設(shè)置界面布局搓扯,這時(shí)我們?cè)倏纯?strong>setContentView方法:
package com.app.Activity;
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
這個(gè)方法其實(shí)做兩件事检痰,第一件事是調(diào)用getWindow()的setContentView方法,而getWindow()返回的是一個(gè)android.app.Window對(duì)象锨推,這個(gè)對(duì)象就是剛剛在attach()中賦值的mWindow成員變量铅歼。
后面的initWindowDecorActionBar方法看名字就是初始化DecorView的ActionBar,這也印證了前面我們講的Activity的框架結(jié)構(gòu),這個(gè)分支就走到這里换可,我們還是繼續(xù)看getWindow().setContentView,由于Window是一個(gè)抽象類谭贪,而Window的setContentView方法其實(shí)是一個(gè)抽象方法,并沒有具體的實(shí)現(xiàn)锦担,所以我們要看的是window的子類:PhoneWindow的setContentView方法:
package com.android.internal.policy.PhoneWindow;
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
view.setLayoutParams(params);
final Scene newScene = new Scene(mContentParent, view);
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
//如果調(diào)用的是同類構(gòu)造方法:setContentView(View view, ViewGroup.LayoutParams params)的話
//則這里的代碼是:mContentParent.addView(view, params);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
...
}
知識(shí)點(diǎn):FEATURE_CONTENT_TRANSITIONS是標(biāo)記當(dāng)前內(nèi)容加載有沒有使用過渡動(dòng)畫俭识,也就是轉(zhuǎn)場(chǎng)動(dòng)畫。
首先我們注意一個(gè)變量洞渔,mContentParent是一個(gè)ViewGroup套媚,而學(xué)過自定義View的同學(xué),肯定知道ViewGroup就是FrameLayout,LinearLayout,RelativeLayout等布局文件的父類磁椒,所以這里有一個(gè)執(zhí)行判斷就是堤瘤,如果mContentParent不為空,并且沒有過度動(dòng)畫就執(zhí)行mContentParent.removeAllViews();來清理界面浆熔,之后通過判斷本辐,沒有過渡動(dòng)畫后,給mContentParent這個(gè)ViewGroup中添加view和布局文件医增。
現(xiàn)在我們來看看installDecor方法:
package com.android.internal.policy.PhoneWindow;
private void installDecor() {
...
if (mDecor == null) {
mDecor = generateDecor(-1);
...
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
...
}
}
上面的方法主要做的其實(shí)就是初始化DecorView慎皱,并將DecorView和PhoneWindow進(jìn)行關(guān)聯(lián),并初始化mContentParent.
看看generateDecor方法:
package com.android.internal.policy.PhoneWindow;
protected DecorView generateDecor(int featureId) {
// System process doesn't have application context and in that case we need to directly use
// the context we have. Otherwise we want the application context, so we don't cling to the
// activity.
Context context;
if (mUseDecorContext) {
Context applicationContext = getContext().getApplicationContext();
if (applicationContext == null) {
context = getContext();
} else {
context = new DecorContext(applicationContext, getContext());
if (mTheme != -1) {
context.setTheme(mTheme);
}
}
} else {
context = getContext();
}
return new DecorView(context, featureId, this, getAttributes());
}
這里注意一下最后new DecorView的時(shí)候傳入了this,這就說明DecorView與PhoneWindow確實(shí)關(guān)聯(lián)了叶骨,并返回了一個(gè)DecorView的實(shí)例茫多。
我們?cè)倏纯?strong>generateLayout方法:
package com.android.internal.policy.PhoneWindow;
protected ViewGroup generateLayout(DecorView decor) {
...
layoutResource = R.layout.screen_title;
...
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
...
mDecor.finishChanging();
return contentParent;
}
這個(gè)方法很長(zhǎng),注意看onResourcesLoaded方法:
if (mBackdropFrameRenderer != null) {
loadBackgroundDrawablesIfNeeded();
mBackdropFrameRenderer.onResourcesLoaded(
this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
getCurrentColor(mNavigationColorViewState));
}
mDecorCaptionView = createDecorCaptionView(inflater);
final View root = inflater.inflate(layoutResource, null);
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 {
// Put it below the color views.
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
initializeElevation();
細(xì)節(jié)太多就不細(xì)說了忽刽,這個(gè)方法主要是創(chuàng)建mDecorCaptionView天揖,然后將傳遞進(jìn)來的布局文件inflate到這個(gè)DecorView中去。
再回到generateLayout方法跪帝,ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
而ID_ANDROID_CONTENT的常量值就是:com.android.internal.R.id.content;
然后根據(jù):layoutResource = R.layout.screen_title;我們打開此布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:fitsSystemWindows="true">
<!-- Popout bar for action modes -->
<ViewStub android:id="@+id/action_mode_bar_stub"
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/action_mode_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?attr/actionBarTheme" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="?android:attr/windowTitleSize"
style="?android:attr/windowTitleBackgroundStyle">
<TextView android:id="@android:id/title"
style="?android:attr/windowTitleStyle"
android:background="@null"
android:fadingEdge="horizontal"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
<FrameLayout android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
發(fā)現(xiàn)最下面的FrameLayout的id屬性就是android:id="@android:id/content"今膊,也就是說這個(gè)FrameLayout其實(shí)就是我們的變量contentParent。
最后放一個(gè)前輩對(duì)于DecorView的總結(jié):
綜合以上的探究伞剑,加上自己的一些思考和猜測(cè)斑唬。對(duì)PhoneWindow做一下小小的總結(jié):
1.一個(gè)Activity對(duì)應(yīng)著一個(gè)PhoneWindow對(duì)象,是一對(duì)一的關(guān)系,如果從Activity A啟動(dòng)到Activity B赖钞,那么Activity B會(huì)創(chuàng)建一個(gè)自己的PhoneWindow對(duì)象腰素。
2.PhoneWindow管理著整個(gè)屏幕的內(nèi)容,不包括屏幕最頂部的系統(tǒng)狀態(tài)條雪营。所以弓千,PhoneWindow或者Window是與應(yīng)用的一個(gè)頁面所相關(guān)聯(lián)萍诱。
3.PhoneWindow同時(shí)管理著ActionBar和下面的內(nèi)容主題宫静,setContentView()方法是用來設(shè)置內(nèi)容主體的,而setTitle()等其他方法就是操作ActionBar的斑司,Window中定義的requestFeature()等方法谴餐,有很多與ActionBar屬性相關(guān)的設(shè)置姻政。另外這些方法都是公有方法,顯然是為了給客戶端程序員調(diào)用的岂嗓,也進(jìn)一步佐證了這些操作的意義與作用汁展。
4.PhoneWindow自己并不是一個(gè)視圖(View),它的成員變量mDecor才是整個(gè)界面的視圖厌殉,mDecor是在generateLayout()的時(shí)候被填充出來的食绿,而actionBar和contentParent兩個(gè)視圖都是通過findViewById()直接從mDecor中獲取出來的。
講到這里公罕,算是把方法installDecor講完了器紧,現(xiàn)在繼續(xù)回到代碼塊:com.android.internal.policy.PhoneWindow的setContentView中去繼續(xù)從installDecor方法往下看,mContentParent.removeAllViews();簡(jiǎn)單的說過了楼眷,這里就不復(fù)述了铲汪,之后PhoneWindow類的setContentView方法最后通過調(diào)用mLayoutInflater.inflate(layoutResID, mContentParent);或者mContentParent.addView(view, params);將我們的xml或者java View插入到了mContentParent(id為content的FrameLayout對(duì)象)ViewGroup中,最后setContentView還會(huì)調(diào)用一個(gè)Callback接口的成員函數(shù)onContentChanged來通知對(duì)應(yīng)的Activity組件視圖內(nèi)容發(fā)生了變化罐柳。
現(xiàn)在重新放一下setContentView這個(gè)代碼段:
package com.android.internal.policy.PhoneWindow;
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
view.setLayoutParams(params);
final Scene newScene = new Scene(mContentParent, view);
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
//如果調(diào)用的是同類構(gòu)造方法:setContentView(View view, ViewGroup.LayoutParams params)的話
//則這里的代碼是:mContentParent.addView(view, params);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
...
}
注意最后幾行代碼掌腰,是由Callback的實(shí)例對(duì)象調(diào)用的onContentChanged方法,進(jìn)入Callback的源碼我們得知硝清,Callback就是定義在抽象類Window中的一個(gè)接口辅斟,而getCallback()也僅僅是獲取Callback接口的實(shí)例,但是這個(gè)Callback具體在哪里實(shí)現(xiàn)的芦拿,我們還得繼續(xù)查,這里分享一下我的查詢方式查邢,我是通過在對(duì)應(yīng)接口上按Ctrl+T的方式羅列出該接口的實(shí)現(xiàn)類蔗崎,如下:
這時(shí)我們就注意到了,一個(gè)親切的家伙就出現(xiàn)在我們面前了扰藕, Activity呀缓苛!對(duì)呀,如果PhoneWindow沒有實(shí)現(xiàn)這個(gè)接口,那么作為組合類的Activity應(yīng)該就會(huì)實(shí)現(xiàn)呀未桥,而且我們回憶一下Activity的attach方法笔刹,呃,不用回憶了冬耿,直接貼再貼一次源碼:
package android.app.Activity;
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
....
}
注意舌菜,mWindow.setCallback(this);這行代碼,這個(gè)this不就代表的是Activity本身嗎亦镶?那么cb.onContentChanged();方法不就是Activity的onContentChanged()方法嗎日月?我們看一下:
package android.app.Activity;
public void onContentChanged() {
}
Activity的onContentChanged()是一個(gè)空方法,這就是說缤骨,etContentView()或者addContentView()方法執(zhí)行完畢時(shí)就會(huì)調(diào)用該方法爱咬,那么我們知道這個(gè)邏輯后,以后有什么布局二次變化的需求后绊起,就可以將組件初始化的代碼精拟,如:findViewById()或者一些參數(shù)的初始化等業(yè)務(wù)代碼放在我們App對(duì)應(yīng)的Activity重寫的onContentChanged()方法中去,讓系統(tǒng)幫忙回調(diào)虱歪。
現(xiàn)在來總結(jié)一下setContentView做的事:
創(chuàng)建一個(gè)DecorView的對(duì)象mDecor串前,該mDecor對(duì)象將作為整個(gè)應(yīng)用窗口的根視圖。
依據(jù)Feature等style theme創(chuàng)建不同的窗口修飾布局文件实蔽,并且通過findViewById獲取Activity布局文件該存放的地方(窗口修飾布局文件中id為content的FrameLayout)荡碾。
將Activity的布局文件添加至id為content的FrameLayout內(nèi)。
題目叫做Activity的加載和顯示局装,前面講的都是Activity的加載坛吁,現(xiàn)在講講Activity的顯示吧,至于為什么會(huì)是這個(gè)調(diào)用順序或執(zhí)行過程铐尚,那個(gè)需要單開一篇文章細(xì)說拨脉,這里只分析具體的加載和顯示的源碼過程,現(xiàn)在我們來看Activity中的handleResumeActivity方法:
package android.app.ActivityThread;
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
if (r == null) {
// We didn't actually resume the activity, so skipping any follow-up actions.
return;
}
...
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);
}
}
...
performResumeActivity()這個(gè)方法追到底宣增,其主要就是Instrumentation調(diào)用了Acitivy的onResume()方法玫膀,我們了解就好,然后主要要看的是wm.addView(decor, l);爹脾,這里就是要?jiǎng)诱娓竦牧颂迹覀兝^續(xù)往下追:
package android.view;
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);
}
addView是接口ViewManager的一個(gè)方法,但是我們很詫異的是wm沒記錯(cuò)的話應(yīng)該是windowManager的實(shí)例啊灵妨,怎么成了ViewManager了解阅?我們看一下handleResumeActivity的這行代碼:
ViewManager wm = a.getWindowManager();
通過追蹤這個(gè)方法的調(diào)用,發(fā)現(xiàn)其實(shí)這里的設(shè)計(jì)是這樣的:
package android.view;
@SystemService(Context.WINDOW_SERVICE)
public interface WindowManager extends ViewManager {
...
}
WindowManager接口繼承了ViewManager接口泌霍,從而在加載View時(shí)就使用了ViewManager中的addView方法货抄,現(xiàn)在可以知道的是addView只是一個(gè)抽象方法,我們需要找到WindowManager的實(shí)現(xiàn)類,查看addView的源碼而WindowManagerImpl就是WindowManager的實(shí)現(xiàn)類蟹地,我們查看這個(gè)類的addView方法:
package android.view;
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
...
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
...
}
其實(shí)本質(zhì)上調(diào)用的是WindowManagerGlobal的addView方法积暖,我們進(jìn)去看一下:
package android.view;
public final class WindowManagerGlobal {
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
root = new ViewRootImpl(view.getContext(), display);
...
root.setView(view, wparams, panelParentView);
...
}
}
這段代碼主要的一個(gè)作用是調(diào)用了ViewRootImpl的setView方法,我們繼續(xù)追蹤:
package android.view.ViewRootImpl;
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
...
requestLayout();
...
view.assignParent(this);
}
}
首先怪与,此方法會(huì)把之前傳進(jìn)來的參數(shù)view賦值給mView夺刑,mView其實(shí)就是handleResumeActivity中的wm.addView時(shí)傳進(jìn)來的DecorView,而DecorView又是一個(gè)FrameLayout,這里其實(shí)就是將setContentView所做的一切初始化操作的DecorView設(shè)置成這個(gè)Activity的最基礎(chǔ)的視圖框架琼梆,具體見代碼:
view.assignParent(this);
然后調(diào)用了requestLayout()方法來顯示界面內(nèi)容:
package android.view.ViewRootImpl;
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
先判斷當(dāng)前線程是不是主線程性誉,然后就調(diào)用了scheduleTraversals()方法,繼續(xù)跟進(jìn):
package android.view.ViewRootImpl;
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);通過跟進(jìn)源碼得知其本質(zhì)就是通過handler發(fā)送消息茎杂,那么我們關(guān)注的重點(diǎn)就應(yīng)該是mTraversalRunnable這個(gè)Runnable接口:
package android.view.ViewRootImpl;
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
進(jìn)doTraversal方法看看:
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
繼續(xù)跟進(jìn)performTraversals方法:
private void performTraversals() {
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
...
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
performLayout(lp, mWidth, mHeight);
...
performDraw();
...
}
這個(gè)方法的邏輯非常長(zhǎng)错览,反正我是沒看完,但是我們可以注意到煌往,這三個(gè)方法和兩個(gè)屬性的初始化倾哺,其主要作用其實(shí)就是對(duì)基礎(chǔ)的根節(jié)點(diǎn)View進(jìn)行View的初始化操作,也就是我們常說的onMeasure(測(cè)量)刽脖、onLayout(布局)和onDraw(繪制)羞海,而childWidthMeasureSpec和childHeightMeasureSpec主要的作用就是為測(cè)量提供測(cè)量規(guī)格,這里具體的內(nèi)容可以看我的另一篇文章:Android自定義View的測(cè)量過程詳解
總結(jié)
千言萬語的總結(jié)不如最后繪成一張圖來的清晰明了: