1. 程式人生 > >Activity的建立,配置上下文,繪製檢視過程

Activity的建立,配置上下文,繪製檢視過程

概述

Activity啟動過程分析一文中分析到了通過反射建立了activity並且呼叫了activity的onCreate表示activity啟動了,但是我們見到的activity一般都是一個介面,所以這篇文章就繼續分析了Activity的檢視建立過程.這個過程中涉及到的主要類如下:
Activity->使用者操作的類
Context-> 負責管理actvity的資源等資訊
Window-> 真正表示介面的類,也就是視窗,管理要顯示內容,用來描述當前正在啟動的應用程式視窗的。這個應用程式視窗在執行的過程中,會接收到一些事件,例如,鍵盤、觸控式螢幕事件等,這些事件需要轉發給與它所關聯的Activity元件處理,這個轉發操作是通過一個Window.Callback介面來實現的
ViewRootImpl -> 與WMS通訊使用mSseionWindow,並且將訊息傳送到主執行緒,保證ui的順序建立,且檢測UI的更新必須在主執行緒
WindowManagerService ->負責所有介面的管理,每個activity隊友一個WindowState物件在這裡,
SurfaceFlinger -> 負責真正的繪製介面

每一個Activity元件都有對應一個window,一個對應的ViewRootImpl物件、View物件以及WindowManager.LayoutParams物件,這三個物件的對應關係是由WindowManagerImpl類來維護的。具體來說,就是由WindowManagerImpl類的成員變數mRoots、mViews和mParams所描述的三個陣列來維護的.

每一個ViewRootImpl物件都有一個型別為Surface的成員變數mSurface,它指向了一個Java層的Surface物件。這個Java層的Surface物件通過它的成員變數mNativeSurface與一個C++層的Surface物件。C++層中,每一個Surface物件都有一個對應的SurfaceControl物件。這個對應的SurfaceControl物件是用來設定應用程式視窗的屬性,例如,設定大小、位置等屬性。

每一個Activity元件在WindowManagerService服務這一側都有一個對應的WindowState物件,用來描述Activity元件的視窗狀態。WindowState類有兩個重要的成員變數mSession和mSurface,它們的型別分別為SurfaceSession和Surface。SurfaceSession類有一個型別為int的成員變數mClient,它儲存的是一個C++層的SurfaceComposerClient物件的地址,即每一個Java層的SurfaceSession物件在C++層都有一個對應的SurfaceComposerClient物件。當一個SurfaceSession物件建立的時候,與它所關聯的SurfaceComposerClient物件也會同時被建立。每一個Android應用程式程序在WindowManagerService服務這一側對應有一個Session物件、一個SurfaceSession物件以及一個SurfaceComposerClient物件。由於每一個Android應用程式程序都可以執行若干個Activity元件,因此,我們又可以說,Activity元件與WindowServiceManager服務這一側的Session物件、SurfaceSession物件以及SurfaceComposerClient物件是多對一的關係。因此,關係如下:
在Wms中通過WindowState來操作每一個activity,在activity通過mSessionWindow來操作Wms.

Activity的建立,啟動過程

1.建立

   public Activity newActivity(ClassLoader cl, String className,
            Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        return (Activity)cl.loadClass(className).newInstance();
    }

通過類載入器將對應的類載入進記憶體,然後通過反射建立一個Activity物件

