View的繪製流程一(視圖的加載)

視圖層級關係

image

PhoneWindow的初始化

PhoneWindow 伴隨着 Activity 的創建而創建, 而 ActivityThread 掌握着 Activity 的創建.

在ActivityThread的performLaunchActivity 方法中,完成了PhoneWindow的初始化和activity的創建,初始化完 Activity 後, 就調用 Activity 的 onCreate 方法了.

ActivityThread.performLaunchActivity

細節如下:

  1. 在performLaunchActivity中創建了Activity對象以及Context, theme, PackageInfo 等,且初始化了一個Windows
  2. 之後調用了方法activity.attach實例化了 PhoneWindow
  3. 調用 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;
}

總結

  1. 在 ActivityThread 的 performLaunchActivity 中, 創建了 PhoneWindow 並調用了 Activity 的 attach 方法

  2. 在 Activity 的 attach 方法中, 關聯了 PhoneWindow

  3. 在 Activity 的 setContentView 方法中 獲取了 PhoneWindow , 並將 layoutResID 傳入 PhoneWindow 的 setContentView 中

  4. 在 PhoneWindow 的 setContentView 中, 初始化了 DecorView,然後初始化mContentParent,把加載出來的這個佈局文件對應的View賦值給mDecor

  5. 在完成上述邏輯後,會回到installDecor中,再調用LayoutInflater的inflate方法解析我們自己的XML佈局文件,也就是我們在自己的Activity中onCreate方法中調用setContentView方法中傳入的Layout ID,把解析出來的View添加mContentParent中,這樣就把我們自己的Layout添加到mDecor中。

image

  1. 在onCreate方法中也即Activity生命週期的第一個方法我們通常會調用setContentView(layoutID),這個過程我們在前面已經說過了,緊接着進行生命週期的第二個方法onResume(通過AMS及Handler機制),最終會通過Handler消息分發機制最終調用到ActivityThread中mH中handleMessage()方法調用what=RESUME_ACTIVITY的邏輯判斷體內,即調用handleResumeActivity方法。
  2. 在handleResumeActivity方法中會調用WindowManagerImpl的addView(mDecor,LayoutParam)把前面創建的DecorView(即從系統加載的那個XML佈局文件,同樣也包含了我們自己的佈局文件)。
  3. 在WindowManagerImpl內部其實是通過WindowManagerGlobal去完成addView的操作的。
  4. 在WindowManagerGlobal的addView方法中創建了實例對象ViewRootImpl的,調用了其setView方法,在後面就調用到了performTraversals
  5. 在後面就依次調用了performMeasure,performLayout,performDraw
    注:詳細看此處

參考1

參考2

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章