导读:对于Android开发者来说,成系列的技术文章对他们的技术成长帮助最大。如下是我们向您强烈推荐的主题为Android开发的第一个系列文章。
《Android核心分析》整理如下:
12.Android核心分析之Android GEWS窗口管理基本架构篇
Android的窗口管理是C/S模式的。Android中的Window是表示Top
Level等顶级窗口的概念。DecorView是Window的Top-Level View,这个View我称之为主View,DecorView会缺省的attach到Activity的主窗口中。主View被加入到WindowManager中,WM使用WindowState与这个主View对应。
Activity建立一个主窗口后,在将主窗口添加到WindowManager时,首先要建立WindowManager代理对象,并打开一个会话(实现IWindowSession
AIDL接口),并维持该会话。Activity将通过该会话与WindowManager建立联系,这个Session是C/S体系的基础,Client通过WindowSession将window加入到Window
Manager中。一个完整的窗口概念横跨了View,ViewRoot,WindowManager Service。Window,DecorView,View,IWindow
,ISession,WindowState之间的关系如下:
客户端的Activity通过Session会话与WindowManager建立对话,而WindowManager则通过IWindow接口访问Client,将消息传递到Client端,通过消息分发渠道,将消息传递到处理函数OnXXX。
后面我们将通过Client,WM Service分别加以分析。
13.Android GWES窗口管理详解
Android的窗口管理是C/S模式的。Android中的Window是表示Top
Level等顶级窗口的概念。DecorView是Window的Top-Level View,这个View我称之为主View,DecorView会缺省的attach到Activity的主窗口中。主View被加入到WindowManager中,WM使用WindowState与这个主View对应。
Activity建立一个主窗口后,在将主窗口添加到WindowManager时,首先要建立WindowManager代理对象,并打开一个会话(实现IWindowSession
AIDL接口),并维持该会话。Activity将通过该会话与WindowManager建立联系,这个Session是C/S体系的基础,Client通过WindowSession将window加入到Window
Manager中。
一个完整的窗口概念横跨了View,ViewRoot,WindowManager
Service。Window,DecorView,View,IWindow ,ISession,WindowState之间的关系如下
Client端的Activity通过Session会话与WindowManager建立对话,而WindowManager则通过IWindow接口访问Client,将消息传递到Client端,通过消息分发渠道,将消息传递到处理函数OnXXX。
后面我们将通过Client,WM Service分别加以分析。
2 Client端
我一致认为在Android中Window的概念并不是个很重要的概念。他的Window类,只是在PhoneWindow和MidWindow中使用。而PhoneWindow只是做了一个具体跟手机功能相关的公用事件的处理,所以在Android中PhoneWindow并不是一个抽象的纯正概念,而是一个跟手机系统相关的一个特别窗口概念,例如按键的默认动作处理,按键音的发出等等。
2.1 View
在Activity中真正重要的概念是View,以下是Google官方对View的定义:
This class represents the basic building
block for user interface components. A View occupies
a rectangular area on the screen and is responsible
for drawing and event handling. View is the base class
for <em>widgets</em>, which are used to
create interactive UI components (buttons, text fields,
etc.). The {@link android.view.ViewGroup} subclass is
the base class for <em>layouts</em>, which
are invisible containers that hold other Views (or other
ViewGroups) and define their layout properties.
我对View不做翻译,翻译成视图好像不太佳,View在Android中,View比视图具有广的外延。View包含了用户交互,包含了显示,视图在中文中仅仅表示了静态的显示。对于View的理解应该从最容易的理解开始。我们使用过编辑器,在Android中这个编辑器就是一个View,这个编辑器需要显示文字,需要接收用户的键盘输入和鼠标选择,但是一个屏幕上有多个编辑器,如何管理,如何切换焦点编辑器,这些都是需要管理的。
客户端的组成:(Window,View,ViewRoot,WindowManager
Proxy)
在Activity在performLaunchActivity时,会使用Activity.attach()建立一个PhoneWindow主窗口。这个主窗口的建立并不是一个重点。handleResumeActivity真正要启动一个Activity时候,将主窗口加入到WindowManager,当然并不是将主窗口本身,而是将主窗口的DecorView加入到WindowManager中。
真正Window核心的抽象概念存在于View,ViewRoot,WindowManger中的WindowState。为了描述概念的方便性,我特别提出主View这个概念,这个主View就是Top-Level
View of the window. 主View与View想对,突出主View是attatch到主窗口上的。而一般的View则是存在于主View中的。主窗口这个概念,我讲的主窗口实际上就是Android提到的Top
Level Window。
我们所提到的概念:View,GroupView,DecorView,ViewRoot都是存在于Client端,只有WindowState这个概念存在于Window
Manager Service端。
DecorView实际上是一个ViewGroup。在依存关系上来讲,对看个主窗口来讲,DecorView是Top-Level
View.View并不是关注的重点,重要的是我们如何需要知道分发路径是建立在什么关系上的。View的成员变量mParent用来管理View上级关系的。而ViewGroup顾名思义就是一组View的管理,于是在ViewGroup构建了焦点管理和子View节点数组。这样通过View的mParent和ViewGroup的mChildren构建了Android中View直接的关系网。
2.2 Focus Path
所谓的Foucs Path就是我们的KeyEvent传递的路线。一般的我们的KeyEvent在主循环中主View通过View的焦点记录关系传递到焦点View上。例如下图,View22是焦点,我们从最顶层的View通过mFcous的关系链找到最后所形成的路径就是Focus
Path。
2.3 ViewRoot,Window Manager Proxy
ViewRoot与Window Manager的核心是IWindowSession和IWindow。ViewRoot通过IWindowSession添加窗口到Window
Manager。而IWindow这是Window Manager分发消息给Client ViewRoot的渠道。利用AIDL接口进行进程间通信。
ViewRoot实际是一个Handler,ViewRoot建立主View与WindowsManger通讯的桥梁。ViewRoot在本质上一个Handler。我们知道Handler的基本功能就是处理回调,发送消息。
Activity在使用getSystemService获取WindowManagerImpl
,建立了一个WindowManagerImpl实例,即Window Manager服务的代理:
wm=(WindowManagerImpl)context.getSystemService(Context.WINDOW_SERVICE);并调用wm.addview添加窗口到WMService中。
这个过程在客户端建立了什么样的管理框架,并如何这个会话?在Window
Manager Proxy中建立了View,Layout ,ViewRoot三者的对应关系表。构造一个ViewRoot就会打开一个session,并利用IWindowSession建立会话上下文。
4 Window Manager Service
本次对于Window Manager Service的研究仅限于FocusWindow,消息系统。其他的部分将在后面的专门章节讨论。
Window Manager管理的窗口是应用程序的Top-level窗口,我这里参照Window的概念叫主窗口。主窗口为什么要放在在Service这边来管理呢?为什么不放在Client那边?主窗口放置在一起管理是为了计算Z-order序列,根据应用程序的状态来显隐应用程序的窗口。我想Android设计者在考虑设计窗口系统的时候,一定首先考虑:
窗口z-order序的管理
活动窗口的计算,及其变化通知
窗口归属(属于哪个应用)
输入法管理
Window Service大体上实现了如下的功能:,
(1)Z-ordered的维护函数
(2)输入法管理
(3)AddWindow/RemoveWindow
(4)Layerout
(5)Token管理,AppToken
(6)活动窗口管理(FocusWindow)
(7)活动应用管理(FocusAPP)
(8)转场动画
(9)系统消息收集线程
(11)系统消息分发线程
在服务端的窗口对象叫做WindowState。在Service维护了一个mWindow数组,这个mWindow就是Window的Z-order序数组。mWindowMap用于记录<Client:Binder,WindowState对象>。
WindowState有一个叫做mClient成员变量来记录客户端IWindow实例,通过IWindow接口实例,Service可以访问客户端的信息,说以IWindow是Service连接View桥梁。
(1) FocusWindow活动窗口如何计算?
基本原理就是查找前景应用(FousActivity),并同Z-Order序中找出属于该FousActivity(AppToken)的主窗口,这个窗口就是计算出来的Focus
Window。
(2)为什么要提出Token这个概念呢?
一个应用程序要管理自己的窗口,那么如何来标识该窗口是属于某个Activity,Andoid设计者提出了AppToken这个概念。AppToken在本质上的描述:<Token:IBinder,allWindows>,通过Token找到属于该Token的allWindows。使用Token开始完成该应用程序的所有窗口的显示和隐藏。
(3)系统消息收集与处理
我们下面重点研究Service中的系统消息收集模式及其分发模式。Service使用KeyQ作为专门的消息队列。
KeyEvent
TouchEvent
TrackballEvent
系统有两个线程:
KeyQ线程,通过Navite函数readEvent轮询设备,将读取的结果放置在KeyQ队列中。
系统dispatcher 等待在KeyQ消息队列上,一旦从消息队列中获取到消息,就通过分发函数通过mClient传递到Client端。
14.Android GWES输入系统篇
依照惯例,在研究Android输入系统之前给出输入系统的本质描述:从哲学的观点来看,输入系统就是解决从哪里来又将到哪里去问题。输入的本质上的工作就是收集用户输入信息并放置到目标位置。
Android在源代码分类上,并没有输入系统分类。本章的输入系统研究是一个综合的分析,前面的GWES的分析,特别是View的Focus
Path以及Window Manager Proxy是本章分析的基础,如果没有理解,请参阅前面的窗口管理的相关章节。
Android输入系统的组成
输入系统由如下几部分组成:
1)后台窗口管理服务
2)Focus Activity
3)Focus Window
4)Focus View:用来接收键盘消息
从输入系统这个角度去看Android的Window Manager服务解决了用户信息输入收集,而FocusActvitiy,Focus
Window、Focus View这些概念的设计是为了解决用户输入应该放到哪里去这个问题。在整个Android系统中,同时只有一个一个Focus
Window,而属于该Window的Focus View才是真正的Focus View。
在Android系统中,在设计上要求多个Actvitiy同时存在运行。在实现中,每次把Actvitiy变成Focused
Actvitiy时(setFocusedActivity@ActivityManagerService.java)激活程序的时候,就把该Activity的主窗口设置成前景窗口,即系统中的顶层窗口,AppToken概念的引进就是为了解决窗口对象的归属问题。在这个过程中,在逻辑上看,我们挑选了一个Activity作为了Focus
Activity来接收系统的消息,实质上这个Focus Activity的Focus窗口就是前景窗口。
Focus窗口的改变将改变焦点View,前景窗口的改变也将引起焦点View的变化。焦点和光标的概念用于管理输入设备和输入事件的传送。光标是一个绘制在屏幕之上的小位图,指示当前的输入位置。键盘输入有类似的输入焦点和键盘输入插入符的概念。只有具有输入焦点的窗口才能获取键盘事件。改变窗口的焦点通常由特殊的按键组合或者TouchEvent事件完成。具有输入焦点的窗口通常绘制有一个键盘插入符。该插入符的存在、形式、位置,以及该插入符的控制完全是由窗口的事件处理例程完成的。
现在站在更宏观的位置来看Actvitiy的输入系统,可以从Linux
Driver开始到输入框结束的整个链条,我这里给出大输入系统的概念,Android大输入系统包含:Linux
driver, Window Manager, Message System, View Focus Path,Focus
View。
Android输入系统架构图
现在从Android的代码分析的角度,来看看输入系统的组成。这个过程从代码中分析处理:
在Window Manager Service端
readEvent@com_android_server_KeyInputQueue.cpp
KeyQ@WindowMangerService.java
KeyInputQ@KeyInputeQueue.java
InputDispatcherThread@WindowMangerService.java
在Client端
IWindow@ViewRoot.Java
ViewRoot@ViewRoot.java
KeyInputQ在WindowMangerService中建立一个独立的线程InputDeviceReader,使用Native函数readEvent来读取Linux
Driver的数据构建RawEvent,并放入到KeyQ消息队列中。
InputDispatcherThread从KeyQ中读取Events,找到Window
Manager中的Focus Window,通过Focus Window记录的mClient接口,将Events专递到Client端。Client端在根据自己的Focus
Path传递事件,直到事件被处理。
15.Android GWES输入系统之输入路径详解
1 输入路径的一般原理
按键,鼠标消息从收集到最终将发送到焦点窗口,要经历怎样的路径,是Android
GWES设计方案中需要详细考虑的问题。按键,鼠标等用户消息消息的处理可分为不同的情况进行判定:
(1)用户输入根据系统状况是否应该派送。如在ScreenOff的情况下,在按键属于特殊按键的情况下等
(2)是否有拦截Listener
(3)对按键事件来讲,是否存在输入法
(4)是否是焦点终点
(5)是否为焦点切换按相关键
这些情况都是设计输入路径需要考虑的基本条件。
1.1一般的输入路径设计
该输入路径实际上是指的按键消息(MSG_KEYDOWN,MSG_KEYUP,
MSG_LongPress)的输入路径,即从活动主窗口到焦点窗口所经历的路程。
将信息输入路径分为两步:
Step 1)窗口管理器将信息发送到活动窗口
Step 2)活动窗口通过缺省处理函数将该消息一层层的传递到焦点。
这样应用程序可以在活动View的处理函数中来预先处理用户输入信息,从而增强应用对用户信息的控制力。
传递路径是通过View的缺省处理函数Onxxx来完成。通过ActiveView
->focus->focus->focus的链条关系,一级一级的将按键消息MSG_KEYDOWN,MSG_KEYUP,
MSG_CHAR等传递到focus窗口。
此时用户按键输入先发送到输入法窗口,经过输入法管理器处理,过滤后将输入法产生的结果放置到焦点View。
1.3输入系统整体流程
下面示意图是Android输入系统的数据流途径,通过WM的输入系统线程收集消息,分发到Focus
Activity消息队列,然后通过消息系统派发。
2 Android输入路径详细描述
2.1 第一步:用户数据收集及其初步判定
KeyInputQ在WindowMangerService中建立一个独立的线程InputDeviceReader,使用Native函数readEvent来读取Linux
Driver的数据构建RawEvent,放入到KeyQ消息队列中。
preProcessEvent()@KeyInptQ@KeyInputQueue.java这个是在输入系统中的第一个拦截函数原型。KeyQ重载了preProcessEvent()@WindowManagerService.java。在该成员函数中进行如下动作:
(1) 根据PowerManager获取的Screen on,Screen
off状态来判定用户输入的是否WakeUPScreen。
(2) 如果按键式应用程序切换按键,则切换应用程序。
(3) 根据WindowManagerPolicy觉得该用户输入是否投递。
2.2 第二步 消息分发第一层面
InputDispatcherThread从KeyQ中读取Events,找到Window
Manager中的Focus Window,通过Focus Window记录的mClient接口,将Events专递到Client端。
如何将KeyEvent对象传到Client端:
在前面的章节(窗口管理ViewRoot,Window Manager Proxy)我们已经知道:在客户端建立Window
Manager Proxy后,添加窗口到Window Manager service时,带了一个跟客户ViewRoot相关的IWindow接口实例过去,记录在WindowState中的mClient成员变量中。通过IWindow这个AIDL接口实例,Service可以访问客户端的信息,IWindow是Service连接View桥梁。
看看在Client ViewRootKeyEvent的分发过程
IWindow:dispatchKey(event)
dispatchKey(event)@W@ViewRoot@ViewRoot.java
ViewRoot.dispatchKey(event)@ViewRoot.java
message>
sendMessageAtTime(msg)@Handler@Handler.java
至此我们通过前面的Looper,Handler详解章节的分析结论,我们可以知道Key
Message已经放入到应用程序的消息队列。
2.3第三步:应用消息队列分发
消息的分发,在Looper,Handler详解章节我们分析了Looper.loop()在最后后面调用了handleMesage.
…
ActivityThread.main()
Looper.loop()
ViewRoot$RootHandler().dispatch()
handleMessage
....
注意到在分发的调用msg.target.dispatch(),而这个target在第二层将消息sendMessageAtTime到消息队列时填入了mag.target=this即为msg.target=ViewRoot实例。所有此时handleMessage就是ViewRoot重载的handleMessage函数。
handlerMessage@ViewRoot@ViewRoot.java
deliverkeyEvent
如果输入法存在,dispatchKey到输入法服务。
否则deliverKeyEventToViewHierarchy@ViewRoot.java
在这里需要强调的是,输入法的KeyEvent的拦截并没有放入到Window
Manager Service中,而是放入到了客户端的RootView中来处理。
2.4第四步:向焦点进发,完成焦点路径的遍历。
分发函数调用栈
deliverKeyEventToViewHierarchy@ViewRoot.java
mView.dispatchKeyEvent:mView是与ViewRoot相对应的Top-Level
View.如果mView是一个ViewGroup则分发消息到他的mFocus。
mView.dispatchKeyEvent @ViewGroup (ViewRoot@root)
Event.dispatch
mFocus.dispatchKeyEevnet
如果此时的mFocu还是一个ViewGroup,这回将事件专递到下一层的焦点,直到mFocus为一个View。通过这轮调用,就遍历了焦点Path,至此,用户事件传递完成一个段落。
2.5第五步 缺省处理
如果事件在上述Focus View没有处理掉,并且为方向键之类的焦点转换相关按键,则转移焦点到下一个View。
16.Android电话系统-概述篇
首先抛开Android的一切概念来研究一下电话系统的最基本的描述。我们的手机首先用来打电话的,随后是需要一个电话本,随后是PIM,随后是网络应用,随后是云计算,随后是想我们的手机无所不能,替代PC。但是作为一个电话的基本功能如下:
0)拨叫电话,接听电话,挂断电话,发送短信,网络连接,PIM管理
1)由于电话运营商为我们提供了呼叫等待,电话会议等补充业务,所以我们的手机需要管理多路通话,如何管理?
2)来电时,我们要播出来电铃声,接通时我们需要切换语音通道,这个又跟多媒体系统打上了交道,例如有耳机插上了,有蓝牙耳机连上了,系统该做如何的管理和切换?
3)上网的网络通路建立(例如GSM GPRS),如何PPP连接并连接到LinuxSocket通道上的?系统如何管理数据连接?
4)AP跟Modem通讯时通过AT指令的,如何将AT指令变成一个个具体的操作函数,如何管理Modem发给我们的回应,AT命令通道,数据通道如何管理?
5)sim卡的电话本如何管理?
上面的关于手机的基本问题,Android电话系统设计者必须要解答的问题。该设计如何的管理框架,提出什么概念来表达?所以要分析Android的电话部分,还是需要理解电话实现的背景知识,通讯协议,大体框架。
我们回到电话系统基本构成上,先从整体上去把握一下电话模块的大体框架,先从空中俯瞰。我给出的图是一般的智能手机的框架图,该框架基本能够概括所有手机电话模块的构成,当然也包括Android的电话系统构成。
智能机架构一般是应用处理器+Modem。应用处理器与Modem的连接使用串口或者USB。在一个硬件串口通路上实现为了要同时实现数据传输并同时实现控制Modem,就需要实现多路复用协议(GSM
TS07.10),在底层我们在多路复用的基础上虚拟了两个串口,一个用于CMD通道,一个用于DATA通道。电话的所有控制通路都是在这连个通道上。
RIL,Radio Interface Layer。本层为一个协议转换层,手机框架需要适应多类型的Modem接入到系统中,而对于不同的Modem有不同的特性,AT指令的格式或者回应有所不同,但是这种特性在设计应用时不可能完全考虑和兼容。所以设计者在设计电话系统时,建立了一个虚拟电话系统,为该虚拟电话系统规定了标准的功能,上层的电话管理都是建立在这些标准的功能基础之上。而RIL则是将虚拟电话系统的标准功能转换成实际的所使用的Modem的AT指令。
Android设计者将电话系统设计成了三部分。
Andoird的Phone Service其实是PhoneApp。GSMPhone(CDMAPhone)是Phone
Service核心的对象,他包含了如下的相关对象。
我们的分析任务就是要把这些对象的相互关系,及其对象间数据传递关系弄清楚。首先我们给出以下的Android电话系统的框架,以便对Android电话系统有个概要的认识,然后从数据流的角度,以及对象的引用关系来分析系统。下面是android电话系统整体框架图。
|