創(chuàng)建應(yīng)用窗口過程

window類關(guān)系圖

1.每個窗口都對應(yīng)一個Activity對象,所以創(chuàng)建應(yīng)用窗口首先是創(chuàng)建Activity.而啟動Activity的任務(wù)由ActivityThread完成,本質(zhì)是構(gòu)造一個Activity對象.當(dāng)ActivityThread接受到Ams發(fā)送的啟動Activity通知時,會創(chuàng)建指定的Activity對象,Activity創(chuàng)建PhoneWindow類--DecorView類--創(chuàng)建相應(yīng)的View或ViewGroup.創(chuàng)建完成后,Activity把創(chuàng)建好的界面顯示到屏幕上,于是調(diào)用WindowManager類,WindowManager創(chuàng)建ViewRoot類和ViewRoot的內(nèi)部類W.之后WindowManager在調(diào)用Wms提供的遠(yuǎn)程接口完成窗口的添加并顯示在屏幕上

  1. 代碼入口是ActivityThread.performLaunchActivity
  private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ActivityInfo aInfo = r.activityInfo;

    Activity activity = null;
    try { //1.創(chuàng)建activity的對象,用classLoader從程序中裝載指定Activity對應(yīng)的Class文件
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
    } 
         //2.創(chuàng)建 系統(tǒng)application,appContext appContext本質(zhì)是ContextImpl
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);
         Context appContext = createBaseContextForActivity(r, activity);
         //3.為activity 配置內(nèi)部變量.同時創(chuàng)建Window對象. this變量使Activity對象持有ActivityThread的引用.r 是ActivityClientRecord對象.
r.token是一個Binder,是Ams中的一個HistoryRecord對象. r.parent是一個父Activity,這種理念是為了允許把Activity嵌入到另一個Activity內(nèi)部執(zhí)行,
            activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config,
                    r.voiceInteractor);

            int theme = r.activityInfo.getThemeResource(); //activity使用的主題
            if (theme != 0) {
                activity.setTheme(theme);
            }

          //4 這里會調(diào)用Activity的onCreate方法.
            if (r.isPersistable()) {
                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            } else {
                mInstrumentation.callActivityOnCreate(activity, r.state);
            }

            //調(diào)用 onstart
            if (!r.activity.mFinished) {
                activity.performStart();
                r.stopped = false;
            }
            //調(diào)用 onRestoreInstanceState
            if (!r.activity.mFinished) {
                if (r.isPersistable()) {
                    if (r.state != null || r.persistentState != null) {
                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                r.persistentState);
                    }
                } else if (r.state != null) {
                    mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                }
            }
            //調(diào)用 onPostCreate
            if (!r.activity.mFinished) {
                activity.mCalled = false;
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnPostCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnPostCreate(activity, r.state);
                }
              
            }
        }

        //ArrayMap<IBinder, ActivityClientRecord>結(jié)構(gòu)
        mActivities.put(r.token, r);
    return activity;
}