2.Activty的初始化: 配置上下文環境,資源,建立視窗等

  1.  
  2.     final void attach(Context context, ActivityThread aThread,  
  3.             Instrumentation instr, IBinder token, int ident,  
  4.             Application application, Intent intent, ActivityInfo info,  
  5.             CharSequence title, Activity parent, String id,  
  6.             Object lastNonConfigurationInstance,  
  7.             HashMap<String,Object> lastNonConfigurationChildInstances,  
  8.             Configuration config) {  
  9.          //將conextt設定ContextThemeWrapper包裝類
  10.         attachBaseContext(context);  
  11.         //建立window
  12.         mWindow = PolicyManager.makeNewWindow(this);  
  13.       設定activity為window的回撥,這樣就可以將在activity處理邏輯事件
  14.         mWindow.setCallback(this);  
  15. 
  16. 
  17. 引數info指向的是一個ActivityInfo物件,用來描述當前正在啟動的Activity元件的資訊。其中,這個ActivityInfo物件的成員變數softInputMode用來描述當前正在啟動的一個Activity元件是否接受軟鍵盤輸入。如果接受的話,那麼它的值就不等於WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED,並且描述的是當前正在啟動的Activity元件所接受的軟鍵盤輸入模式。這個軟鍵盤輸入模式設定到前面所建立的一個PhoneWindow物件內部去,這是通過呼叫Window類的成員函式setSoftInputMode來實現的。
  18.         if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {  
  19.             mWindow.setSoftInputMode(info.softInputMode);  
  20.         }  
  21.         ......  
  22.   
  23.         mApplication = application;  
  24.         ......  
  25.          activity中的   視窗管理者和window中的是同一個mToken,它是一個Binder代理物件,引用了在ActivityManagerService這一側所建立的一個型別為ActivityRecord的Binder本地物件,用來描述該Activity元件的執行狀態。這個Binder代理物件會被儲存在Window類的成員變數mAppToken中,這樣當前正在處理的視窗就可以知道與它所關聯的Activity元件是什麼。
  26.         mWindow.setWindowManager(null, mToken, mComponent.flattenToString());  
  27.         mWindowManager = mWindow.getWindowManager();  
  28.             配置資訊
  29.         mCurrentConfig = config;  
  30.     }  

初始化一個Activity元件例項需要一個Application物件app、一個ContextImpl物件appContext以及一個Configuration物件config,它們分別用來描述該Activity元件例項的應用程式資訊、執行上下文環境以及配置資訊。一個activity的資源都在ContextImpl中管理,所以在初始activity只要設好對應的Context,在啟動activity的時候 ,用到資源才不會報錯,外掛化的時候要替換資源其實就是替換context

3.Window的建立

  1. public class PhoneWindow extends Window implements MenuBuilder.Callback {  
  2.     ......  
  3.   
  4.     // This is the top-level view of the window, containing the window decor.  
  5.     private DecorView mDecor;  
  6.   
  7.     // This is the view in which the window contents are placed. It is either  
  8.     // mDecor itself, or a child of mDecor where the contents go.  
  9.     private ViewGroup mContentParent;  
  10.     ......  
  11.   
  12.     private LayoutInflater mLayoutInflater;  
  13.     ......  
  14.   
  15.     public PhoneWindow(Context context) {  
  16.         super(context);  
  17.         mLayoutInflater = LayoutInflater.from(context);  
  18.     }  
  19.   
  20.     ......  
  21. } 

一個Activity元件所關聯的應用程式視窗物件的型別為PhoneWindow。
這個型別為PhoneWindow的應用程式視窗是通過一個型別為LocalWindowManager的本地視窗管理器來維護的。
這個型別為LocalWindowManager的本地視窗管理器又是通過一個型別為WindowManagerImpl的視窗管理器來維護應用程式視窗的。
這個型別為PhoneWindow的應用程式視窗內部有一個型別為DecorView的檢視物件,這個檢視物件才是真正用來描述一個Activity元件的UI的。

4.Activty的啟動

呼叫mInstrumentation就會呼叫Activity的OnCreate方法
mInstrumentation.callActivityOnCreate(activity, r.state);

Activity預設的onCreate方法
  1.  protected void onCreate(Bundle savedInstanceState) {  
  2.         mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(  
  3.                 com.android.internal.R.styleable.Window_windowNoDisplay, false);  
  4.         mCalled = true;  
  5.     }

一般來說,我們都是通過定義一個Activity子類來實現一個Activity元件的。重寫父類Activity的某些成員函式的時候,必須要回調父類Activity的這些成員函式。例如,當Activity子類在重寫父類Activity的成員函式onCreate時,就必須回撥父類Activity的成員函式onCreate。這些成員函式被回調了之後,Activity類就會將其成員變數mCalled的值設定為true。這樣,Activity類就可以通過其成員變數mCalled來檢查其子類在重寫它的某些成員函式時,是否正確地回調了父類的這些成員函式。
Activity類的另外一個成員變數mVisibleFromClient用來描述一個應用程式視窗是否是可見的。如果是可見的,那麼它的值就會等於true。當Activity類的成員函式onCreate被其子類回撥時,它就會檢查對應的應用程式視窗的主題屬性android:windowNoDisplay的值是否等於true。如果等於true的話,那麼就說明當前所啟動的應用程式視窗是不可見的,這時候Activity類的成員變數mVisibleFromClient的值就會被設定為false,否則的話,就會被設定為true。

