文章目錄
視圖層級關係
PhoneWindow的初始化
PhoneWindow 伴隨着 Activity 的創建而創建, 而 ActivityThread 掌握着 Activity 的創建.
在ActivityThread的performLaunchActivity 方法中,完成了PhoneWindow的初始化和activity的創建,初始化完 Activity 後, 就調用 Activity 的 onCreate 方法了.
ActivityThread.performLaunchActivity
細節如下:
- 在performLaunchActivity中創建了Activity對象以及Context, theme, PackageInfo 等,且初始化了一個Windows
- 之後調用了方法activity.attach實例化了 PhoneWindow
- 調用 Activity 的 onCreate 方法
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
// 創建 Activity的appContext
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
// 創建 Activity
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
...
} catch (Exception e) {
...
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
...
if (activity != null) {
...
Window window = null;
if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
// window 創建了
window = r.mPendingRemoveWindow;
r.mPendingRemoveWindow = null;
r.mPendingRemoveWindowManager = null;
}
appContext.setOuterContext(activity);
// 初始化 Activity 相關的內容, Activity 的attach這個方法中關聯了 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);
...
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
...
}
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
...
}
return activity;
}
Activity的setContentView
在上述執行完成之後,Activity的onCreat()被執行,進而執行Activity的setContentView,其會調用PhoneWindow.setContentView方法
PhoneWindow.setContentView
public void setContentView(int layoutResID) {
if (mContentParent == null) {
//installDecor 這個方法中, 初始化了 DecorView, mContentParent, 初始化了比如標題,icon, logo, 是否全屏等該 window 的一些基礎屬性
// 初始化 DecorView
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
//如果沒有過渡動畫, 並且已經創建過 DecorView
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
// 場景轉換(過渡動畫)
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext());
transitionTo(newScene);
} else {
// 開始填充我們設置的 XML 佈局, 容器爲 mContentParent
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
閱讀上面代碼可知, mContentParent 就是裝載我們所有內容的根容器(ViewGroup)了. 在這裏, 最關鍵的代碼就是 mLayoutInflater.inflate(layoutResID, mContentParent) , 它就是填充我們的佈局的代碼了.
PhoneWindow.installDecor
- 初始化mContentParent和頂級ViewGroup :mDecorView
private void installDecor() {
...
mForceDecorInstall = false;
if (mDecor == null) {
// generateDecor方法生成了 DecorView
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
// generateLayout方法生成裝載我們的佈局的容器
mContentParent = generateLayout(mDecor);
...
}
PhoneWindow.generateDecor
- 初始化mDecorView
protected DecorView generateDecor(int featureId) {
Context context;
if (mUseDecorContext) {
Context applicationContext = getContext().getApplicationContext();
if (applicationContext == null) {
context = getContext();
} else {
context = new DecorContext(applicationContext, getContext().getResources());
if (mTheme != -1) {
context.setTheme(mTheme);
}
}
} else {
context = getContext();
}
// 實例化了一個 DecorView
return new DecorView(context, featureId, this, getAttributes());
}
PhoneWindow.generateLayout
- 初始化mContentParent以及window的title, titleColor,ActionBar等
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
// 獲取當前 window 的 主題
TypedArray a = getWindowStyle();
... // 初始化樣式, 例如 windowNoTitle, windowActionBar, windowIsTranslucent, windowSoftInputMode 等等
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
...
//設置 windowBackground, title, titleColor 等
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
mDecor.finishChanging();
return contentParent;
}
總結
-
在 ActivityThread 的 performLaunchActivity 中, 創建了 PhoneWindow 並調用了 Activity 的 attach 方法
-
在 Activity 的 attach 方法中, 關聯了 PhoneWindow
-
在 Activity 的 setContentView 方法中 獲取了 PhoneWindow , 並將 layoutResID 傳入 PhoneWindow 的 setContentView 中
-
在 PhoneWindow 的 setContentView 中, 初始化了 DecorView,然後初始化mContentParent,把加載出來的這個佈局文件對應的View賦值給mDecor
-
在完成上述邏輯後,會回到installDecor中,再調用LayoutInflater的inflate方法解析我們自己的XML佈局文件,也就是我們在自己的Activity中onCreate方法中調用setContentView方法中傳入的Layout ID,把解析出來的View添加mContentParent中,這樣就把我們自己的Layout添加到mDecor中。
- 在onCreate方法中也即Activity生命週期的第一個方法我們通常會調用setContentView(layoutID),這個過程我們在前面已經說過了,緊接着進行生命週期的第二個方法onResume(通過AMS及Handler機制),最終會通過Handler消息分發機制最終調用到ActivityThread中mH中handleMessage()方法調用what=RESUME_ACTIVITY的邏輯判斷體內,即調用handleResumeActivity方法。
- 在handleResumeActivity方法中會調用WindowManagerImpl的addView(mDecor,LayoutParam)把前面創建的DecorView(即從系統加載的那個XML佈局文件,同樣也包含了我們自己的佈局文件)。
- 在WindowManagerImpl內部其實是通過WindowManagerGlobal去完成addView的操作的。
- 在WindowManagerGlobal的addView方法中創建了實例對象ViewRootImpl的,調用了其setView方法,在後面就調用到了performTraversals
- 在後面就依次調用了performMeasure,performLayout,performDraw
注:詳細看此處