Window視圖機(jī)制

1.Window

Activity中的相關(guān)變量

    /**  Activity.java */
    public class Activity extends  ... {
        private Instrumentation mInstrumentation;
        private IBinder mToken;
        ...
        private Application mApplication;
        private Window mWindow;
        private WindowManager mWindowManager;
        View mDecor = null;
        ...
    }

Activity的啟動(dòng)是由Activity中通過(guò)H這個(gè)handler調(diào)用的芋齿,實(shí)現(xiàn)代碼在ActivityThread中,代碼如下氧急。

    /**  ActivityThread.java  */
    /**  Core implementation of activity launch. */
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            ...
        } catch (Exception e) {
            ...
        }

        try {
            ...
            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) {
                ...
        } catch (Exception e) {
                ...
        }
        return activity;
    }

剔除大部分代碼我們可以看到斯辰,Activity的實(shí)例化通過(guò)反射的方式實(shí)現(xiàn)的帆焕,Activity在onCreate之前救氯,調(diào)用了attach方法進(jìn)行初始化找田。下面是attach的部分代碼

    /**Activity.java**/
    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, ActivityConfigCallback activityConfigCallback) {
        ...
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        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);
        }
        mUiThread = Thread.currentThread();

        mMainThread = aThread;
        mInstrumentation = instr;
        mToken = token;
        mIdent = ident;
        mApplication = application;
        mIntent = intent;
        mReferrer = referrer;
        mComponent = intent.getComponent();
        mActivityInfo = info;
        mTitle = title; 
        mParent = parent;
        ...
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;

        mWindow.setColorMode(info.colorMode);
        ...
    }

在attach方法里,會(huì)實(shí)例化一個(gè)PhoneWindow對(duì)象賦值給mWindow變量着憨,并將獲取到的WindowManager傳遞給mWindow墩衙。

2.頁(yè)面布局

我們?cè)贏ctivity的onCreate里,都會(huì)通過(guò)setContentView()設(shè)置界面布局甲抖,我們來(lái)看看setContentView干了啥漆改。

    /**Activity.java*/
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);//1
        initWindowDecorActionBar();
    }

    /**PhoneWindow.java*/
    @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();//2
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);//3
        }
        ...
    }

    private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            mDecor = generateDecor(-1);
            ...
        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
            ...
        }
    }

    protected DecorView generateDecor(int featureId) {
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext());
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes());
    }

1.setContentView在activity類中,會(huì)調(diào)用PhoneWindow的setContentView方法
2.PhoneWindow會(huì)檢查mContentParent准谚,如果沒(méi)有挫剑,會(huì)通過(guò)generateDecor()方法創(chuàng)建出DecorView,進(jìn)而通過(guò)generateLayout方法得到mContentParent氛魁。DecorView是一個(gè)繼承自FrameLayout的子類暮顺,在generateDecor()中,通過(guò)new關(guān)鍵字來(lái)實(shí)例化的秀存。
3.注釋3處的方法我們都不陌生捶码,將mContentParent作為ViewGroup,從xml文件中創(chuàng)建布局或链,mContentParent就是我們布局文件的父布局了惫恼。

繼續(xù)看看generateLayout()方法

    //PhoneWindow.java
    protected ViewGroup generateLayout(DecorView decor) {
        ...
        int layoutResource;
        int features = getLocalFeatures();
        // System.out.println("Features: 0x" + Integer.toHexString(features));
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            layoutResource = R.layout.screen_swipe_dismiss;
            setCloseOnSwipeEnabled(true);
            ...        
        } else {
            // Embedded, so no decoration is needed.
            layoutResource = R.layout.screen_simple;//1
            // System.out.println("Simple!");
        }

        mDecor.startChanging();
        ...
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);//2
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }
        
        // Remaining setup -- of background and title -- that only applies
        // to top-level windows.
        if (getContainer() == null) {//3
            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;
    }

    //DecorView.java
    void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
        if (mBackdropFrameRenderer != null) {
            loadBackgroundDrawablesIfNeeded();
            mBackdropFrameRenderer.onResourcesLoaded(
                    this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
                    mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
                    getCurrentColor(mNavigationColorViewState));
        }

        mDecorCaptionView = createDecorCaptionView(inflater);
        final View root = inflater.inflate(layoutResource, null);
        if (mDecorCaptionView != null) {
            if (mDecorCaptionView.getParent() == null) {
                addView(mDecorCaptionView,
                        new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
            }
            mDecorCaptionView.addView(root,
                    new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
        } else {

            // Put it below the color views.
            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
        mContentRoot = (ViewGroup) root;
        initializeElevation();
    }

    //screen_simple.xml
    <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>

1.PhoneWindow會(huì)根據(jù)features中的值,假如有設(shè)置澳盐,會(huì)給對(duì)應(yīng)的布局祈纯,實(shí)現(xiàn)相關(guān)的特性,例如第一個(gè)是用來(lái)實(shí)現(xiàn)側(cè)滑關(guān)閉當(dāng)前Activity的功能叼耙。如果都沒(méi)設(shè)置的話腕窥,會(huì)給screen_simple.xml,布局代碼在下方筛婉。
2.DecorView通過(guò)onResourcesLoaded方法簇爆,將給定的布局添加到DecorView中。其中id為content就是contentParent爽撒。我們寫(xiě)的頁(yè)面布局都將加入到這個(gè)contentParent中入蛆。
3.window可以有container,這個(gè)container也是個(gè)window硕勿。只有最頂層的window才會(huì)設(shè)置背景和標(biāo)題哨毁。

小結(jié):

每一個(gè)Activity都有個(gè)PhoneWindow對(duì)象,并且這個(gè)PhoneWindow對(duì)象有個(gè)DecorView視圖對(duì)象源武,Activity的視圖都掛在DecorView的mContentParent節(jié)點(diǎn)下扼褪。那么視圖是如何更新的呢,我們繼續(xù)看粱栖。

3.頁(yè)面更新流程

在ActivityThread內(nèi)部類H的handleMessage方法中迎捺,在EXECUTE_TRANSACTION這個(gè)分支中,對(duì)應(yīng)的Activity的生命周期查排,在處理ResumeActivityItem的execute方法時(shí)凳枝,會(huì)調(diào)用ActivityThread的handleResumeActivity方法。我們看看該方法的實(shí)現(xiàn)

    //ActivityThread.java
    @Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
        ...
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
        ...
        final Activity a = r.activity;
        ...
        boolean willBeVisible = !a.mStartedActivity;
        if (!willBeVisible) {
            try {
                willBeVisible = ActivityManager.getService().willActivityBeVisible(
                        a.getActivityToken());
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
        if (r.window == null && !a.mFinished && willBeVisible) {//1
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            ...
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);//2
                } else {
                    // The activity will get a callback for this {@link LayoutParams} change
                    // earlier. However, at that time the decor will not be set (this is set
                    // in this method), so no action will be taken. This call ensures the
                    // callback occurs with the decor set.
                    a.onWindowAttributesChanged(l);
                }
            }

            // If the window has already been added, but during resume
            // we started another activity, then don't yet make the
            // window visible.
        } else if (!willBeVisible) {
            if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
            r.hideForNow = true;
        }
        ...
        if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
            if (r.newConfig != null) {
                performConfigurationChangedForActivity(r, r.newConfig);
                if (DEBUG_CONFIGURATION) {
                    Slog.v(TAG, "Resuming activity " + r.activityInfo.name + " with newConfig "
                            + r.activity.mCurrentConfig);
                }
                r.newConfig = null;
            }
            WindowManager.LayoutParams l = r.window.getAttributes();
            if ((l.softInputMode
                    & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
                    != forwardBit) {
                l.softInputMode = (l.softInputMode
                        & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
                        | forwardBit;
                if (r.activity.mVisibleFromClient) {
                    ViewManager wm = a.getWindowManager();
                    View decor = r.window.getDecorView();
                    wm.updateViewLayout(decor, l);//3
                }
            }

            r.activity.mVisibleFromServer = true;
            mNumVisibleActivities++;
            if (r.activity.mVisibleFromClient) {
                r.activity.makeVisible();
            }
        }
        ...
    }

1.ActivityClientRecord r跋核,這是個(gè)記錄Activity相關(guān)信息的對(duì)象岖瑰,該對(duì)象在Activity對(duì)象實(shí)例化之前被創(chuàng)建出來(lái),并且該類的參數(shù)用來(lái)實(shí)例化Activity砂代√6可以說(shuō)先有的r,然后activity被創(chuàng)建出來(lái)刻伊,然后activity在attach的時(shí)候擁有了window露戒。然后這時(shí)候在這里椒功,r還有沒(méi)window,所以會(huì)從注釋1進(jìn)去智什,賦值window成員變量动漾。因?yàn)槭堑谝淮危栽谧⑨?處荠锭,調(diào)用wm.addView(decor,l)旱眯。隨后會(huì)調(diào)用注釋3處的wm.updateViewLayout(decor, l)
2.wm是一個(gè)WindowManagerImpl實(shí)例(在Window類里面賦值),addView和updateViewLayout兩個(gè)方法在WindowManagerImpl中是交給WindowManagerGlobal這個(gè)單例處理证九。在WindowManagerGlobal中删豺,addView時(shí),創(chuàng)建一個(gè)與根view相對(duì)應(yīng)的ViewRootImpl愧怜。
3.在updateViewLayout時(shí)呀页,ViewRootImpl會(huì)調(diào)用setLayoutParams,調(diào)用scheduleTraversals拥坛,借助于mChoreographer去執(zhí)行doTraversal()方法赔桌,該方法會(huì)執(zhí)行view的measure,layout渴逻,draw方法疾党。使用mThreadedRenderer渲染,使用mWindowSession與WindowManagerService通信惨奕。

