现在的位置: 首页 > 综合 > 正文

android窗体加载过程剖析之消息处理的注册机制

2013年08月27日 ⁄ 综合 ⁄ 共 3852字 ⁄ 字号 评论关闭

这一篇是接着上一篇android窗体加载过程剖析之一Activity的初始化
的内容继续往下走。 在查阅了网上很多文章和源码之后,终于对android这一块的内容有了一定的了解。网上相似内容的文章似乎已经有蛮多篇了,不过我想在写文章的同时也可以进一步加深自己的理解,所以就再自行梳理了一遍这块内容。

由上一篇文章我们知道,Window类中存放的WindowManager实际上是一个WindowManagerImpl对象(LocalWindowManager相当于一个包装器):

源码路径:frameworks\base\core\java\android\view\Window.java
源码中国链接:http://www.oschina.net/code/explore/android-2.2-froyo/android/view/Window.java

    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,  
            boolean hardwareAccelerated) {  
        mAppToken = appToken;  
        mAppName = appName;  
        if (wm == null) {  
            wm = WindowManagerImpl.getDefault();  
        }  
        mWindowManager = new LocalWindowManager(wm, hardwareAccelerated);  
    }  

那么接下来就看看WindowManagerImpl的代码:
源码路径:frameworks\base\core\java\android\view\WindowManagerImpl.java
源码中国链接:http://www.oschina.net/code/explore/android-2.2-froyo/android/view/WindowManagerImpl.java

    private void addView(View view, ViewGroup.LayoutParams params,
            CompatibilityInfoHolder cih, boolean nest) {
        if (false) 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;
        
        ViewRootImpl root;
        View panelParentView = null;
        
        synchronized (this) {
            ...
            
            root = new ViewRootImpl(view.getContext());
            root.mAddNesting = 1;
            if (cih == null) {
                root.mCompatibilityInfo = new CompatibilityInfoHolder();
            } else {
                root.mCompatibilityInfo = cih;
            }

            view.setLayoutParams(wparams);
            
            if (mViews == null) {
                index = 1;
                mViews = new View[1];
                mRoots = new ViewRootImpl[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 ViewRootImpl[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);
    }

我们就从第一次启动Activity添加窗体的情况来说明,由于函数有点长,因此省略掉了这种情况下不会进入的代码。
int index = findViewLocked(view, false);
findViewLocked的内容:

    private int findViewLocked(View view, boolean required) {
        synchronized (this) {
            final int count = mViews != null ? mViews.length : 0;
            for (int i=0; i<count; i++) {
                if (mViews[i] == view) {
                    return i;
                }
            }
            if (required) {
                throw new IllegalArgumentException(
                        "View not attached to window manager");
            }
            return -1;
        }
    }

是在当前的窗体栈中寻找指定的View,有的话就返回在数组中的索引。由于假定的是第一次加载,那么就是返回-1.

            root = new ViewRootImpl(view.getContext());
            root.mAddNesting = 1;

接下来可以看到root被初始化,由于我的源码是4.0版本的,因此实例化的对象是ViewRootImpl,4,0以下是ViewRoot对象。等下再来分析下ViewRootImpl这个类,先继续往下走。

            if (mViews == null) {
                index = 1;
                mViews = new View[1];
                mRoots = new ViewRootImpl[1];
                mParams = new WindowManager.LayoutParams[1];
            } 

新启动的窗体的根View和ViewRootImpl都被添加到了类的成员变量中,WindowManagerImpl会负责维护这些窗体,并控制事件的分发。

root.setView(view, wparams, panelParentView);

函数的最后,调用了ViewRootImpl.setView函数。

源码路径:frameworks\base\core\java\android\view\ViewRootImpl.java
源码中国链接:http://www.oschina.net/code/explore/android-2.2-froyo/android/view/ViewRoot.java

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                mFallbackEventHandler.setView(view);
                mWindowAttributes.copyFrom(attrs);
                attrs = mWindowAttributes;
                ...
                if (mInputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue(mInputChannel);
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    } else {
                        InputQueue.registerInputChannel(mInputChannel, mInputHandler,
                                Looper.myQueue());
                    }
                }
                ...
}

只贴我关心的而且又是看懂了的部分,嘿嘿。
这里将传入的窗体的根view存入了类变量mView中。并且在InputQueue中注册了消息接收通道以及接收消息并处理的对象。当有按键,滚动等事件发生时,就是通过这个渠道从底层通知到了Java层。

InputQueue

源码地址 http://www.oschina.net/code/explore/android-4.0.1/core/java/android/view/InputQueue.java

An input queue provides a mechanism for an application to receive incoming input events. Currently only usable from native code.
一个给应用提供了某种机制来接收输入事件的输入队列。这个类我只看了下,没有深究,里面有一些Native的函数,这个类相当于是一个桥梁供Java层调用Native层的方法。

抱歉!评论已关闭.