//2.接著看步驟1.3的Actdivity 的attacth方法

  //Activity 類
 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, IVoiceInteractor voiceInteractor) {
    attachBaseContext(context); //新建的contextImpl賦值給mBase.


    //1.創(chuàng)建Window.其實是創(chuàng)建了Window的子類PhoneWindow
    mWindow = PolicyManager.makeNewWindow(this);
    mWindow.setCallback(this); //表示Activity實現(xiàn)的Window的所有回調(diào)函數(shù)


      //activity配置重要變量
    mUiThread = Thread.currentThread();
    mMainThread = aThread;  
    mInstrumentation = instr;
    mToken = token;   //Binder HistoryRecord類
    mApplication = application;
    mIntent = intent;
    mComponent = intent.getComponent();
    mActivityInfo = info;
    mTitle = title;
    mParent = parent; //activity
     
    //2.給每個window分配WindowManager,其實是WindowManagerImpl.每個Window都對應(yīng)一個WindowManagerImpl對象,
但WindowManger是一個重量級的類.他的真正實現(xiàn)是WindowManagerGlobal,而WindowManagerImpl只是一個裝飾類,只是
把功能交給WindowManagerGlobal來實現(xiàn)
    mWindow.setWindowManager(
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    if (mParent != null) {//如果有父activity,就把父acitivty的window賦值給本Activity的window的container變量.
        mWindow.setContainer(mParent.getWindow());
    }
    //activity 也持有WindowManager變量
    mWindowManager = mWindow.getWindowManager();
}

//3.接下來是步驟1.4的 mInstrumentation.callActivityOnCreate(activity, r.state);

//1.Instrumentation類 他又調(diào)用Activity的performCreate方法
public void callActivityOnCreate(Activity activity, Bundle icicle) {
    prePerformCreate(activity);
    activity.performCreate(icicle);
    postPerformCreate(activity);
} 
  //2.Activity 類 此時走到了onCreate ,我們會在onCreate中調(diào)用setContentView方法.接著看
final void performCreate(Bundle icicle) {
    onCreate(icicle);
    mActivityTransitionState.readState(icicle);
    performCreateCommon();
  }
 //3.主要還是調(diào)用    getWindow().setContentView().而   getWindow()就是我們第一步生的Window類PhoneWindow對象.
 public void setContentView(View view, ViewGroup.LayoutParams params) {
    getWindow().setContentView(view, params);
    initWindowDecorActionBar();
  }
  //4.phoneWindow類
 @Override
public void setContentView(View view, ViewGroup.LayoutParams params) {

    if (mContentParent == null) {
        installDecor(); //5.初始化Decor,為Window創(chuàng)建界面.
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews(); 
    }
      //6.把要添加的view放在 mContentParent中.下邊一個是有位移的添加方法,一個是直接添加.
  mContentParnet其實就是我們setcontView是的那個view.他的id就是R.id.content.
  我們平時定義的lauout.xml就成了mContentParnet 的子View
    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        view.setLayoutParams(params);
        final Scene newScene = new Scene(mContentParent, view);
        transitionTo(newScene);
    } else {
        mContentParent.addView(view, params);
    }
    //7.此時的callback就是2.1時創(chuàng)建Window時設(shè)置的回調(diào).既Activity自己.
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged(); //調(diào)用activity的onContentChanged回調(diào)方法.
    }
}

//4.看3.5的installDecor()方法

 private void installDecor() {
    if (mDecor == null) {
        mDecor = generateDecor(); //1.創(chuàng)建一個DecorView.繼承自FrameLayout,是窗口的最頂層View.
    }
    if (mContentParent == null) {
        //2.這是我們自定義View的父View.
        mContentParent = generateLayout(mDecor);
    }
        // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
        mDecor.makeOptionalFitsSystemWindows();//設(shè)置view適配系統(tǒng)窗口
        //下邊是根據(jù)設(shè)定的主題來設(shè)置標(biāo)題欄樣式.先不關(guān)注
        ...
 }

//5.接著看 generateLayout()方法.

 protected ViewGroup generateLayout(DecorView decor) {//decor是剛創(chuàng)建的DecorView
    TypedArray a = getWindowStyle(); //1.指定Window樣式

    //2.requestFeature()啟用窗體的擴(kuò)展特性,用戶可調(diào)用這個方法更改window樣式
    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);
    }
      ...
     //3.得到最終要使用的resource文件id.這個文件肯定包含一個id=content的FrameLayout,
    int layoutResource;
    int features = getLocalFeatures();
    mDecor.startChanging();
    //4.把該resource文件添加到Decorview里.它是DevorView的唯一view,同時賦值給變量mContentRoot
    View in = mLayoutInflater.inflate(layoutResource, null);
    decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    mContentRoot = (ViewGroup) in;
    //5.contentParent是mContentRoot的子view
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    if (contentParent == null) {
        throw new RuntimeException("Window couldn't find content container view");
    }
    mDecor.finishChanging();

    return contentParent; 
}

//6.這樣.window的窗口元素就設(shè)置完成,接下來通知Wms創(chuàng)建窗口.首先是Activity通知Ams,Ams處理完后調(diào)用Activity的makeVisible()方法,該方法和后續(xù)操作將把窗口真正添加進(jìn)Wms中.

//Activity類
 void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();//1.這里就是Activity內(nèi)部的WindowManager類,具體其實是
WindowManagerImpl類,然后會調(diào)用WindowManagerGlobal的addView方法.
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}
//2.接著看WindowManagerGlobal的addView
//1.WindowMangerGlobal有三個變量.ArrayList<View> mViews 每個View都將成為Wms所認(rèn)為的一個窗口.
ArrayList<ViewRootImpl> mRoots 每個View都對應(yīng)的一個ViewRootImpl對象,
ArrayList<WindowManager.LayoutParams> mParams 每個View對應(yīng)的參數(shù),
public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;

    ViewRootImpl root;
    View panelParentView = null;

    synchronized (mLock) {
        // Start watching for system property changes.
        if (mSystemPropertyUpdater == null) {
            mSystemPropertyUpdater = new Runnable() {
                @Override public void run() {
                    synchronized (mLock) {
                        for (int i = mRoots.size() - 1; i >= 0; --i) {
                            mRoots.get(i).loadSystemProperties(); //加載系統(tǒng)屬性
                        }
                    }
                }
            };
            SystemProperties.addChangeCallback(mSystemPropertyUpdater);
        }

        int index = findViewLocked(view, false); //2.如果該View已經(jīng)添加過了.就不允許重復(fù)添加
        if (index >= 0) {
            if (mDyingViews.contains(view)) {
                // Don't wait for MSG_DIE to make it's way through root's queue.
                mRoots.get(index).doDie();
            } else {
                throw new IllegalStateException("View " + view
                        + " has already been added to the window manager.");
            }
            // The previous removeView() had not completed executing. Now it has.
        }

        // 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) {
 //3.1000-1999之間,是子窗口,需要找他的父窗口保存在panelParentView變量中
            final int count = mViews.size();
            for (int i = 0; i < count; i++) {
                if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                    panelParentView = mViews.get(i);
                }
            }
        }
     //4.生成ViewRootImpl.用來溝通Wms和View,
        root = new ViewRootImpl(view.getContext(), display);
        view.setLayoutParams(wparams);
     //5.把該view對應(yīng)的變量添加到WindowManagerGlobal的三個對象中.
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
    }

    // do this last because it fires off messages to start doing things
    try {
    //6.完成最后的真正意義的添加動作 view是要添加的窗口 wParams是窗口的參數(shù)LayoutParams,panelParentView表示該窗口的父窗口,可以為空
        root.setView(view, wparams, panelParentView);
    } catch (RuntimeException e) {
        throw e;
    }
}

//接下來看ViewRootImpl的setView方法

