Android 中Activity,Window和View之間的關系(二)


轉自:http://hi.baidu.com/xiaofanqing/blog/item/9c4ef9116e5889dca6ef3f02.html


上篇講解了3個對象之間的關系和創建的時機。這篇講解窗口是如何被繪制出來的。

首先,我們看一個概念。就是View的draw方法的doc:

Manually render this view (and all of its children) to the given Canvas.

意思是說把View繪制在畫布上。個人覺得這個概念很重要,View和Canvas 的關系,按常規的思維,肯定認為View聚合了Canvas對象,然后在View的onDraw 方法中,在View中繪制圖形。實際上恰恰相反,Canvas 是由系統提供,view通過draw方法來把自身繪制在畫布上。如果這樣來理解的話,很多東西理解起來就很自然了。系統在window中提供一個Canvas對象,DocerView通過調用draw方法來將自己繪制到canvas上。draw方法實際上是一個遞歸方法,他會循環調用孩子View的draw方法來完成整棵樹的繪制。所以實際上一個界面的繪制所用的Cavans是同一個對象。Canvas內部聚合了Matrix對象來實現坐標系的變換。

這里將的是題外話,只是想讓大家理解一個東西。

下面回到系統如何來繪制一個窗口。

android 系統提供了WindowManager,WindowManager顧名思義窗口管理器。實際上它只是對WindowManager服務做了一個包裝。其內部實現通過ISessionWindow和IWindow接口來和WindowManager服務來通信,這里設計到IPC的概念。IPC即進程間的通訊,ANDROID通過IBinder接口來實現,IBinder通過transact方法來實現進程間的交互,這是一個使用很不友好的接口,好在android還提供了aidl的更人性化的接口,它使得IPC通信就像調用普通的JAVA方法那樣便捷。不了解aidl的同學可以自行研究研究(其實我自己也是半桶水,了解概念,而用的不熟悉)

我來看Window是如何被添加到WindowManager的.

Activity有一個public的方法setVisible用來控制Activity的窗口是否顯示。

我們看其內部實現發現其調用了makeVisible方法,該方法就是讓Window顯示在屏幕當中的方法,實現如下:

    void makeVisible() {

        if (!mWindowAdded) {

            ViewManager wm = getWindowManager();

            wm.addView(mDecor, getWindow().getAttributes());

            mWindowAdded = true;

        }

        mDecor.setVisibility(View.VISIBLE);

    }

這時我們立馬就明白了,Activity通過Context來獲取WindowManager對象,然后把Window對象的DocerView添加到了WindowManager 服務中,所以android的窗口的創建和顯示並不是在同一個進程中,而是把窗口的繪制和管理交給了專門的WindowManager服務,這也是android framework給我提供的基礎服務。

在上面綠色的代碼當中,我們看看mDeocr是在哪被創建的。

    public void onWindowAttributesChanged(WindowManager.LayoutParams params) {

      // Update window manager if: we have a view, that view is

       // attached to its parent (which will be a RootView), and

       // this activity is not embedded.

        if (mParent == null) {

            View decor = mDecor;

            if (decor != null && decor.getParent() != null) {

                getWindowManager().updateViewLayout(decor, params);

            }

        }

    }

搜索代碼發現其創建的地方如上面紅色代碼。

這時我們已經知道,Activity創建好Window之后只要調用WindowManager 的addView方法來將Window的DocerView添加進去即可是使Window顯示出來。還方法Window其實是Activity中的概念,在WindowManager中是直接和View打交道的。

下面我們開始研究WindowManager對象,打開其源代碼,發現它是一個接口,且只是簡單的擴展了ViewManager接口.並增加了一個方法

getDefaultDisplay():Display.  內部還有一個繼承自ViewGroup.LayoutParam的內部類。

我們在framework/base/core/java/android/view/WindowManagerImpl.java 中找到其實現類。

我們找到其核心的實現方法addView 方法,如下:

   

    private void addView(View view, ViewGroup.LayoutParams params, boolean nest)

    {

        if (Config.LOGV) Log.v("WindowManager""addView view=" + view);

        if (!(params instanceof WindowManager.LayoutParams)) {

            throw new IllegalArgumentException(

                    "Params must be WindowManager.LayoutParams");

        }

        final WindowManager.LayoutParams wparams

                = (WindowManager.LayoutParams)params;

        

        ViewRoot root;

        View panelParentView = null;

        

        synchronized (this) {

            // Here's an odd/questionable case: if someone tries to add a

            // view multiple times, then we simply bump up a nesting count

            // and they need to remove the view the corresponding number of

            // times to have it actually removed from the window manager.

            // This is useful specifically for the notification manager,

            // which can continually add/remove the same view as a

            // notification gets updated.

            int index = findViewLocked(view, false);

            if (index >= 0) {

                if (!nest) {

                    throw new IllegalStateException("View " + view

                            + " has already been added to the window manager.");

                }

                root = mRoots[index];

                root.mAddNesting++;

                // Update layout parameters.

                view.setLayoutParams(wparams);

                root.setLayoutParams(wparams, true);

                return;

            }

            

            // 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 != null ? mViews.length : 0;

                for (int i=0; i<count; i++) {

                    if (mRoots[i].mWindow.asBinder() == wparams.token) {

                        panelParentView = mViews[i];

                    }

                }

            }

            

           root =newViewRoot(view.getContext());

            root.mAddNesting = 1;

            view.setLayoutParams(wparams);

            

            if (mViews == null) {

                index = 1;

                mViews = new View[1];

                mRoots = new ViewRoot[1];

                mParams = new WindowManager.LayoutParams[1];

            } else {

                index = mViews.length + 1;

                Object[] old = mViews;

                mViews = new View[index];

                System.arraycopy(old, 0, mViews, 0, index-1);

                old = mRoots;

                mRoots = new ViewRoot[index];

                System.arraycopy(old, 0, mRoots, 0, index-1);

                old = mParams;

                mParams = new WindowManager.LayoutParams[index];

                System.arraycopy(old, 0, mParams, 0, index-1);

            }

            index--;

            mViews[index] = view;

            mRoots[index] = root;

            mParams[index] = wparams;

        }

        // do this last because it fires off messages to start doing things

       root.setView(view, wparams, panelParentView);

    }

我們看看我標記未紅色的兩行代碼  root =newViewRoot(view.getContext());和 root.setView(view, wparams, panelParentView);

創建了一個ViewRoot對象,然后把view添加到ViewRoot中。

ViewRoot對象是handler的一個實現,其聚合了ISessionWindow和IWindow對象來和WindowManager服務進行IPC通信。

今天就寫到這里了, 本來想畫類圖和交互圖的,哎呀只是mac電腦不給力,就沒畫了。

希望有朋友能多多交流,文筆不好,像記流水賬。看的同學 需結合源代碼來看^_^

原創文章,轉載注明謝謝。



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM