Android應(yīng)用程序窗口(Activity)的窗口對(duì)象(Window)的創(chuàng)建過程分析

每一個(gè)Activity組件都有一個(gè)關(guān)聯(lián)的ContextImpl對(duì)象,同時(shí)秫舌,它還關(guān)聯(lián)有一個(gè)Window對(duì)象梦重,用來(lái)描述一個(gè)具體的應(yīng)用程序窗口赏壹。與Activity組件所關(guān)聯(lián)的窗口對(duì)象的實(shí)際類型為PhoneWindow炊甲。

那么創(chuàng)建這個(gè)window的過程就如圖所示:
TIM圖片20180531155329.png

上面的圖可以分為9個(gè)步驟,接下來(lái)我們開始分析每一個(gè)步驟

如果想研究Activity中的Window創(chuàng)建過程就必須知道Activity的啟動(dòng)過程欲芹,這里不做具體講解卿啡,我們只要知道最終會(huì)由ActivityThread中的performLaunchActivity()來(lái)完成整個(gè)啟動(dòng)過程的,這個(gè)過程中會(huì)調(diào)用Activity的attach方法為activity關(guān)聯(lián)運(yùn)行過程中所依賴的一系列上下文環(huán)境變量菱父。

 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

         ......省略部分
            if (activity != null) {
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (r.overrideConfig != null) {
                    config.updateFrom(r.overrideConfig);
                }
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
                Window window = null;
                if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                    window = r.mPendingRemoveWindow;
                    r.mPendingRemoveWindow = null;
                    r.mPendingRemoveWindowManager = null;
                }
                appContext.setOuterContext(activity);
                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);

    ......省略部分
        return activity;
    }
步驟一:Activity.attach

在Activity的attach方法里颈娜,系統(tǒng)會(huì)創(chuàng)建Activity所屬的Window對(duì)象并為它設(shè)置回調(diào)接口,首先是調(diào)用PolicyManager的makeNewWindow來(lái)創(chuàng)建一個(gè)類型為PhoneWindow的應(yīng)用程序窗口浙宜,并且保存在Activity類的成員變量mWindow中官辽,因?yàn)樵O(shè)置了Callback接口,所以當(dāng)外界的狀態(tài)改變的時(shí)候Activity就能感知到并做出回應(yīng)處理粟瞬。

public class Activity extends ContextThemeWrapper    
        implements LayoutInflater.Factory,    
        Window.Callback, KeyEvent.Callback,    
        OnCreateContextMenuListener, ComponentCallbacks {    
    ......     
    
    private Window mWindow;     
    ......    
    
    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,    
            Object lastNonConfigurationInstance,    
            HashMap<String,Object> lastNonConfigurationChildInstances,    
            Configuration config) {    
        ......    
    
        mWindow = PolicyManager.makeNewWindow(this);    
        mWindow.setCallback(this);    
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {    
            mWindow.setSoftInputMode(info.softInputMode);    
        }    
        ......    
    
        mWindow.setWindowManager(null, mToken, mComponent.flattenToString());    
        ......    
      
    }    
    
    ......    
}    
步驟二:PolicyManager.makeNewWindow

Activity的Window是通過PolicyManager的一個(gè)方法創(chuàng)建的同仆, PolicyManager是一個(gè)窗口管理策略類,它在第一次被使用的時(shí)候裙品,就會(huì)創(chuàng)建一個(gè)Policy類實(shí)例俗批,并且保存在靜態(tài)成員變量sPolicy中,而PolicyManager的makeNewWindow方法其實(shí)就是調(diào)用的sPolicy.makeNewWindow(context);來(lái)實(shí)現(xiàn)的。

public final class PolicyManager {  
    private static final String POLICY_IMPL_CLASS_NAME =  
        "com.android.internal.policy.impl.Policy";  
  
    private static final IPolicy sPolicy;  
  
    static {  
        // Pull in the actual implementation of the policy at run-time  
        try {  
            Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);  
            sPolicy = (IPolicy)policyClass.newInstance();  
        } catch (ClassNotFoundException ex) {  
            throw new RuntimeException(  
                    POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);  
        } catch (InstantiationException ex) {  
            throw new RuntimeException(  
                    POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);  
        } catch (IllegalAccessException ex) {  
            throw new RuntimeException(  
                    POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);  
        }  
    }  
  
    ......  
  
    // The static methods to spawn new policy-specific objects  
    public static Window makeNewWindow(Context context) {  
        return sPolicy.makeNewWindow(context);  
    }  
  
    ......  
}  
步驟三:Policy.makeNewWindow

這個(gè)方法就跟簡(jiǎn)單了就是創(chuàng)建了一個(gè)PhoneWindow對(duì)象市怎,然后返回給了調(diào)用者了.

public class Policy implements IPolicy {  
    ......  
  
    public PhoneWindow makeNewWindow(Context context) {  
        return new PhoneWindow(context);  
    }  
   
    ......  
}  
步驟四:new PhoneWindow

下面代碼可以發(fā)現(xiàn)岁忘,使用LayoutInflater.from創(chuàng)建了一個(gè)LayoutInflater,PhoneWindow使用它來(lái)創(chuàng)建窗口的視圖区匠,并放在類型為ViewGroup的成員變量mContentParent中干像,這個(gè)稍后會(huì)講。

 public PhoneWindow(Context context) {
        super(context);
        mLayoutInflater = LayoutInflater.from(context);
    }

這個(gè)Context通過一層層的調(diào)用我們知道正是當(dāng)前啟動(dòng)的這個(gè)Activity,這樣一來(lái)那麻汰,PhoneWindow就可以訪問與Activity組件相關(guān)的資源了速客。接下來(lái)我們接著回到第一個(gè)步驟,在Activity的attach中創(chuàng)建了Window什乙,來(lái)給當(dāng)前的Activity通過setCallback來(lái)設(shè)置窗口回調(diào)接口挽封。接下來(lái)就分析一下這個(gè)方法

步驟五:Window.setCallback

當(dāng)我們給Activity設(shè)置上回調(diào)的時(shí)候Callback接口中的方法很多,這樣當(dāng)這個(gè)PhoneWindow對(duì)象接收到系統(tǒng)給它分發(fā)的IO輸入事件臣镣,例如辅愿,鍵盤和觸摸屏事件,轉(zhuǎn)發(fā)給與它所關(guān)聯(lián)的Activity組件處理忆某,点待,我們比較熟悉的方法有:dispatchTouchEvent,onAttachedToWindow弃舒,等等吧癞埠。

public abstract class Window {  
    ......  
  
    private Callback mCallback;  
    ......  
  
    /** 
     * Set the Callback interface for this window, used to intercept key 
     * events and other dynamic operations in the window. 
     * 
     * @param callback The desired Callback interface. 
     */  
    public void setCallback(Callback callback) {  
        mCallback = callback;  
    }  
    
    ......  
}  
步驟六:Window.setSoftInputMode

接著第一步驟繼續(xù)往下走,設(shè)置應(yīng)用程序窗口的軟鍵盤輸入?yún)^(qū)域的顯示模式,此方法中首先判斷當(dāng)參數(shù)mode的值是否等于SOFT_INPUT_STATE_UNSPECIFIED聋呢,如果不等于苗踪,就表示當(dāng)前窗口被指定軟鍵盤輸入?yún)^(qū)域的顯示模式,然后調(diào)用函數(shù)onWindowAttributesChanged來(lái)通知與窗口所關(guān)聯(lián)的Activity組件削锰,它的窗口布局屬性發(fā)生了變化通铲。

 public void setSoftInputMode(int mode) {
        final WindowManager.LayoutParams attrs = getAttributes();
        if (mode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            attrs.softInputMode = mode;
            mHasSoftInputMode = true;
        } else {
            mHasSoftInputMode = false;
        }
        dispatchWindowAttributesChanged(attrs);
    }
步驟七:Window.setWindowManager