Activity的視窗檢視的建立

1.從oncreate中呼叫setContentView開始,就是建立視窗檢視的開始

  1. public void setContentView(int layoutResID) {  
  2.         getWindow().setContentView(layoutResID);  
  3.     }  

2.PhoneWindow.setContentView

  1. public class PhoneWindow extends Window implements MenuBuilder.Callback {  
  2.     
  3.    window的頂級檢視,但是它也是Framelayout的子類,這個是出去狀態列和虛擬按鍵的部分,呼叫這個的getTop就可以知道狀態列的高度了
  4.    private DecorView mDecor;  
  5. 
  6.     id等於content的佈局,其實就是我們自定義好的佈局的父佈局,mContentParent是mDecor的間接子類,直接子類是系統定義的一些包含id值為“content”佈局,根據使用者的feature,載入不同的佈局,這個是除去標題欄的部分,這個呼叫getTop就可以知道標題欄的高度了
  7.     private ViewGroup mContentParent;  
  8.     ......  
  9.   
  10.     @Override  
  11.     public void setContentView(int layoutResID) {  
  12.         mContentParent 等於null的時候,就說明正在處理的應用程式視窗的檢視物件還沒有建立
  13.         if (mContentParent == null) {  
  14.          建立視窗檢視,就是建立mDecor 和mContentParent物件,這裡會根據feature,讓mDecor來addview對應的佈局
  15.             installDecor();  
  16.         } else {  
  17.        否則就是表示要重新設定檢視,先清空
  18.             mContentParent.removeAllViews();  
  19.         }  
  20.        將他們的佈局新增到mContentParent中
  21.         mLayoutInflater.inflate(layoutResID, mContentParent);  
  22.         final Callback cb = getCallback();  
  23.         if (cb != null) {  
  24.             呼叫Callback 的回撥onContentChanged,其實就呼叫activity的onContentChanged
  25.             cb.onContentChanged();  
  26.         }  
  27.     }  
  28.   
  29.     
  30. } 

3.PhoneWindow.installDecor

  1. private void installDecor() {  
  2.         if (mDecor == null) {  
  3.           建立mDecor
  4.             mDecor = generateDecor();  
  5.             ......  
  6.         }  
  7.         根據mDecor建立自定義佈局的父佈局
  8.         if (mContentParent == null) {  
  9.             mContentParent = generateLayout(mDecor);  
  10.       
  11.        根據我們設定的feature來設定標題
  12.             mTitleView = (TextView)findViewById(com.android.internal.R.id.title);  
  13.             if (mTitleView != null) {  
  14.                 if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {  
  15.                     View titleContainer = findViewById(com.android.internal.R.id.title_container);  
  16.                     if (titleContainer != null) {  
  17.                         titleContainer.setVisibility(View.GONE);  
  18.                     } else {  
  19.                         mTitleView.setVisibility(View.GONE);  
  20.                     }  
  21.                     if (mContentParent instanceof FrameLayout) {  
  22.                         ((FrameLayout)mContentParent).setForeground(null);  
  23.                     }  
  24.                 } else {  
  25.                     mTitleView.setText(mTitle);  
  26.                 }  
  27.             }  
  28.         }  
  29.     }  
  30.   
  31.     ......  
  32. } 

4.PhoneWindow.generateDecor

 protected DecorView generateDecor() {
        return new DecorView(getContext(), -1);
    }

generateDcor其實就是new一個DecorView物件而已,而DecorView其實就是Framelayout的子類,DecorView的定義如下:
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {}
mDecor建立好了在看下是如何來建立mContentParent的

5.PhoneWindow.generateLayout

protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.
      通過主題設定的屬性,然後呼叫requestFeature來給視窗設定對應的屬性,
在requestFeature中會先判斷mContentParent是否為空,如果不為空,就會丟擲異常,"requestFeature() must be called before adding content,這就是我們在程式碼中設定Feature時在要setContentView之前的原因
        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 (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) {
            requestFeature(FEATURE_ACTION_BAR_OVERLAY);
        }
        if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) {
            requestFeature(FEATURE_ACTION_MODE_OVERLAY);
        }

        if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) {
            requestFeature(FEATURE_SWIPE_TO_DISMISS);
        }

        if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
            setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
        }

        if (a.getBoolean(R.styleable.Window_windowTranslucentStatus,
                false)) {
            setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS
                    & (~getForcedWindowFlags()));
        }

        if (a.getBoolean(R.styleable.Window_windowTranslucentNavigation,
                false)) {
            setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION
                    & (~getForcedWindowFlags()));
        }

        if (a.getBoolean(R.styleable.Window_windowOverscan, false)) {
            setFlags(FLAG_LAYOUT_IN_OVERSCAN, FLAG_LAYOUT_IN_OVERSCAN&(~getForcedWindowFlags()));
        }

        if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) {
            setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));
        }

        if (a.getBoolean(R.styleable.Window_windowEnableSplitTouch,
                getContext().getApplicationInfo().targetSdkVersion
                        >= android.os.Build.VERSION_CODES.HONEYCOMB)) {
            setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));
        }

        a.getValue(R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);
        a.getValue(R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);
        if (a.hasValue(R.styleable.Window_windowFixedWidthMajor)) {
            if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();
            a.getValue(R.styleable.Window_windowFixedWidthMajor,
                    mFixedWidthMajor);
        }
        if (a.hasValue(R.styleable.Window_windowFixedWidthMinor)) {
            if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();
            a.getValue(R.styleable.Window_windowFixedWidthMinor,
                    mFixedWidthMinor);
        }
        if (a.hasValue(R.styleable.Window_windowFixedHeightMajor)) {
            if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();
            a.getValue(R.styleable.Window_windowFixedHeightMajor,
                    mFixedHeightMajor);
        }
        if (a.hasValue(R.styleable.Window_windowFixedHeightMinor)) {
            if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();
            a.getValue(R.styleable.Window_windowFixedHeightMinor,
                    mFixedHeightMinor);
        }
        if (a.getBoolean(R.styleable.Window_windowContentTransitions, false)) {
            requestFeature(FEATURE_CONTENT_TRANSITIONS);
        }
        if (a.getBoolean(R.styleable.Window_windowActivityTransitions, false)) {
            requestFeature(FEATURE_ACTIVITY_TRANSITIONS);
        }

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

判斷是否有actionBar
        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);
        }
獲取狀態列和導航欄的顏色
        if (!mForcedStatusBarColor) {
            mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);
        }
        if (!mForcedNavigationBarColor) {
            mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);
        }
     呼叫setSystemUiVisibility來設定視窗的顯示狀態,是否顯示狀態列,是否全屏等
        if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) {
            decor.setSystemUiVisibility(
                    decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
        }

       獲取Params
        WindowManager.LayoutParams params = getAttributes();
      設定軟鍵盤的模式
        if (!hasSoftInputMode()) {
            params.softInputMode = a.getInt(
                    R.styleable.Window_windowSoftInputMode,
                    params.softInputMode);
        }
        // 根據設定的feature,載入系統定義好的含有id=content的佈局,是否有標題欄,actionbar,進度條等
        int layoutResource;
        int features = getLocalFeatures();
        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
                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
            // Special case for a window with only a progress bar (and title).
            // XXX Need to have a no-title version of embedded windows.
            layoutResource = R.layout.screen_progress;
            // System.out.println("Progress!");
        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
            // Special case for a window with a custom title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogCustomTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_custom_title;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
            // If no other features and not embedded, only need a title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
                layoutResource = a.getResourceId(
                        R.styleable.Window_windowActionBarFullscreenDecorLayout,
                        R.layout.screen_action_bar);
            } else {
                layoutResource = R.layout.screen_title;
            }
            // System.out.println("Title!");
        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
            layoutResource = R.layout.screen_simple_overlay_action_mode;
        } else {
            // Embedded, so no decoration is needed.
            layoutResource = R.layout.screen_simple;
            // System.out.println("Simple!");
        }
        mDecor.startChanging();
        View in = mLayoutInflater.inflate(layoutResource, null);
