View的創(chuàng)建繪制的過(guò)程

1. 基本概念

Activity: 應(yīng)用與用戶交互的入口(活動(dòng)界面).

Window: 表示一個(gè)窗口,不一定有屏幕那么大,可以很大也可以很小;

PhoneWindw: 是window的一個(gè)實(shí)現(xiàn)類,給view包裹一層DecorView.

DecorView: DecorView是整個(gè)ViewTree的最頂層View,它是一個(gè)FrameLayout布局,代表了整個(gè)應(yīng)用的界面.

2.時(shí)序圖

image
image

3.view添加到activity的流程

  • Activity對(duì)象創(chuàng)建,并調(diào)用attach方法,創(chuàng)建PhoneWindw
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) {
        attachBaseContext(context);
        mFragments.attachHost(null /*parent*/);
        mWindow = new PhoneWindow(this, window);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
       ........................
    }

Activity在attch后,Instrumentation對(duì)象回調(diào)activity的onCreate方法

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

其實(shí)是PhoneWidow中的setContentView方法被調(diào)用

    getWinodw()的是PhoneWindow對(duì)象
    
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }
  
  • PhoneWindow類中有兩個(gè)和視圖相關(guān)的成員變量,一個(gè)是DecorView mDecor,另一個(gè)是ViewGroup mContentParent。
  @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) { // 1
        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 { //無(wú)轉(zhuǎn)場(chǎng)動(dòng)畫
    //;可看到原來(lái)mContentParent是id="@android:id/content"的ViewGroup。再回到
        mLayoutInflater.inflate(layoutResID, mContentParent); // 2
    }
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }

installDecor()方法初始化DecorView對(duì)象

private void installDecor() {
    if (mDecor == null) {
        mDecor = generateDecor();
        ...
    }
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);
        ...
    }
}

protected DecorView generateDecor() {
    return new DecorView(getContext(), -1);
    
}
  • DecorView是PhoneWindow類中定義的一個(gè)內(nèi)部類,它繼承了FrameLayout,用來(lái)作為整個(gè)PhoneWindow的根視圖。
protected ViewGroup generateLayout(DecorView decor) {

  
     // Apply data from current theme.
    // 從主題文件中獲取樣式信息
    TypedArray a = getWindowStyle();

    ...

    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);
    }

    if(...){
            ...
    }

    //以下開(kāi)始填充decor

    // Inflate the window decor.
    int layoutResource;    //這個(gè)是用來(lái)inflate的id

    ...    //這里省去,內(nèi)容是根據(jù)Window的各種特性(feature)選擇一個(gè)合適的視圖id賦給layoutResource

    mDecor.startChanging();

    View in = mLayoutInflater.inflate(layoutResource, null); //將布局轉(zhuǎn)換成view
    decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));//往DecorView中添加子View,即mContentParent

    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); //找到contentView
    //注意這個(gè)地方
    if (contentParent == null) {
        throw new RuntimeException("Window couldn't find content container view");
    }

    ... //省去,內(nèi)容是設(shè)置背景,設(shè)置ActionBar的一些屬性。

    mDecor.finishChanging();

    return contentParent;
}

cotentView布局id


<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>

綜合以上的探究,加上自己的一些思考和猜測(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è)頁(yè)面所相關(guān)聯(lián)。

3.PhoneWindow同時(shí)管理著ActionBar和下面的內(nèi)容主題,setContentView()方法是用來(lái)設(shè)置內(nèi)容主體的,而setTitle()等其他方法就是操作ActionBar的,Window中定義的requestFeature()等方法,有很多與ActionBar屬性相關(guān)的設(shè)置。另外這些方法都是公有方法,顯然是為了給客戶端程序員調(diào)用的,也進(jìn)一步佐證了這些操作的意義與作用。

4.PhoneWindow自己并不是一個(gè)視圖(View),它的成員變量mDecor才是整個(gè)界面的視圖,mDecor是在generateLayout()的時(shí)候被填充出來(lái)的,而actionBar和contentParent兩個(gè)視圖都是通過(guò)findViewById()直接從mDecor中獲取出來(lái)的。

接下來(lái)是mContentParent.removeAllViews()。這個(gè)好理解,如果setContentView()被調(diào)用兩次,第二次肯定要把里面的內(nèi)容都給清空移除了。移除后就是添加,mContentParent.addView(view, params)。

  • 將DecorView添加至Window

首先,在ActivityThread#handleLaunchActivity中啟動(dòng)Activity,在這里面會(huì)調(diào)用到Activity#onCreate方法,從而完成上面所述的DecorView創(chuàng)建動(dòng)作,當(dāng)onCreate()方法執(zhí)行完畢.在handleLaunchActivity方法會(huì)繼續(xù)調(diào)用到ActivityThread#handleResumeActivity方法,我們看看這個(gè)方法的源碼:

final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) { 
    //...
    ActivityClientRecord r = performResumeActivity(token, clearHide); // 這里會(huì)調(diào)用到onResume()方法
    if (r != null) {
        final Activity a = r.activity;

        //...
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow(); // 獲得window對(duì)象
            View decor = r.window.getDecorView(); // 獲得DecorView對(duì)象
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager(); // 獲得windowManager對(duì)象
            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); // WindowManagerImpl對(duì)象調(diào)用addView方法
            }
            //...
        }
    }
}

在該方法內(nèi)部,獲取該activity所關(guān)聯(lián)的window對(duì)象,DecorView對(duì)象,以及windowManager對(duì)象,而WindowManager是抽象類,它的實(shí)現(xiàn)類是WindowManagerImpl,所以后面調(diào)用的是WindowManagerImpl#addView方法,我們看看源碼:

public final class WindowManagerImpl implements WindowManager {    
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    ...
    @Override
    public void addView(View view, ViewGroup.LayoutParams params) {
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }
}

接著調(diào)用了mGlobal的成員函數(shù),而mGlobal則是WindowManagerGlobal的一個(gè)實(shí)例,那么我們接著看WindowManagerGlobal#addView方法:

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        ...

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            ...

            root = new ViewRootImpl(view.getContext(), display); // 1

            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); // 2
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
    }

先看①號(hào)代碼處,實(shí)例化了ViewRootImpl類,接著,在②號(hào)代碼處,調(diào)用ViewRootImpl#setView方法,并把DecorView作為參數(shù)傳遞進(jìn)去,在這個(gè)方法內(nèi)部,會(huì)通過(guò)跨進(jìn)程的方式向WMS(WindowManagerService)發(fā)起一個(gè)調(diào)用,從而將DecorView最終添加到Window上,在這個(gè)過(guò)程中,ViewRootImpl、DecorView和WMS會(huì)彼此關(guān)聯(lián),至于詳細(xì)過(guò)程這里不展開(kāi)來(lái)說(shuō)了。
最后通過(guò)WMS調(diào)用ViewRootImpl#performTraversals方法開(kāi)始View的測(cè)量、布局、繪制流程.

4. WindowManager和應(yīng)用進(jìn)程View繪制通信

image

5.參考資料

-http://blog.csdn.net/houliang120/article/details/51138087

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容