public void setView(View view, WindowManager.LayoutParams attrs, View       panelParentView) {
    synchronized (this) {
        if (mView == null) {
            mView = view;
          //1.賦值attrs
          mWindowAttributes.copyFrom(attrs);
            if (mWindowAttributes.packageName == null) {
                mWindowAttributes.packageName = mBasePackageName;
            }
            attrs = mWindowAttributes;
            //2.配置參數(shù)
            mSoftInputMode = attrs.softInputMode;
            mWindowAttributesChanged = true;
            mWindowAttributesChangesFlag = WindowManager.LayoutParams.EVERYTHING_CHANGED;
            mAttachInfo.mRootView = view;
            mAttachInfo.mScalingRequired = mTranslator != null;
            mAttachInfo.mApplicationScale =
                    mTranslator == null ? 1.0f : mTranslator.applicationScale;
        //3.如果有父窗口,把父窗口的token賦值給mAttachInfo
            if (panelParentView != null) {
                mAttachInfo.mPanelParentWindowToken
                        = panelParentView.getApplicationWindowToken();
            }
            mAdded = true;
            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.
        //4.發(fā)出重繪請求,僅是發(fā)出一個異步請求,使Ui線程的下一個消息處理是界面重繪,使窗口在相應(yīng)其他消息之前
        先變得可見
            requestLayout();
 
            try { //5.通知Wms添加窗口,mWindowSession是WindowManagerGlobla的一個鏡頭變量,每個應(yīng)用程序僅有一個mWindowSession對象,類型為IWindowSession,是一個Binder引用,對應(yīng)Wms中的Session子類,Wms為每個應(yīng)用程序分配一個Session對象.
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(),
                        mAttachInfo.mContentInsets, mInputChannel);
            } catch (RemoteException {
                throw new RuntimeException("Adding window failed", e);
            } 
    }

至此,窗口的創(chuàng)建過程全部結(jié)束.
最后總結(jié)一下.第一步.是在ActivityThread的performLaunchActivity方法通過反射創(chuàng)建Activity啟動的對象,同時為Activity創(chuàng)建添加一個ContextImpl對象,調(diào)用Activity.attach方法,然后執(zhí)行Activity的onCreate,onStart.第二步Activity.attach中,為該Acitivity創(chuàng)建一個PhoneWindow(繼承自Window)對象,這是一個輕量的對象,其實內(nèi)部是由WindowManagerGlobal實現(xiàn)具體操作.第三步.在執(zhí)行Activity的onCreate過程中,為activity創(chuàng)建DecorView,這是每個activity的頂層view.他里邊包含一個id為content的view.就是我們調(diào)用setContentView的父view,第四部,Activity的view設(shè)置完成后.Activity通知Ams,Ams處理完后調(diào)用Activity的makeVisible()方法,該方法和后續(xù)操作將把窗口真正添加進(jìn)Wms中.此時wms為創(chuàng)建一個ViewRootImpl類,用來溝通wms和view.最后.ViewRootImpl在把view進(jìn)行一些處理后,通知Wms添加窗口

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蒙幻,一起剝皮案震驚了整個濱河市究飞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌屡谐,老刑警劉巖,帶你破解...
    沈念sama閱讀 223,126評論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異览爵,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)段磨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評論 3 400
  • 文/潘曉璐 我一進(jìn)店門取逾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人苹支,你說我怎么就攤上這事砾隅。” “怎么了债蜜?”我有些...
    開封第一講書人閱讀 169,941評論 0 366
  • 文/不壞的土叔 我叫張陵晴埂,是天一觀的道長究反。 經(jīng)常有香客問我,道長儒洛,這世上最難降的妖魔是什么精耐? 我笑而不...
    開封第一講書人閱讀 60,294評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮琅锻,結(jié)果婚禮上卦停,老公的妹妹穿的比我還像新娘。我一直安慰自己恼蓬,他們只是感情好惊完,可當(dāng)我...
    茶點故事閱讀 69,295評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著处硬,像睡著了一般小槐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上荷辕,一...
    開封第一講書人閱讀 52,874評論 1 314
  • 那天凿跳,我揣著相機(jī)與錄音,去河邊找鬼桐腌。 笑死拄显,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的案站。 我是一名探鬼主播躬审,決...
    沈念sama閱讀 41,285評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蟆盐!你這毒婦竟也來了承边?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,249評論 0 277
  • 序言:老撾萬榮一對情侶失蹤石挂,失蹤者是張志新(化名)和其女友劉穎博助,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體痹愚,經(jīng)...
    沈念sama閱讀 46,760評論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡富岳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,840評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了拯腮。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窖式。...
    茶點故事閱讀 40,973評論 1 354
  • 序言:一個原本活蹦亂跳的男人離奇死亡夜惭,死狀恐怖痛垛,靈堂內(nèi)的尸體忽然破棺而出扔罪,到底是詐尸還是另有隱情秃臣,我是刑警寧澤塞俱,帶...
    沈念sama閱讀 36,631評論 5 351
  • 正文 年R本政府宣布抡笼,位于F島的核電站畔柔,受9級特大地震影響咖驮,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜启妹,卻給世界環(huán)境...
    茶點故事閱讀 42,315評論 3 336
  • 文/蒙蒙 一筛严、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧翅溺,春花似錦脑漫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至褪猛,卻和暖如春网杆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伊滋。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評論 1 275
  • 我被黑心中介騙來泰國打工碳却, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人笑旺。 一個月前我還...
    沈念sama閱讀 49,431評論 3 379
  • 正文 我出身青樓昼浦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親筒主。 傳聞我的和親對象是個殘疾皇子关噪,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,982評論 2 361

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