參考
Android源碼分析-Activity的啟動(dòng)過(guò)程
Android視圖框架Activity,Window,View,ViewRootImpl理解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末雪位,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子梨撞,更是在濱河造成了極大的恐慌雹洗,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卧波,死亡現(xiàn)場(chǎng)離奇詭異时肿,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)港粱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門螃成,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人查坪,你說(shuō)我怎么就攤上這事寸宏。” “怎么了偿曙?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵氮凝,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我望忆,道長(zhǎng)罩阵,這世上最難降的妖魔是什么竿秆? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮稿壁,結(jié)果婚禮上幽钢,老公的妹妹穿的比我還像新娘。我一直安慰自己常摧,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布威创。 她就那樣靜靜地躺著落午,像睡著了一般。 火紅的嫁衣襯著肌膚如雪肚豺。 梳的紋絲不亂的頭發(fā)上溃斋,一...
    開(kāi)封第一講書(shū)人閱讀 49,764評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音吸申,去河邊找鬼梗劫。 笑死,一個(gè)胖子當(dāng)著我的面吹牛截碴,可吹牛的內(nèi)容都是我干的梳侨。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼日丹,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼走哺!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起哲虾,我...
    開(kāi)封第一講書(shū)人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤丙躏,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后束凑,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體晒旅,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年汪诉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了废恋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡扒寄,死狀恐怖拴签,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情旗们,我是刑警寧澤蚓哩,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站上渴,受9級(jí)特大地震影響岸梨,放射性物質(zhì)發(fā)生泄漏喜颁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一曹阔、第九天 我趴在偏房一處隱蔽的房頂上張望半开。 院中可真熱鬧,春花似錦赃份、人聲如沸寂拆。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)纠永。三九已至,卻和暖如春谒拴,著一層夾襖步出監(jiān)牢的瞬間尝江,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工英上, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留炭序,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓苍日,卻偏偏與公主長(zhǎng)得像惭聂,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子相恃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

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