我們繼續(xù)來(lái)看Activity的attach方法,接下來(lái)就到了調(diào)用Window的setWindowManager來(lái)實(shí)現(xiàn)窗口管理器贩。
appToken保存了當(dāng)前處理的窗口是與哪一個(gè)Activity組件關(guān)聯(lián)的颅夺。
appName用來(lái)描述當(dāng)前正在處理的窗口所關(guān)聯(lián)的Activity組件的名稱。
wm是用來(lái)描述一個(gè)窗口管理者蛹稍,一開始傳進(jìn)來(lái)的就是一個(gè)null所以會(huì)創(chuàng)建一個(gè)窗口管理器吧黄,然后再使用它創(chuàng)建一個(gè)本地窗口管理器即可用來(lái)維護(hù)當(dāng)前正在處理的應(yīng)用程序窗口。

 public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }
步驟八:mContext.getSystemService(Context.WINDOW_SERVICE);

因?yàn)楫?dāng)前的mContext是傳入的Activity唆姐,所以調(diào)用的是Activity的getSystemService拗慨,判斷傳入的name等于WINDOW_SERVICE,返回了一個(gè)WindowManager奉芦。

 @Override
    public Object getSystemService(@ServiceName @NonNull String name) {
        if (getBaseContext() == null) {
            throw new IllegalStateException(
                    "System services not available to Activities before onCreate()");
        }

        if (WINDOW_SERVICE.equals(name)) {
            return mWindowManager;
        } else if (SEARCH_SERVICE.equals(name)) {
            ensureSearchManager();
            return mSearchManager;
        }
        return super.getSystemService(name);
    }
步驟九: ((WindowManagerImpl)wm).createLocalWindowManager(this);

創(chuàng)建一個(gè)WindowManagerImpl胆描,類繼承WindowManager,所以就是創(chuàng)建了一個(gè)窗口管理仗阅。這樣就可以使用窗口管理器來(lái)管理應(yīng)用程序窗口了

public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

至此昌讲,我們就分析完成一個(gè)Activity組件所關(guān)聯(lián)的應(yīng)用程序窗口對(duì)象的創(chuàng)建過程了。如果有錯(cuò)誤的地方希望大家多多指出來(lái)减噪,此時(shí)視圖還沒有添加上來(lái)短绸,接下來(lái)的一篇我們將分析應(yīng)用窗口視圖對(duì)象的添加過程车吹!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市醋闭,隨后出現(xiàn)的幾起案子窄驹,更是在濱河造成了極大的恐慌,老刑警劉巖证逻,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乐埠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡囚企,警方通過查閱死者的電腦和手機(jī)丈咐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)龙宏,“玉大人棵逊,你說我怎么就攤上這事∫铮” “怎么了辆影?”我有些...
    開封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)黍特。 經(jīng)常有香客問我蛙讥,道長(zhǎng),這世上最難降的妖魔是什么灭衷? 我笑而不...
    開封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任次慢,我火速辦了婚禮,結(jié)果婚禮上今布,老公的妹妹穿的比我還像新娘。我一直安慰自己拭抬,他們只是感情好部默,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著造虎,像睡著了一般傅蹂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上算凿,一...
    開封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天份蝴,我揣著相機(jī)與錄音,去河邊找鬼氓轰。 笑死婚夫,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的署鸡。 我是一名探鬼主播案糙,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼限嫌,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了时捌?” 一聲冷哼從身側(cè)響起怒医,我...
    開封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎奢讨,沒想到半個(gè)月后稚叹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拿诸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年扒袖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片佳镜。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡僚稿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蟀伸,到底是詐尸還是另有隱情蚀同,我是刑警寧澤,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布啊掏,位于F島的核電站蠢络,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏迟蜜。R本人自食惡果不足惜刹孔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望娜睛。 院中可真熱鬧髓霞,春花似錦、人聲如沸畦戒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)障斋。三九已至纵潦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間垃环,已是汗流浹背邀层。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留遂庄,地道東北人寥院。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像涛目,于是被迫代替她去往敵國(guó)和親只磷。 傳聞我的和親對(duì)象是個(gè)殘疾皇子经磅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

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