Android Window状土,View,Activity之間關(guān)系

在Android一個(gè)View是怎么展示在屏幕上斥季?過(guò)程是什么樣的呢累驮?現(xiàn)提出問(wèn)題再帶著問(wèn)題去查看源碼。
Android所有展示的頁(yè)面所有的View都被裝載在Window中躁锡,Window是一個(gè)抽象類稚铣,它的唯一實(shí)現(xiàn)類是PhoneWindow墅垮,而Activity中有一個(gè)window的對(duì)象這點(diǎn)從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, String referrer, IVoiceInteractor voiceInteractor,
            Window window) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window);  //注釋:1
        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;
        mEmbeddedID = id;
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        if (voiceInteractor != null) {
            if (lastNonConfigurationInstances != null) {
                mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
            } else {
                mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                        Looper.myLooper());
            }
        }

        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);//注釋:2
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;
    }

如果對(duì)attach()方法不是很了解可以查詢下Android Activity的啟動(dòng)流程
注釋1:new 了一個(gè)PhoneWindow的對(duì)象峡钓,這樣activity就擁有了window對(duì)象能岩,
但是這樣遠(yuǎn)遠(yuǎn)不夠?qū)iew展示出來(lái)萧福,因?yàn)檫@只是有了一個(gè)窗口真正的繪制工作并沒有開始。
再看注釋2:Activity中window調(diào)用了setWindowManager方法膏燕,WindowManager又是干什么用的呢?這里引用皇叔的博客中的一段話來(lái)下個(gè)定義:

WindowManager對(duì)Window進(jìn)行管理篷就,說(shuō)到管理那就離不開對(duì)Window的添加近忙、更新和刪除的操作及舍,在這里我們把它們統(tǒng)稱為Window的操作。對(duì)于Window的操作,最終都是交由WMS來(lái)進(jìn)行處理更振。

如果大家熟悉AMS的話對(duì)這種方式應(yīng)該不會(huì)陌生饭尝,它們其實(shí)相似。接著查看setWindowManager实撒,他是Window的一個(gè)方法涉瘾,

/**
     * Set the window manager for use by this Window to, for example,
     * display panels.  This is <em>not</em> used for displaying the
     * Window itself -- that must be done by the client.
     *
     * @param wm The window manager for adding new windows.
     */
    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);
        }//注釋3
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

看注釋3的位置立叛,先判斷傳入的WM是否為空秘蛇,如果為空則再次執(zhí)行(WindowManager)context.getSystemService(Context.WINDOW_SERVICE)
下面看getSystemService方法,這個(gè)方法是通過(guò)Binder機(jī)制來(lái)獲取WMS妖泄,再看getSystemService返回的對(duì)象是什么艘策?具體的實(shí)現(xiàn)是在ContextImpl中,

@Override
    public Object getSystemService(String name) {
        return SystemServiceRegistry.getSystemService(this, name);
    }

SystemServiceRegistry又是個(gè)什么東東呢审残?看一下:

static{
...
registerService(Context.WINDOW_SERVICE, WindowManager.class,
                new CachedServiceFetcher<WindowManager>() {
            @Override
            public WindowManager createService(ContextImpl ctx) {
                return new WindowManagerImpl(ctx);
            }});
...
}

SystemServiceRegistry 的靜態(tài)代碼塊中會(huì)調(diào)用多個(gè)registerService方法搅轿,這里只列舉了和本文有關(guān)的一個(gè)。registerService方法會(huì)將傳入的服務(wù)的名稱存入到SYSTEM_SERVICE_NAMES中既穆。從上面代碼可以看出雀鹃,傳入的Context.WINDOW_SERVICE對(duì)應(yīng)的就是WindowManagerImpl實(shí)例黎茎,因此得出結(jié)論,Context的getSystemService方法得到的是WindowManagerImpl實(shí)例踢代。
在返回到注釋3的位置的下一行代碼調(diào)用了WindowManagerImpl對(duì)象的createLocalWindowManager方法嗅骄,這個(gè)方法又是干嘛的呢?

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

這個(gè)方法很簡(jiǎn)單就是就是又一次創(chuàng)建了WindowManagerImpl對(duì)象慕爬,和通過(guò)之前那次不同的是這次傳入了PhoneWindow對(duì)象医窿,這時(shí)WindowManager開始真正和WIndow關(guān)聯(lián)起來(lái)炊林。
哪View是被怎么Add到Window上的呢?這里以最典型的應(yīng)用程序窗口Activity為例隔显,Activity在啟動(dòng)過(guò)程中饵逐,如果Activity所在的進(jìn)程不存在則會(huì)創(chuàng)建新的進(jìn)程倍权,創(chuàng)建新的進(jìn)程之后就會(huì)運(yùn)行代表主線程的實(shí)例ActivityThread捞烟,不了解的請(qǐng)查看Android應(yīng)用程序進(jìn)程啟動(dòng)過(guò)程(前篇)這篇文章题画。ActivityThread管理著當(dāng)前應(yīng)用程序進(jìn)程的線程德频,這在Activity的啟動(dòng)過(guò)程中運(yùn)用的很明顯,不了解的請(qǐng)查看Android深入四大組件(一)應(yīng)用程序啟動(dòng)過(guò)程(后篇)這篇文章竞思。當(dāng)界面要與用戶進(jìn)行交互時(shí)盖喷,會(huì)調(diào)用ActivityThread的handleResumeActivity方法难咕,如下所示。

...
if (r.window == null && !a.mFinished && willBeVisible) {
                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;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (r.mPreserveWindow) {
                    a.mWindowAdded = true;
                    r.mPreserveWindow = false;
                    // Normally the ViewRoot sets up callbacks with the Activity
                    // in addView->ViewRootImpl#setView. If we are instead reusing
                    // the decor view we have to notify the view root that the
                    // callbacks may have changed.
                    ViewRootImpl impl = decor.getViewRootImpl();
                    if (impl != null) {
                        impl.notifyChildRebuilt();
                    }
                }
                if (a.mVisibleFromClient && !a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);//注釋4
                }

            // 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;
            }
...

注意注釋4:調(diào)用了在注釋3位置返回的WindowManagerImpl對(duì)象的addview方法,這個(gè)方法是ViewManager中的方法沾歪,ViewManager是一個(gè)接口雾消,WindowManager是個(gè)接口同時(shí)又實(shí)現(xiàn)了ViewManager接口立润,而WindowManagerImpl又實(shí)現(xiàn)了WindowManager媳板,這是只捋清楚這個(gè)方法的層級(jí),清楚后放到一邊繼續(xù)查看WindowManagerImpl中的addview方法

@Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);//注釋5
    }

看注釋5:又調(diào)用了mGlobal的addview方法破讨,如下

    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
...
//熟悉的LayoutParams出來(lái)了
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
//根據(jù)父window調(diào)整當(dāng)前window的尺寸
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // If there's no parent, then hardware acceleration for this view is
            // set from the application's hardware acceleration setting.
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // Start watching for system property changes.
//查看系統(tǒng)屬性的更改

            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();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }
            int index = findViewLocked(view, false);
            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) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }
//添加View的所有準(zhǔn)備工作就緒開始添加
            root = new ViewRootImpl(view.getContext(), display);

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

注釋6:調(diào)用了ViewRootImpl中的setView方法:

        ...
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } catch (RemoteException e) {
                    mAdded = false;
                    mView = null;
                    mAttachInfo.mRootView = null;
                    mInputChannel = null;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    throw new RuntimeException("Adding window failed", e);
                } finally {
                    if (restore) {
                        attrs.restore();
                    }
                }
...

主要就是調(diào)用了mWindowSession的addToDisplay方法隙笆。mWindowSession是IWindowSession類型的,它是一個(gè)Binder對(duì)象瘸爽,用于進(jìn)行進(jìn)程間通信铅忿,IWindowSession是Client端的代理辆沦,它的Server端的實(shí)現(xiàn)為Session,此前包含ViewRootImpl在內(nèi)的代碼邏輯都是運(yùn)行在本地進(jìn)程的肢扯,而Session的addToDisplay方法則運(yùn)行在WMS所在的進(jìn)程蔚晨。

@Override
 public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
         int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
         Rect outOutsets, InputChannel outInputChannel) {
     return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
             outContentInsets, outStableInsets, outOutsets, outInputChannel);
 }

addToDisplay方法中會(huì)調(diào)用了WMS的addWindow方法,并將自身也就是Session银择,作為參數(shù)傳了進(jìn)去累舷,每個(gè)應(yīng)用程序進(jìn)程都會(huì)對(duì)應(yīng)一個(gè)Session被盈,WMS會(huì)用ArrayList來(lái)保存這些Session。這樣剩下的工作就交給WMS來(lái)處理袜瞬,在WMS中會(huì)為這個(gè)添加的窗口分配Surface身堡,并確定窗口顯示次序,可見負(fù)責(zé)顯示界面的是畫布Surface汞扎,而不是窗口本身擅这。WMS會(huì)將它所管理的Surface交由SurfaceFlinger處理,SurfaceFlinger會(huì)將這些Surface混合并繪制到屏幕上一忱。

結(jié)論:Activity持有Window的對(duì)象,View在Window上的增刪等操作又是通過(guò)WindowManager來(lái)管理的票渠,而WindowManager又是通過(guò)Binder機(jī)制獲取到的WMS的映射芬迄,WMS把View真正顯示到屏幕上禀梳。

部分源碼線上鏈接https://www.androidos.net.cn/android/8.0.0_r4/xref/frameworks/base/core/java/android/app/ContextImpl.java
https://www.androidos.net.cn/android/8.0.0_r4/xref/frameworks/base/core/java/android/app/SystemServiceRegistry.java

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末算途,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子扫外,更是在濱河造成了極大的恐慌廓脆,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異席赂,居然都是意外死亡氧枣,警方通過(guò)查閱死者的電腦和手機(jī)别垮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門碳想,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人逊移,你說(shuō)我怎么就攤上這事龙填」詹妫” “怎么了凤瘦?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵案铺,是天一觀的道長(zhǎng)控汉。 經(jīng)常有香客問(wèn)我,道長(zhǎng)乎婿,這世上最難降的妖魔是什么街佑? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任舆乔,我火速辦了婚禮,結(jié)果婚禮上吊宋,老公的妹妹穿的比我還像新娘颜武。我一直安慰自己,他們只是感情好这吻,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布唾糯。 她就那樣靜靜地躺著鬼贱,像睡著了一般这难。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上嵌溢,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音炸站,去河邊找鬼疚顷。 笑死,一個(gè)胖子當(dāng)著我的面吹牛阀坏,可吹牛的內(nèi)容都是我干的笆檀。 我是一名探鬼主播酗洒,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼樱衷,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了沸移?” 一聲冷哼從身側(cè)響起侄榴,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤癞蚕,失蹤者是張志新(化名)和其女友劉穎蕊爵,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體桦山,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡攒射,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了度苔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片匆篓。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡浑度,死狀恐怖寇窑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情箩张,我是刑警寧澤甩骏,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站饮笛,受9級(jí)特大地震影響咨察,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜福青,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一摄狱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧无午,春花似錦媒役、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至次泽,卻和暖如春穿仪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背意荤。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工啊片, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人玖像。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓钠龙,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親御铃。 傳聞我的和親對(duì)象是個(gè)殘疾皇子碴里,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348