`
Teok
  • 浏览: 147807 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

AndroidUI学习

阅读更多
没搞过swing,也没丰富经验的j2me ui,ios的ui也没折腾过。说实话UI这块看得很辛苦,4天里断断续续的看,至今也没从我想象的高度来认识Android UI的设计细节。

没看明白归没看明白,至少还是有一些问题的。老样子,仔细分析冷冰博文第十二和十三篇: Android GEWS之Android窗口管理之基本架构 和 Android GEWS窗口管理。

下面是我想到的几点:

1. 按照冷冰的分析,为什么在Activity和WindowManager之间会存在一个Session的概念?

先看一张图:

再看Wiki上对Session的解释:

In computer science, in particular networking, a session is a semi-permanent interactive information interchange, also known as a dialogue, a conversation or a meeting, between two or more communicating devices, or between a computer and user (see Login session). A session is set up or established at a certain point in time, and torn down at a later point in time. An established communication session may involve more than one message in each direction. A session is typically, but not always, stateful, meaning that at least one of the communicating parts needs to save information about the session history in order to be able to communicate, as opposed to stateless communication, where the communication consists of independent requests with responses.

所以Session是一种半永久性的互动信息交换(直译,以免翻译错误导致误解)。

再看冷冰的一段话:

Activity建立一个主窗口后,在将主窗口添加到WindowManager时,首先要建立WindowManager代理对象,并打开一个会话(实现IWindowSession AIDL接口),并维持该会话。Activity将通过该会话与WindowManager建立联系,这个Session是C/S体系的基础,Client通过WindowSession将window加入到Window Manager中。

由此看来,C/S结构中,C与S不是能直接通信的(至少在当前场景下),所以Session就像一个中间件,连接一个Activity与WindowManager,使两者可以进行通讯(互动信息的交换)。在J2EE中,我所见到的Login Session也差不多是这样,每个客户端(Browser)和服务器(Server,Server就像这里的WindowManager,单例的)都存在一个维护通信现场和提供通信结构的session,当客户端的浏览器退出或者网页被关闭或者用户主动logout,那么session manager会立即或在未来某个时间关掉这个这个session(可以依据的一些session过期特征,例如过期时间)。

为了证明Session的交换特性,我试图找到Android IWindowSession这个interface的源文件,可惜没找到,只能从class文件来大概了解一下:
// Method descriptor #10 (Landroid/view/IWindow;Landroid/view/WindowManager$LayoutParams;ILandroid/graphics/Rect;Landroid/view/InputChannel;)I

public abstract int add(android.view.IWindow arg0, android.view.WindowManager.LayoutParams arg1, int arg2, android.graphics.Rect arg3, android.view.InputChannel arg4) throws android.os.RemoteException;

  // Method descriptor #16 (Landroid/view/IWindow;)V

public abstract void remove(android.view.IWindow arg0) throws android.os.RemoteException;
单从这两个方法就可以看出,IWindowSession接口的确定义了一些交互性的方法(add、remove等)。从设计模式的角度来看,有点适配器(Adapter)的味道(Android中有很明显的Adapter,各种ListView都会用到:它完成了从数据到视图的一个适配)。从应用开发者的角度来看,Session是无需关心的。

补充或者更正:

刚看到IWindowSession实际上是一个AIDL定义的接口,那么就是说,它工作于在linux平台层,而且从
public static IWindowSession getWindowSession(Looper mainLooper) {
        synchronized (mStaticInit) {
            if (!mInitialized) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
                    sWindowSession = IWindowManager.Stub.asInterface(
                            ServiceManager.getService("window"))
                            .openSession(imm.getClient(), imm.getInputContext());
                    mInitialized = true;
                } catch (RemoteException e) {
                }
            }
            return sWindowSession;
        }
    }

这段代码来看,IWindowSession实际上又是单例的(这跟J2EE中的session和wiki中定义的有些不同)。如果是单例的话,那就意味着这个session是长久的,它至少在android linux启动时被生成,关闭时被销毁,它不会被某个与之相关的WindowManager或者ViewRoot的存亡影响,WM也是单例的,所以它与WM一对一,与VR一对多。因此,android的设计者引入这样一个叫做IWindowSession的东西,并不意味者它就是一个session,也许它只有一些特征相似而已(说实在的,这里比较复杂了,我的小脑瓜实在转不过来,等慢慢理解了再回过头来补充)。

2. View组织结构

UI界面通常是一个树形结构,Android也不例外。那么这棵树的根节点是谁呢?是ViewRoot,这是一个不公开的类。给出两个比较明显的是“根”的特征:

1.继承ViewParent接口

2. ViewParent getParent方法返回null

在ViewRoot之下,冷冰认为有一个DecorView,它在具体的Window中负责组织各个具体View和ViewGroup(就是View或者ViewGroup的实现者)。但是我没找到这个DecorView,但是我也认同这个DecorView的存在,因为两点:

1.ViewRoot实现ViewParent但不实现ViewGroup,所以它不能组织子View元素;

2.ViewRoot持有一个IWindowSession,而这个session正是Activity与外界(WindowManager)交互的桥梁

所以ViewRoot作为根节点它同时肩负着与外界通信的能力,至于组织View结构这类工作就托付给另外一个抽象(DecorView)去做吧。现在来看冷冰的这张图就不难理解了:

ViewRoot并不属于Window空间,因为它更倾向于是Activity对外的桥梁,包括Window也是通过调用ViewRoot的门面方法(方法的实现者其实是IWindowSession的实现者,这点代码中可以印证)而加入到WindowManager中的。

在看ViewParent接口时我注意到一个api:
/**
     * Find the nearest view in the specified direction that wants to take focus
     *
     * @param v The view that currently has focus
     * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
     */
    public View focusSearch(View v, int direction);

是的,在UI结构中,focus路径是要关心的一个问题。我忘记了fclient中如何设计解决这个问题的(代码如果能找到的话再补充),在Android中下面的一张图就表述的很清晰:

要找到View22,首先从根部遍历(当然这个根部应该是DecorView,它本身就是一个ViewGroup),找到那个存在focused view的ViewGroup11,然后再在ViewGroup11中找到mFocused(源码中该私有变量的说明原文是这样的:The view contained within this ViewGroup that has or contains focus.) 。

3.事件分发

其实ViewRoot也是一个Handler,它处理或者分发了来自那边(WindowManager)的消息(包括焦点切换、key和pointer事件、die、checkfocus等等很多)。消息在handleMessage内经过switch case语句按照分支进行分发之后,由具体的处理方法去完成剩下的工作。我这里只举一个例子(key event的处理)来帮助自己对这个流程的理解。先看一段代码:
private void deliverKeyEvent(KeyEvent event, boolean sendDone) {
        // If mView is null, we just consume the key event because it doesn't
        // make sense to do anything else with it.
        boolean handled = mView != null
                ? mView.dispatchKeyEventPreIme(event) : true;
        if (handled) {
            if (sendDone) {
                finishInputEvent();
            }
            return;
        }
        // If it is possible for this window to interact with the input
        // method window, then we want to first dispatch our key events
        // to the input method.
        if (mLastWasImTarget) {
            InputMethodManager imm = InputMethodManager.peekInstance();
            if (imm != null && mView != null) {
                int seq = enqueuePendingEvent(event, sendDone);
                if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
                        + seq + " event=" + event);
                imm.dispatchKeyEvent(mView.getContext(), seq, event,
                        mInputMethodCallback);
                return;
            }
        }
        deliverKeyEventToViewHierarchy(event, sendDone);
    }

要被处理的消息被包装成KeyEvent并传递进来,首先一个mView会试图处理这个事件,mView又是什么呢,在给mView赋值的setView方法是这么说的:We have one child.那么,这个child就可能就是前面所指的DecorView(但是后来又发现好像不是),所以除过IME(输入法)的事件都给mView去处理(并且如果mView为null,那么该事件会被consume掉),它爱分发分发,爱处理处理,IME的事件呢,则在ViewRoot里面处理,就是第二个if块里面的代码,当然最后deliverKeyEventToViewHierarchy方法也只在处理IME事件时才生效。但实际上在IME之前处理键事件的情况很少,详见dispatchKeyEventPreIme方法的注释.

根据代码我大概得知mView是其实是与当前这个message有直接关系的那个View,所以mView的赋值,应该是由WindowManager这样的设施来负责,因为只有它们才能具体知道用户点击了哪个View。的下面的代码不分析了,很复杂也很多。。总体上感觉,Android事件处理的分发路径就像投石模型(把石头扔到水里,石头一直是下沉的,HTML的事件模型是冒泡),从根节点一直往下,不过这个只是感觉,并不准确(等我发现理解有误时再改正吧,主要是我发现为了举这个例子,我快掉进细节的泥沼了,得赶紧脱身才是,:))。

最后再来看一张图:

结合源码看这段,感觉精妙的很(冷冰大牛啊)。WindowManager、View、ViewRoot、WindowSession等的交互关系也是很准确,只是图略显凌乱,刚开始看不知所云,我也是结合代码看了好久才明白其含义。只是图中没有给清除左下角这些View到底是什么,要不我就比较确定的知道mView了。

4.在Android UI中有没有显示层这样的概念(就像html中的z-index),如果有,那它是怎么处理的?

有一个类似的概念叫做Z-order,在Window Service(注意:Android中I开头的接口通常是aidl接口,它们通常都继承自android.os.IInterface 接口,所以这里指的window service就是实现某个具体aidl接口的远程service),这个order也由该service来维护。这个我还没找到具体的代码来印证。
  • 大小: 12.1 KB
  • 大小: 26.3 KB
  • 大小: 36.6 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics