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电脑不给力,就没画了。

希望有朋友能多多交流,文笔不好,像记流水账。看的同学 需结合源代码来看^_^

原创文章,转载注明谢谢。


posted @ 2012-05-17 09:29  移动应用开发  阅读(3473)  评论(0编辑  收藏  举报