通過mLayoutInflater載入好對應的佈局,然後新增到decor中
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        mContentRoot = (ViewGroup) in;
//這裡就找到id為conteng的佈局,也就是我們自己定義的佈局的直接父佈局了
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }
設定背景
        if (getContainer() == null) {
            final Drawable background;
            if (mBackgroundResource != 0) {
                background = getContext().getDrawable(mBackgroundResource);
            } else {
                background = mBackgroundDrawable;
            }
            mDecor.setWindowBackground(background);

            final Drawable frame;
            if (mFrameResource != 0) {
                frame = getContext().getDrawable(mFrameResource);
            } else {
                frame = null;
            }
            mDecor.setWindowFrame(frame);

            mDecor.setElevation(mElevation);
            mDecor.setClipToOutline(mClipToOutline);

            if (mTitle != null) {
                setTitle(mTitle);
            }

            if (mTitleColor == 0) {
                mTitleColor = mTextColor;
            }
            setTitleColor(mTitleColor);
        }

        mDecor.finishChanging();

        return contentParent;
    }

這個方法主要就是根據設定的feature載入含有id=content的佈局,然後新增到decor中,然後在載入id=content的佈局當contentParent.所以就是decor->含有id=content的佈局(包含標題欄,等)->我們自定的佈局.
拿到contengParent之後就又回到setContentView中通過mLayoutInflater載入我們自己定義的佈局,在通過callback回撥給activit的onContentChanged,到此應用程式視窗檢視就建立完成了.下面通過一張圖表示window,Decorview和mContentParent的關係
這裡寫圖片描述

視窗檢視的顯示

視窗的檢視的建立是在oncreate中完成的,而oncreate是在handleLaunchActivity的performLaunchActivity中創建出activity並且呼叫的,視窗的顯示就是要呼叫handleLaunchActivityhandleResumeActivity(r.token, false, r.isForward);來完成了.

1.ActivityThread.handleLaunchActivity

  1. public final class ActivityThread {  
  2.     ......  
  3.   
  4.     private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {  
  5.         ......  
  6.        //建立activity
  7.         Activity a = performLaunchActivity(r, customIntent);  
  8.   
  9.         if (a != null) {  
  10.             ......  
  11.            顯示activity
  12.             handleResumeActivity(r.token, false, r.isForward);  
  13.   
  14.             ......  
  15.         }  
  16.   
  17.         ......  
  18.     }  
  19.   
  20.     ......  
  21. } 

