本篇文章所分析源碼基于android 6.0.1
一.從setContentView()說起
從我們寫第一個(gè)Android程序的時(shí)候,就會(huì)在MainActivity的onCreat()函數(shù)中寫下setContentView(R.layout.activity_main);這句代碼,我們知道這句是將Activity與xml可視化文件關(guān)聯(lián)的代碼,但是在我們寫下這句代碼的時(shí)候,我們有沒有想過這句代碼的背后系統(tǒng)到底做了什么?~~前一陣子被我們組內(nèi)的學(xué)長問起這個(gè)問題的時(shí)候還真是自覺慚愧,無奈前一陣子為了趕項(xiàng)目進(jìn)度確實(shí)沒有時(shí)間深究這個(gè)問題,今天那我們就來從這句司空見慣的代碼入手,開始我們的View事件分發(fā)機(jī)制前傳之旅.
??首先我們將源碼定位到(frameworks/base/core/java/android/app/Activity):
/**
* Set the activity content from a layout resource. The resource will be
* inflated, adding all top-level views to the activity.
*
* @param layoutResID Resource ID to be inflated.
*
* @see #setContentView(android.view.View)
* @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
*/
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
可以看到,在我們執(zhí)行setContentView()的時(shí)候,源碼中調(diào)用了getWindow().setContentView(layoutResID);
這句代碼,那么這個(gè)getWindow()得到的是什么東西呢?我們繼續(xù)追蹤源碼(frameworks/base/core/java/android/app/Activity):
/**
* Retrieve the current {@link android.view.Window} for the activity.
* This can be used to directly access parts of the Window API that
* are not available through Activity/Screen.
*
* @return Window The current window, or null if the activity is not
* visual.
*/
public Window getWindow() {
return mWindow;
}
根據(jù)這段代碼的注釋的一句:檢索當(dāng)前Activity的Window(其中{@link android.view.Window}給出的是Window類的地址:frameworks/base/core/java/android/view/Window),以及最后一句@return~~當(dāng)前的window,或者當(dāng)activity不可見的時(shí)候返回null(這里我們是不是可以get一個(gè)點(diǎn)呢?)----從這兩句注釋我們可以看出,這個(gè)返回值mWindow是一個(gè)Window類型的對(duì)象,但是這個(gè)Window類是一個(gè)抽象類,那么我們繼續(xù)在當(dāng)前類中須找,看看這個(gè)mWindow是在哪里賦值的(frameworks/base/core/java/android/app/Activity):
private Window mWindow;
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) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
......
}
在Activity類的attach()方法中我們找到了這么一句mWindow = new PhoneWindow(this);可以看到這里實(shí)例化了PhoneWindow類,mWindow是PhoneWindow類的對(duì)象,而點(diǎn)開PhoneWindow的源碼我們會(huì)發(fā)現(xiàn)它繼承自Window類,由此我們可以知道,PhoneWindow類是Window的具體實(shí)現(xiàn)類.
???那么我們繼續(xù)追蹤到PhoneWindow類中,看看他的setContentVeiw()方法實(shí)現(xiàn)了什么(frameworks/base/core/java/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) { //當(dāng)mContentParent為空的時(shí)候,表示當(dāng)前內(nèi)容還未放置到窗口,也就是第一次調(diào)用的時(shí)候
installDecor(); // 創(chuàng)建并添加DecorView
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { //如果內(nèi)容已經(jīng)加載過(不是第一次加載),并且不需要?jiǎng)赢?removeAllViews()
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,getContext());
transitionTo(newScene); //添加完Content后如有設(shè)置了FEATURE_CONTENT_TRANSITIONS則添加Scene來過度啟動(dòng)
} else {
mLayoutInflater.inflate(layoutResID, mContentParent); //否則將我們的資源文件通過LayoutInflater對(duì)象轉(zhuǎn)換為View樹,并且添加至mContentParent視圖中.
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
(題外話:注意到PhoneWindow類的路徑中有internal這個(gè)詞,也就是說這個(gè)是內(nèi)部API,我們是不能在開發(fā)中通過SDK來正常調(diào)用的)可以看到在這段代碼中,首先判斷mContentParent是否位null,那么這個(gè)mContentParent又是什么鬼呢?
// This is the view in which the window contents are placed. It is either
// mDecor itself, or a child of mDecor where the contents go.
private ViewGroup mContentParent;
結(jié)合注釋:這是放置Window內(nèi)容的View,它或者是mDecor本身,或者是內(nèi)容所在的mDecor的子元素.,注意到這句代碼:mLayoutInflater.inflate(layoutResID, mContentParent);
public View inflate (int resource, ViewGroup root) ----可以看到,layoutResID就是我們在MainActivity的setContentView中設(shè)置的R.layout.activity_main,而這個(gè)mContentParent是activity_main.xml的父布局~~上面提到了mContentParent是mDecor本身或者是mDecor的一個(gè)子元素,這句話是什么意思呢?這個(gè)作為(問題)先留下,待會(huì)會(huì)我們通過源碼分析.
??解釋一下上面的一段代碼----當(dāng)mContentParent為空的時(shí)候,表示當(dāng)前內(nèi)容還未放置到窗口,也就是第一次調(diào)用的時(shí)候,此時(shí)調(diào)用installDecor()。這里我們得說一下FEATURE_CONTENT_TRANSITIONS標(biāo)志位荷憋,這個(gè)是標(biāo)記當(dāng)前內(nèi)容加載有沒有使用過度動(dòng)畫串前,也就是轉(zhuǎn)場動(dòng)畫荡碾。如果內(nèi)容已經(jīng)加載過(else表示不是第一次加載),并且不需要?jiǎng)赢嫞瑒t會(huì)調(diào)用removeAllViews()。添加完Content后如有設(shè)置了FEATURE_CONTENT_TRANSITIONS則添加Scene來過度啟動(dòng)统舀。否則mLayoutInflater.inflate(layoutResID, mContentParent);將我們的資源文件通過LayoutInflater對(duì)象轉(zhuǎn)換為View樹,并且添加至mContentParent視圖中闷串。
??整理一下第一節(jié)內(nèi)容:Activity通過PhoneWindow的setContentView方法來設(shè)置布局,而設(shè)置布局之前肋拔,會(huì)先判斷是否存在mContentParent,而我們設(shè)置的布局文件則是mContentParent的子元素窿吩。
二.創(chuàng)建DecorView并添加至mContentParent
我們緊接著上面的源碼分析:
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
當(dāng)mContentParent為空的時(shí)候,調(diào)用installDecor():
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor(); ①
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
//一開始DecorView未加載到mContentParent,所以此時(shí)mContentParent=null
if (mContentParent == null) {
mContentParent = generateLayout(mDecor); ② //該方法將mDecorView添加到Window上綁定布局
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeOptionalFitsSystemWindows();
final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
R.id.decor_content_parent);
......//設(shè)置TitleView(TextView),Backdroud等資源
......//設(shè)置有無轉(zhuǎn)場動(dòng)畫以及轉(zhuǎn)場動(dòng)畫的種類
}
}
首先看①處這段代碼:
if (mDecor == null) {
mDecor = generateDecor();
......
}
在這個(gè)if中,我們通過generateDecor()方法創(chuàng)建mDecor的實(shí)例:
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}
這里實(shí)例化了DecorView()類,然后呢我們深入到DecorView()類中去看,可以發(fā)現(xiàn)DecorView()類是PhoneWindow()類的一個(gè)內(nèi)部類(frameworks/base/core/java/com/android/internal/policy/PhoneWindow):
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
/* package */int mDefaultOpacity = PixelFormat.OPAQUE;
/** The feature ID of the panel, or -1 if this is the application's DecorView */
private final int mFeatureId;
......
public DecorView(Context context, int featureId) {
super(context);
mFeatureId = featureId;
......
}
然后我們注意到②處代碼:
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
......
可以看到在這里通過generateLayout()方法將decorView添加到mContentParent中:
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.應(yīng)用當(dāng)前theme的數(shù)據(jù),下面一連串的if就是根據(jù)你設(shè)置的theme來判斷當(dāng)前加載的形式的
TypedArray a = getWindowStyle();
....
//設(shè)置style為Window_windowNoTitle或者Window_windowActionBar
if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
requestFeature(FEATURE_NO_TITLE);
} else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
// Don't allow an action bar if there is no title.
requestFeature(FEATURE_ACTION_BAR);
}
....
//設(shè)置是否全屏
if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
}
......//一大堆if判斷style資源
//這里開始根據(jù)gradle中的targetSdkVersion來判斷是否需要加載菜單欄
final Context context = getContext();
final int targetSdk = context.getApplicationInfo().targetSdkVersion;
final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;
final boolean targetHcNeedsOptions = context.getResources().getBoolean(
R.bool.target_honeycomb_needs_options_menu);
final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);
if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {
setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE);
} else {
setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_FALSE);
}
//高端設(shè)備上的非浮動(dòng)窗口必須放在系統(tǒng)欄下方闲勺,因此必須知道這些窗口的可見性變化翘地。
// Non-floating windows on high end devices must put up decor beneath the system bars and
// therefore must know about visibility changes of those.
if (!mIsFloating && ActivityManager.isHighEndGfx()) {
if (!targetPreL && a.getBoolean(
R.styleable.Window_windowDrawsSystemBarBackgrounds,
false)) {
setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags());
}
}
......//省略多個(gè)if判斷
WindowManager.LayoutParams params = getAttributes();
......//省略其他加載資源
// Inflate the window decor.
//添加布局到DecorView,前面說到橙喘,DecorView是繼承與FrameLayout,它本身也是一個(gè)ViewGroup和簸,
//而我們前面創(chuàng)建它的時(shí)候,只是調(diào)用了new DecorView身诺,此時(shí)里面并無什么東西。而下面的步奏則是根據(jù)
//用戶設(shè)置的Feature來創(chuàng)建相應(yīng)的布局主題穴亏。
//舉個(gè)例子,如果我在setContentView之前調(diào)用了requestWindowFeature(Window.FEATURE_NO_TITLE)刺覆,
//這里則會(huì)通過getLocalFeatures來獲取你設(shè)置的feature驳糯,進(jìn)而加載對(duì)應(yīng)的布局,此時(shí)是加載沒有標(biāo)題欄的主題.
//這也就是為什么我們在代碼中設(shè)置Theme或者requesetFeature()的時(shí)候必須在setContentView之前的原因.
int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
} 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;
}
// XXX Remove this once action bar supports these features.
removeFeature(FEATURE_ACTION_BAR);
// System.out.println("Title Icons!");
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
......//一大堆else if條件判斷
} else {
// Embedded, so no decoration is needed.
layoutResource = R.layout.screen_simple; ③
// System.out.println("Simple!");
}
mDecor.startChanging();
//選擇對(duì)應(yīng)布局創(chuàng)建添加到DecorView中
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); //往DecorView中添加子View,即mContentParent
mContentRoot = (ViewGroup) in;
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
......
mDecor.finishChanging();
return contentParent;
}
由以上代碼可以看出,該方法還是做了相當(dāng)多的工作的卑笨,首先根據(jù)設(shè)置的主題樣式來設(shè)置DecorView的風(fēng)格妖滔,比如說有沒有titlebar之類的沮翔,接著為DecorView添加子View采蚀,而這里的子View則是上面提到的mContentParent,如果上面設(shè)置了FEATURE_NO_ACTIONBAR妆够,那么DecorView就只有mContentParent一個(gè)子View,這也解釋了上面的疑問:mContentParent是DecorView本身(NO_ACTIONBAR)或者是DecorView的一個(gè)子元素(有ACTIONBAR)。
???generateLayout()的返回是contentParent蛹找,而它的獲取則是ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
???DecorView是頂級(jí)View姜挺,它可以標(biāo)示整個(gè)屏幕,其中包含狀態(tài)欄词渤,導(dǎo)航欄,內(nèi)容區(qū)等等高氮。這里的mContentParent指的是屏幕顯示的內(nèi)容區(qū),而我們設(shè)置的activity_main.xml布局則是mContentParent里面的一個(gè)子元素。
???注意上面③處的代碼,這個(gè)R.layout.screen_simple就是當(dāng)我們的theme設(shè)置為NoTitleBar時(shí)的布局,我們來看看這個(gè)文件(SDK/platforms/android-23/data/res/layout/screen_simple.xml):
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<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:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foregroundInsidePadding="false"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
布局中,一般會(huì)包含ActionBar峡继,Title,和一個(gè)id為content的FrameLayout,這個(gè)布局是NoTitle的裤翩。此時(shí)我們設(shè)置一個(gè)activity,其主界面為activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:paddingBottom="50dp"
android:orientation="vertical">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hellow word" />
<View
android:id="@+id/selfView"
android:layout_width="match_parent"
android:layout_height="100dp" />
</LinearLayout>
我們用下面一張圖來表示activity_main.xml在整個(gè)screen_simple.xml中的關(guān)系:
從這張圖我們可以看到,我們平時(shí)在APP中所定義的xml文件,實(shí)際上是在一個(gè)id為content的FrameLayout中的,這個(gè)FrameLayout也就是前面一直提到的mContentParent!(我們看源碼的話就會(huì)發(fā)現(xiàn),不止screen_simple.xml,screen_toobar.xml,screen_title.xml等等布局文件中都含有這個(gè)id為content的FrameLayout)
???我們回到PhoneWindow類的setContentVeiw()方法中:
@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)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
此時(shí)通過installDecor()方法已經(jīng)創(chuàng)建完DecorView并且獲取到mContentParent筐带,接著就是將我們setContentView的內(nèi)容添加到mContentParent中蓝晒,也就是mLayoutInflater.inflate(layoutResID, mContentParent);
???這里我們注意到最后幾句代碼:
......
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
這里出現(xiàn)了一個(gè)回調(diào)接口:cb,通過getCallback()方法,這個(gè)方法是Window抽象類中的一個(gè)方法:
/**
* Set the Callback interface for this window, used to intercept key
* events and other dynamic operations in the window.
*
* @param callback The desired Callback interface.
*/
public void setCallback(Callback callback) {
mCallback = callback;
}
/**
* Return the current Callback interface for this window.
*/
public final Callback getCallback() {
return mCallback;
}
一個(gè)setCallback(),一個(gè)getCallback(),在源碼中就緊挨在一起,所以一并貼出來,而這個(gè)setCallback是在Activity類中被調(diào)用的,我們再Activity的attach()方法中發(fā)現(xiàn)了如下代碼:
final void attach(Context context, ActivityThread aThread,
......
mWindow.setCallback(this);
......
}
再看Activity類本身,它實(shí)現(xiàn)了一個(gè)特殊的接口:Window.Callback:
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback {
也就是說mWindow.setCallback(this);
這里的this就是Wondow中的Callback回調(diào)接口作儿。也就是說Activity類實(shí)現(xiàn)了Window的Callback接口類中的方法晾嘶。
現(xiàn)在我們清楚了PhoneWindow中setContentView最后幾句中cb的身世,我們來看看Activity中實(shí)現(xiàn)的onContentChanged()方法:
public void onContentChanged() {
}
如你所見,onContentChanged是個(gè)空方法。而當(dāng)Activity的布局改動(dòng)時(shí),即setContentView()或者addContentView()方法執(zhí)行完畢時(shí)就會(huì)調(diào)用該方法。所以當(dāng)我們寫App時(shí)乐导,Activity的各種View的findViewById()方法等都可以放到該方法中,系統(tǒng)會(huì)幫忙回調(diào)棵磷。實(shí)際上,如果我們看源碼的話就會(huì)發(fā)現(xiàn),不光是onContentChanged(),Activity中的onWindowFocusChanged(boolean hasFocus),onAttachedToWindow(),onDetachedFromWindow()......等一系列實(shí)現(xiàn)的Widow類中Callback中的方法,都是空方法,這些方法我在開發(fā)當(dāng)中都可以加以適當(dāng)?shù)睦?
到目前為止谢鹊,通過setContentView方法偎巢,創(chuàng)建了DecorView和加載了我們提供的布局压昼,但是這時(shí)遵倦,我們的View還是不可見的,因?yàn)槲覀儍H僅是加載了布局,并沒有對(duì)View進(jìn)行任何的測量续搀、布局、繪制工作。在View進(jìn)行測量流程之前洁桌,還要進(jìn)行一個(gè)步驟,那就是把DecorView添加至window中,然后經(jīng)過一系列過程觸發(fā)ViewRootImpl#performTraversals方法囊卜,在該方法內(nèi)部會(huì)正式開始測量雀瓢、布局司浪、繪制這三大流程吁伺。
三.將DecorView添加至Window并顯示界面
要解釋DecorView是如何添加到Window的,我們就要從Activity的啟動(dòng)源碼來講,還記得之前的這篇文章Activity啟動(dòng)源碼分析(二)——從AMS到Activity
嗎?我們做一個(gè)簡單的復(fù)習(xí):Activity啟動(dòng)的過程中必經(jīng)的一步—ActivityThread類中的的handleLaunchActivity()方法(frameworks/base/core/java/android/app/ActivityThread):
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
......
Activity a = performLaunchActivity(r, customIntent); //④
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward, //⑤
!r.activity.mFinished && !r.startsNotResumed);
......
}
} else {
......
}
}
我們來看④處的方法performLaunchActivity(r, customIntent):
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
......
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent); ⑥
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
......
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
......
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); ⑦
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
......
}
r.paused = true;
mActivities.put(r.token, r);
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to start activity " + component
+ ": " + e.toString(), e);
}
}
return activity;
}
上篇文章中我們說過Instrumentation類是一個(gè)全權(quán)負(fù)責(zé)Activity生命周期的類,在⑥處的代碼中,通過調(diào)用該類的newActivity()方法(frameworks/base/core/java/android/app/Instrumentation):
public Activity newActivity(Class<?> clazz, Context context,
IBinder token, Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
Object lastNonConfigurationInstance) throws InstantiationException,
IllegalAccessException {
Activity activity = (Activity)clazz.newInstance();
ActivityThread aThread = null;
activity.attach(context, aThread, this, token, 0, application, intent,
info, title, parent, id,
(Activity.NonConfigurationInstances)lastNonConfigurationInstance,
new Configuration(), null, null);
return activity;
}
創(chuàng)建了一個(gè)新的Activity實(shí)例,在改方法中調(diào)用了Activity類中的attach()方法,之后在⑦中的代碼中,調(diào)用了Instrumentation類的callActivityOnCreate方法:
/**
* Perform calling of an activity's {@link Activity#onCreate}
* method. The default implementation simply calls through to that method.
*
* @param activity The activity being created.
* @param icicle The previously frozen state (or null) to pass through to onCreate().
*/
public void callActivityOnCreate(Activity activity, Bundle icicle) {
prePerformCreate(activity);
activity.performCreate(icicle);
postPerformCreate(activity);
}
調(diào)用了Activity類中的performCreate()方法(frameworks/base/core/java/android/app/Activity):
final void performCreate(Bundle icicle) {
restoreHasCurrentPermissionRequest(icicle);
onCreate(icicle);
mActivityTransitionState.readState(icicle);
performCreateCommon();
}
調(diào)用了onCreate(icicle);方法:
/**
* Called when the activity is starting. This is where most initialization
* should go: calling {@link #setContentView(int)} to inflate the
* activity's UI, using {@link #findViewById} to programmatically interact
* with widgets in the UI, calling
* {@link #managedQuery(android.net.Uri , String[], String, String[], String)} to retrieve
* cursors for data being displayed, etc.
*
* <p>You can call {@link #finish} from within this function, in
* which case onDestroy() will be immediately called without any of the rest
* of the activity lifecycle ({@link #onStart}, {@link #onResume},
* {@link #onPause}, etc) executing.
*
* <p><em>Derived classes must call through to the super class's
* implementation of this method. If they do not, an exception will be
* thrown.</em></p>
*
* @param savedInstanceState If the activity is being re-initialized after
* previously being shut down then this Bundle contains the data it most
* recently supplied in {@link #onSaveInstanceState}. <b><i>Note: Otherwise it is null.</i></b>
*
* @see #onStart
* @see #onSaveInstanceState
* @see #onRestoreInstanceState
* @see #onPostCreate
*/
@MainThread
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState);
if (mLastNonConfigurationInstances != null) {
mFragments.restoreLoaderNonConfig(mLastNonConfigurationInstances.loaders);
}
if (mActivityInfo.parentActivityName != null) {
if (mActionBar == null) {
mEnableDefaultActionBarUp = true;
} else {
mActionBar.setDefaultDisplayHomeAsUpEnabled(true);
}
}
if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.fragments : null);
}
mFragments.dispatchCreate();
getApplication().dispatchActivityCreated(this, savedInstanceState);
if (mVoiceInteractor != null) {
mVoiceInteractor.attachActivity(this);
}
mCalled = true;
}
在這里,我們看注釋也知道,這個(gè)就是Activity第一個(gè)生命周期:onCreat(),在這里調(diào)用setContentView().⑥⑦流程走完了之后,整個(gè)performLaunchActivity()函數(shù)就會(huì)返回一個(gè)已經(jīng)執(zhí)行完onCreat()和setContetnView()的activity對(duì)象,然而正如我們之前所說,setContentView()執(zhí)行完之后,View此時(shí)還是不可見的,要等DecorView添加至window中咖城,然后觸發(fā)ViewRootImpl#performTraversals方法開始View的繪制,測量等工作.
???回到ActivityThread類中的看⑤處方法
handleResumeActivity();(frameworks/base/core/java/android/app/ActivityThread):
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
......
// TODO Push resumeArgs into the activity for consideration
ActivityClientRecord r = performResumeActivity(token, clearHide); ⑧
if (r != null) {
final Activity a = r.activity;
......
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 (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
// If the window has already been added, but during resume
// we started another activity, then don't yet make the
// window visible.
} else if (!willBeVisible) {
if (localLOGV) Slog.v(
TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
// Get rid of anything left hanging around.
cleanUpPendingRemoveWindows(r);
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
......
WindowManager.LayoutParams l = r.window.getAttributes();
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
!= forwardBit) {
l.softInputMode = (l.softInputMode
& (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
| forwardBit;
if (r.activity.mVisibleFromClient) {
ViewManager wm = a.getWindowManager();
View decor = r.window.getDecorView();
wm.updateViewLayout(decor, l);
}
}
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible(); (11)
}
}
......
} else {
// If an exception was thrown when trying to resume, then
// just end this activity.
try {
ActivityManagerNative.getDefault()
.finishActivity(token, Activity.RESULT_CANCELED, null, false);
} catch (RemoteException ex) {
}
}
}
注意到⑧處performResumeActivity(token, clearHide)方法,聯(lián)系performLaunchActivity(),我們可以猜到這個(gè)方法是與onResume()方法有關(guān)的:
public final ActivityClientRecord performResumeActivity(IBinder token,
boolean clearHide) {
ActivityClientRecord r = mActivities.get(token);
......
if (r != null && !r.activity.mFinished) {
......
try {
r.activity.onStateNotSaved();
r.activity.mFragments.noteStateNotSaved();
if (r.pendingIntents != null) {
deliverNewIntents(r, r.pendingIntents);
r.pendingIntents = null;
}
if (r.pendingResults != null) {
deliverResults(r, r.pendingResults);
r.pendingResults = null;
}
r.activity.performResume(); (12)
......
r.paused = false;
r.stopped = false;
r.state = null;
r.persistentState = null;
} catch (Exception e) {
......
}
}
return r;
}
可以看到,這個(gè)方法中(12)處調(diào)用了Activity類中的performResume()方法:
final void performResume() {
......
// mResumed is set by the instrumentation
mInstrumentation.callActivityOnResume(this);
......
onPostResume();
if (!mCalled) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
" did not call through to super.onPostResume()");
}
}
調(diào)用了Instrumentation類中的callActivityOnResume()方法()(frameworks/base/core/java/android/app/Instrumentation):
public void callActivityOnResume(Activity activity) {
activity.mResumed = true;
activity.onResume();
......
}
調(diào)用了Activity生命周期中的onResume()方法:
/**
* Called after {@link #onRestoreInstanceState}, {@link #onRestart}, or
* {@link #onPause}, for your activity to start interacting with the user.
* This is a good place to begin animations, open exclusive-access devices
* (such as the camera), etc.
*
* <p>Keep in mind that onResume is not the best indicator that your activity
* is visible to the user; a system window such as the keyguard may be in
* front. Use {@link #onWindowFocusChanged} to know for certain that your
* activity is visible to the user (for example, to resume a game).
*
* <p><em>Derived classes must call through to the super class's
* implementation of this method. If they do not, an exception will be
* thrown.</em></p>
*
* @see #onRestoreInstanceState
* @see #onRestart
* @see #onPostResume
* @see #onPause
*/
@CallSuper
protected void onResume() {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this);
getApplication().dispatchActivityResumed(this);
mActivityTransitionState.onResume();
mCalled = true;
}
回到handleResumeActivity()方法⑧處,由這一段的分析可知:⑧處的代碼(ActivityClientRecord r = performResumeActivity(token, clearHide);)執(zhí)行完后,Activity的onResume()方法也就執(zhí)行完了,此時(shí)返回一個(gè)改變了必要參數(shù)或者說必要變量賦值的ActivityClientRecord的對(duì)象r,這個(gè)ActivityClientRecord是ActivityThread類中的內(nèi)部實(shí)體類,分裝了ActivityThread執(zhí)行過程中必要的變量和對(duì)象,包括window,DecorView等.
???緊接著我們看⑨處的幾句代碼:
r.window = r.activity.getWindow();
View decor = r.window.getDecorView(); ⑨
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
r.window = r.activity.getWindow();
獲得當(dāng)前Activity的PhoneWindow對(duì)象,之前已經(jīng)分析過了,getWindow()方法返回的是mWindow,也就是PhoneWindow對(duì)象.
View decor = r.window.getDecorView();
獲得當(dāng)前phoneWindow內(nèi)部類DecorView對(duì)象.
decor.setVisibility(View.INVISIBLE);
剛獲得這個(gè)DecorView的時(shí)候先設(shè)置為不可見.
ViewManager wm = a.getWindowManager();
創(chuàng)建一個(gè)ViewManager對(duì)象,這句要重點(diǎn)說一下:
首先調(diào)用Activity的getWindowManager()方法:
/** Retrieve the window manager for showing custom windows. */
public WindowManager getWindowManager() {
return mWindowManager;
}
Activity類中mWindowManager唯一賦值的地方在attach()方法中:
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) {
......
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();
mCurrentConfig = config;
}
調(diào)用了Window的getWindowManager()方法:
/**
* Return the window manager allowing this Window to display its own
* windows.
*
* @return WindowManager The ViewManager.
*/
public WindowManager getWindowManager() {
return mWindowManager;
}
Window類中mWindowManager唯一賦值的地方在:
/**
* Set the window manager for use by this Window to, for example,
* display panels. This is <em>not</em> used for displaying the
* Window itself -- that must be done by the client.
*
* @param wm The window manager for adding new windows.
*/
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated
|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
調(diào)用了createLocalWindowManager()方法(frameworks/base/core/java/android/view/WindowManagerImpl):
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mDisplay, parentWindow);
}
返回了一個(gè)WindowManagerImpl類的實(shí)例.也就是說⑨處的代碼ViewManager wm = a.getWindowManager();
返回的wm是一個(gè)WindowManagerImpl類的實(shí)例.
而這個(gè)方法在mWindowManager = mWindow.getWindowManager();
這句之前就已經(jīng)調(diào)用過了,我們分析一下:這個(gè)方法中出現(xiàn)了兩個(gè)類:WindowManager和WindowManagerImpl,而⑨處的代碼中第四行ViewManager wm = a.getWindowManager();
中又有一個(gè)ViewManager類,我們來看看這三個(gè)類的關(guān)系(frameworks/base/core/java/android/view/ViewManager):
public interface ViewManager{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
public interface WindowManager extends ViewManager {
public final class WindowManagerImpl implements WindowManager {
Window是一個(gè)抽象的概念,每一個(gè)Window都對(duì)應(yīng)著一個(gè)View和一個(gè)ViewRootImpl亥至,Window又通過ViewRootImpl與View建立聯(lián)系絮供,因此Window并不是實(shí)際存在的,他是以View的形式體現(xiàn)的贮乳。這點(diǎn)從WindowManager的定義也可以看出,它繼承的的三個(gè)接口方法addView浓恳,updateView,removeView都是針對(duì)View的葡兑,這說明View才是Window的實(shí)體!?在實(shí)際使用中無法直接訪問Window奖蔓,對(duì)Window的操作(添加,更新讹堤,刪除)必須通過WindowManager,并且都是在ViewRootImpl中實(shí)現(xiàn)的吆鹤。
??WindowManager顧名思義就是管理Window的類,WindowManager操作Window的過程更像是在操作Window中的View洲守,常用的只有三個(gè)方法疑务,即添加View,更新View和刪除View,這三個(gè)方法定義在ViewManager中梗醇,而WindowManager繼承了ViewManager(當(dāng)然WindowManager還有其它功能知允,比如改變Window的位置等),但是WindowManager也是一個(gè)接口,真正實(shí)現(xiàn)它的類是WindowManagerImpl類,WindowManagerImpl又將所有操作委托給WindowManagerGlobal,當(dāng)然最終還是在ViewRootImpl中實(shí)現(xiàn)的.
???下面我們回到ActivityThread類中handleResumeActivity()方法中的⑩處:
WindowManager.LayoutParams l = r.window.getAttributes(); ⑩
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true; //標(biāo)記根布局DecorView已經(jīng)添加到窗口
wm.addView(decor, l); //將根布局DecorView(decor)以當(dāng)前Window的布局參數(shù)(l)
//添加到當(dāng)前Activity的窗口(WindowManagerImpl對(duì)象wm)上面
}
l是當(dāng)前Window(View)的布局參數(shù),然后又將當(dāng)前DecorVeiw賦給當(dāng)前Activity的mDecor,我們重點(diǎn)追蹤wm.addView(decor, l);這句代碼,先是WindowManagerImpl類中的addView()方法這里比較重要的一點(diǎn)就是完成了DecorView添加到Window的過程 ~~ wm.addView(decor, l);
(frameworks/base/core/java/android/view/WindowManagerImpl):
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
調(diào)用了WindowManagerGlobal類中的addView()方法(frameworks/base/core/java/android/view/WindowManagerGlobal):
//表示View樹的根節(jié)點(diǎn)
private final ArrayList<View> mViews = new ArrayList<View>();
//表示ViewRootImpl,一個(gè)根view對(duì)應(yīng)一個(gè)ViewRootImpl
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
//表示根view的WindowManager.LayoutParams
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
......
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {
......
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
......
// If this is a panel window, then find the window it is being
// attached to for future reference.
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews.size();
for (int i = 0; i < count; i++) {
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}
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.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
可以看到這里出現(xiàn)了ViewRootImpl類的實(shí)例root,而經(jīng)過層層傳遞,這里傳進(jìn)來的參數(shù)View就是decor,也就是DecorVeiw,注意這里調(diào)用了root.setView(view, wparams, panelParentView);
(frameworks/base/core/java/android/view/ViewRootImpl):
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view; //傳進(jìn)來的view(也就是DecorView)賦給了mView.
......
mAdded = true; //表示已成功添加DecorView的標(biāo)志位
int res; /* = WindowManagerImpl.ADD_OKAY; */
// 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(); //完成異步刷新請求,重繪界面
......
try {
......
//通過WindowSession最終完成window的添加過程, WindowSession內(nèi)部通過WindowManagerService來實(shí)現(xiàn)Window的添加
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {
mAdded = false;
......
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}
}
}
這個(gè)方法有點(diǎn)略長(200多行),我們只看關(guān)鍵方法:
requestLayout()該方法最終開始界面的測量繪制等操作.
mWindowSession.addToDisplay()這個(gè)方法最終完成的是Window的添加過程(具體添加的過程這里就不展開了);
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
調(diào)用到了** mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);**,這個(gè)方法在Choreographer類中的具體實(shí)現(xiàn)牽扯到了native層的代碼,本文不做具體分析,我們只看這里注冊了一個(gè)回調(diào)接口,調(diào)用了mTraversalRunnable這個(gè)Runnable對(duì)象,于是我們順藤摸瓜(還是在ViewRootImpl類中):
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
調(diào)用了doTraversal()方法:
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
調(diào)用到了performTraversals()方法:
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView; //mView就是DecorView根布局
if (host == null || !mAdded) //變量mAdded已被賦值為true叙谨,因此條件不成立
return;
mIsInTraversal = true; //是否正在遍歷
mWillDrawSoon = true; //是否馬上繪制View
......
int desiredWindowWidth; //頂層視圖DecorView所需要窗口的寬度和高度
int desiredWindowHeight;
......
if (mFirst) { //在構(gòu)造方法中mFirst已經(jīng)設(shè)置為true温鸽,表示是否是第一次繪制DecorView
mFullRedrawNeeded = true;
mLayoutRequested = true;
//如果窗口的類型是有狀態(tài)欄的,那么頂層視圖DecorView所需要窗口的寬度和高度就是除了狀態(tài)欄
if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL
|| lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
// NOTE -- system code, won't try to do compat mode.
Point size = new Point();
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
} else {//否則頂層視圖DecorView所需要窗口的寬度和高度就是整個(gè)屏幕的寬高
DisplayMetrics packageMetrics =
mView.getContext().getResources().getDisplayMetrics();
desiredWindowWidth = packageMetrics.widthPixels;
desiredWindowHeight = packageMetrics.heightPixels;
}
}
......
//獲得view寬高的測量規(guī)格手负,mWidth和mHeight表示窗口的寬高涤垫,lp.widthhe和lp.height表示DecorView根布局寬和高
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
// Ask host how big it wants to be
//執(zhí)行測量操作
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
......
//執(zhí)行布局操作
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
......
//執(zhí)行繪制操作
performDraw();
}
可以看到,在這個(gè)方法中開始了View的測量、布局竟终、繪制流程三大流程,這個(gè)在之后會(huì)另起文章分析.
???回到ActivityThread類中的handleResumeActivity()方法
(frameworks/base/core/java/android/app/ActivityThread):
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
......
// TODO Push resumeArgs into the activity for consideration
ActivityClientRecord r = performResumeActivity(token, clearHide); ⑧
if (r != null) {
final Activity a = r.activity;
......
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 (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
// If the window has already been added, but during resume
// we started another activity, then don't yet make the
// window visible.
} else if (!willBeVisible) {
if (localLOGV) Slog.v(
TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
// Get rid of anything left hanging around.
cleanUpPendingRemoveWindows(r);
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
......
WindowManager.LayoutParams l = r.window.getAttributes();
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
!= forwardBit) {
l.softInputMode = (l.softInputMode
& (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
| forwardBit;
if (r.activity.mVisibleFromClient) {
ViewManager wm = a.getWindowManager();
View decor = r.window.getDecorView();
wm.updateViewLayout(decor, l);
}
}
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible(); (11)
}
}
......
} else {
// If an exception was thrown when trying to resume, then
// just end this activity.
try {
ActivityManagerNative.getDefault()
.finishActivity(token, Activity.RESULT_CANCELED, null, false);
} catch (RemoteException ex) {
}
}
}
經(jīng)過上面的分析,⑧⑨⑩處代碼執(zhí)行完之后,DecorView才添加到了Window中,但是由于decor.setVisibility(View.INVISIBLE);
導(dǎo)致它目前還是不可見的,而真正顯現(xiàn)是在(11)處的代碼(調(diào)用了Activity類中的makeVisible()方法):
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
可以看到,mDecor.setVisibility(View.VISIBLE);,此時(shí)DecorView才是真正的可以看見了,界面也就顯示出來了!從Activity的生命周期的角度來看,也就是onResume()執(zhí)行完之后,DecorView才開始attach給WindowManager從而顯示出來.
Activity的內(nèi)部實(shí)際上持有了一個(gè)Window的子類PhoneWindow蝠猬。Activity中關(guān)于界面的繪制實(shí)際上是交給PhoneWindow中的setContentView方法來實(shí)現(xiàn)。
我們總結(jié)一下:
??1.Activity在onCreate之前調(diào)用attach方法统捶,在attach方法中會(huì)創(chuàng)建window對(duì)象榆芦。window對(duì)象創(chuàng)建時(shí)并沒有創(chuàng)建Decor對(duì)象柄粹。用戶在Activity中調(diào)用setContentView,然后調(diào)用window的setContentView,這時(shí)會(huì)檢查DecorView是否存在匆绣,如果不存在則創(chuàng)建DecorView對(duì)象驻右,然后把用戶自己的View 添加到DecorView中。
??2.①Window是一個(gè)抽象類犬绒,提供了各種窗口操作的方法旺入,比如設(shè)置背景標(biāo)題ContentView等等
??②PhoneWindow則是Window的唯一實(shí)現(xiàn)類凯力,它里面實(shí)現(xiàn)了各種添加背景主題ContentView的方法茵瘾,內(nèi)部通過DecorView來添加頂級(jí)視圖
??③每一個(gè)Activity上面都有一個(gè)Window雕旨,可以通過getWindow獲取DecorView,頂級(jí)視圖活箕,繼承于FramentLayout育韩,setContentView則是添加在它里面的@id/content里
??④setContentView里面創(chuàng)建了DecorView,根據(jù)Theme/Feature添加了對(duì)應(yīng)的布局文件,當(dāng)setContentView設(shè)置顯示后會(huì)回調(diào)Activity的onContentChanged方法
??至此,除去View的繪制流程,setContentView()的源碼就分析完了,撒花!!!
站在巨人的肩膀上摘蘋果:
Android View源碼解讀:淺談DecorView與ViewRootImpl
Android應(yīng)用setContentView與LayoutInflater加載解析機(jī)制源碼分析
Android窗口機(jī)制(二)Window闺鲸,PhoneWindow筋讨,DecorView,setContentView源碼理解