2.ActivityThread.handleResumeActivity

  1. public final class ActivityThread {  
  2.     ......  
  3.   
  4.     final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {  
  5.         ......  
  6.   通知Activity元件,它要被激活了,即會導致Activity元件的成員函式onResume被呼叫
  7.         ActivityClientRecord r = performResumeActivity(token, clearHide);  
  8.   
  9.          r就表示要啟用的activity
  10.         if (r != null) {  
  11.             final Activity a = r.activity;  
  12.             ......  
  13.            Activity類的成員變數mStartedActivity用來描述一個Activity元件是否正在啟動一個新的Activity元件,並且等待這個新的Activity元件的執行結果。如果是的話,那麼這個Activity元件的成員變數mStartedActivity的值就會等於true,表示在新的Activity元件的執行結果返回來之前,當前Activity元件要保持不可見的狀態.
  14.             boolean willBeVisible = !a.mStartedActivity;  
  15.             if (!willBeVisible) {  
  16.                 try {  
  17.                     willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(  
  18.                             a.getActivityToken());  
  19.                 } catch (RemoteException e) {  
  20.                 }  
  21.             }  
  22. r.window用來描述當前正在啟用的Activity元件a所關聯的應用程式視窗物件,當它的值等於null的時候,就表示當前正在啟用的Activity元件a所關聯的應用程式視窗物件還沒有關聯一個ViewRoot物件,如果這個acitiviy是活著的且接下來是要可見的就要為它建立一個window和viewroot物件
  23.             if (r.window == null && !a.mFinished && willBeVisible) {  
  24.                 通過activity的getWindow(拿到對應的視窗物件
  25.                 r.window = r.activity.getWindow();  
  26.                  在拿到視窗對應的檢視物件
  27.                 View decor = r.window.getDecorView();  
  28.                 decor.setVisibility(View.INVISIBLE);  
  29.                 拿到視窗對應的視窗管理者
  30.                 ViewManager wm = a.getWindowManager();  
  31.                拿到佈局引數
  32.                 WindowManager.LayoutParams l = r.window.getAttributes();  
  33.                 a.mDecor = decor;  
  34.                 l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;  
  35.                 ......  
  36.                 在判斷正在啟用的Activity元件a在本地程序中是否是可見的,即它的成員變數mVisibleFromClient的值是否等於true
  37.                 if (a.mVisibleFromClient) {  
  38.                     a.mWindowAdded = true;  
  39.                    呼叫視窗管理者的addview方法將佈局顯示出來
  40.                     wm.addView(decor, l);  
  41.                 }  
  42.             }   
  43.   
  44.             ......  
  45.         }  
  46.   
  47.         ......  
  48.     }  
  49.     
  50.     ......  
  51. }  

這裡的wm 是一個LocalWindowManager 物件,但是他實際是是利用的WindowManagerImpl物件的addview方法來完成的,

3.WindowManagerImpl .addview

  1. public class WindowManagerImpl implements WindowManager {  
  2.     ......  
  3.   
  4.     public void addView(View view, ViewGroup.LayoutParams params)  
  5.     {  
  6.         addView(view, params, false);  
  7.     }  
  8.   
  9.     ......  
  10.   
  11.     private void addView(View view, ViewGroup.LayoutParams params, boolean nest)  
  12.     {  
  13.         ......  
  14.   
  15.         final WindowManager.LayoutParams wparams  
  16.                 = (WindowManager.LayoutParams)params;  
  17.   
  18.         ViewRootImpl root;  
  19.         View panelParentView = null;  
  20.   
  21.         synchronized (this) {  
  22.           

WindowManagerImpl類關聯著一個應用程式視窗檢視物件(View物件)和一個ViewRoot物件的。一個View物件在與一個ViewRoot物件關聯的同時,還會關聯一個WindowManager.LayoutParams物件,這個WindowManager.LayoutParams物件是用來描述應用程式視窗檢視的佈局屬性的。
怎麼關聯呢?其實就是通過3個數組來關聯的,WindowManagerImpl類有三個成員變數mViews、mRoots和mParams,它們分別是型別為View、ViewRootImpl和WindowManager.LayoutParams的陣列。這三個陣列的大小是始終保持相等的。這樣, 有關聯關係的View物件、ViewRootImpl物件和WindowManager.LayoutParams物件就會分別儲存在陣列mViews、mRoots和mParams的相同位置上,也就是說,mViews[i]、mRoots[i]和mParams[i]所描述的View物件、ViewRoot物件和WindowManager.LayoutParams物件是具有關聯關係的,所以這裡先從mViews中查詢是否有對應的view,如果已經存在的話,那麼就說明該View物件已經關聯過ViewRoot物件以及WindowManager.LayoutParams物件了。在這種情況下,如果引數nest的值等於false,那麼成員函式addView是不允許重複對引數view所描述的一個View物件進行重新關聯的。另一方面,如果引數nest的值等於true,那麼成員函式addView只是重新修改引數view所描述的一個View物件及其所關聯的一個ViewRootImpl物件內部使用的一個WindowManager.LayoutParams物件,即更新為引數params所描述的一個WindowManager.LayoutParams物件,這是通過呼叫它們的成員函式setLayoutParams來實現的。
  23.             int index = findViewLocked(view, false);  
  24.             if (index >= 0) {  
  25.                 if (!nest) {  
  26.                     throw new IllegalStateException("View " + view  
  27.                             + " has already been added to the window manager.");  
  28.                 }  
  29.                 root = mRoots[index];  
  30.                 root.mAddNesting++;  
  31.                 // Update layout parameters.  
  32.                 view.setLayoutParams(wparams);  
  33.                 root.setLayoutParams(wparams, true);  
  34.                 return;  
  35.             }  
  36.   
  37.             當引數view描述的是一個子應用程式視窗的檢視物件時, 還需要找到這個子檢視物件的父檢視物件panelParentView
  38.             if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&  
  39.                     wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {  
  40.                 final int count = mViews != null ? mViews.length : 0;  
  41.                 for (int i=0; i<count; i++) {  
  42.                     if (mRoots[i].mWindow.asBinder() == wparams.token) {  
  43.                         panelParentView = mViews[i];  
  44.                     }  
  45.                 }  
  46.             }  
  47.           通過view建立對應的ViewRootImpl物件
  48.             root = new ViewRootImpl(view.getContext(), display); 
  49.             root.mAddNesting = 1;  
  50.   
  51.             view.setLayoutParams(wparams);  
  52.           
  53.             如果陣列不存在,先建立,否則就繼續擴充
  54.             if (mViews == null) {  
  55.                 index = 1;  
  56.                 mViews = new View[1];  
  57.                 mRoots = new ViewRoot[1];  
  58.                 mParams = new WindowManager.LayoutParams[1];  
  59.             } else {  
  60.                 index = mViews.length + 1;  
  61.                 Object[] old = mViews;  
  62.                 mViews = new View[index];  
  63.                 System.arraycopy(old, 0, mViews, 0, index-1);  
  64.                 old = mRoots;  
  65.                 mRoots = new ViewRoot[index];  
  66.                 System.arraycopy(old, 0, mRoots, 0, index-1);  
  67.                 old = mParams;  
  68.                 mParams = new WindowManager.LayoutParams[index];  
  69.                 System.arraycopy(old, 0, mParams, 0, index-1);  
  70.             }  
  71.             index--;  
  72.   
  73.             mViews[index] = view;  
  74.             mRoots[index] = root;  
  75.             mParams[index] = wparams;  
  76.         }  
  77.         最後呼叫ViewRootImpl的setview方法 
  78.         root.setView(view, wparams, panelParentView);  
  79.     }  
  80.   
  81.     ......  
  82.   
  83.     private View[] mViews;  
  84.     private ViewRootImpl[] mRoots;  
  85.     private WindowManager.LayoutParams[] mParams;  
  86.   
  87.     ......  
  88. }  

4.ViewRootImp.setView

  1. public final class ViewRoot extends Handler implements ViewParent,  
  2.         View.AttachInfo.Callbacks {  
  3.     ......  
  4.   
  5.     final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();  
  6.     ......  
  7.   
  8.     View mView;  
  9.     ......  
  10.   
  11.     final View.AttachInfo mAttachInfo;  
  12.     ......  
  13.   
  14.     boolean mAdded;  
  15.     ......  
  16.   
  17.     public void setView(View view, WindowManager.LayoutParams attrs,  
  18.             View panelParentView) {  
  19.         synchronized (this) {  
  20.             分別將vie賦值給mView和mAttachInfo.mRootView
  21.             if (mView == null) {  
  22.                 mView = view;  
  23.                 mWindowAttributes.copyFrom(attrs);  
  24.                 ......  
  25.   
  26.                 mAttachInfo.mRootView = view;  
  27.                 .......  
  28.              如果panelParentView 不為null,表示這個view是一個子視窗,那麼就要拿到用來描述這個父應用程式視窗檢視物件的一個型別為W的Binder本地物件的IBinder介面,以便可以儲存在ViewRoot類的成員變數mAttachInfo所描述的一個AttachInfo的成員變數mPanelParentWindowToken中去。這樣以後就可以知道ViewRoot類的成員變數mView所描述的一個子應用程式視窗檢視所屬的父應用程式視窗檢視是什麼了
  29.                 if (panelParentView != null) {  
  30.                     mAttachInfo.mPanelParentWindowToken  
  31.                             = panelParentView.getApplicationWindowToken();  
  32.                 }  
  33. mAdded的值設定為true,表示當前正在處理的一個ViewRoot物件已經關聯好一個View物件了
  34.                 mAdded = true;  
  35.                 ......  
  36.   呼叫ViewRootImpl類的另外一個成員函式requestLayout來請求對應用程式視窗檢視的UI作第一次佈局(會呼叫view的 onMeasue,onlayout方法,ondraw方法),這裡首先會判斷checkThread()方法來檢測,當前執行緒是不是UI執行緒
  37.                 requestLayout();  
  38.                 ......  
  39.                 try {  
  40. 呼叫ViewRoot類的靜態成員變數sWindowSession所描述的一個型別為Session的Binder代理物件的成員函式add來請求WindowManagerService增加一個WindowState物件,以便可以用來描述當前正在處理的一個ViewRoot所關聯的一個應用程式視窗。mWindow其實也是一個W的binder物件,表示的是當前的activity,當WMS中就會建立W對應的windowState物件.wms通過這個物件就可以與這個activity互動
  41.                     res = sWindowSession.add(mWindow, mWindowAttributes,  
  42.                             getHostVisibility(), mAttachInfo.mContentInsets,  
  43.                             mInputChannel);  
  44.                 } catch (RemoteException e) {  
  45.                     mAdded = false;  
  46.                     mView = null;  
  47.                     ......  
  48.                     throw new RuntimeException("Adding window failed", e);