第八章 Android GWES
8.1 View System
View & ViewGroup
Android的 UI系统是建立在View和ViewGroup之上的。View是组成UI基本部件,而ViewGroup可以把它看做Panel,就是容器。可以把 View和ViewGroup放到一个ViewGroup里布局,这些View和ViewGroup就组成了显示UI的树形结构(下图)。 Activity创建的Window中的主View既DecorView继承自FrameLayout,而FrameLayout又继承自 ViewGroup,这个DecorView就是每个Activity最最根部的那个ViewGroup。
View在Android中不单纯只是 负责显示功能,它还封装了UI Event的处理功能。在系统中所有要显示的UI元素都继承自View(Surface除外),用户可以使用Android中已实现的一些View的子 类,这些子类包括TextView,Button等,在这里叫他们widget,这些widget都继承自View,但都他们各自的UI表现形式,以及对 UI Event的不同处理方式。当然如果用户觉得这些widget无法满足的话,可以自定义View,既用户去实现一个继承了View的类,也可以继承 widget,并对View的相关成员函数进行重写,如OnDraw。
UI Event
View中以接口的形式定义一些类UI Event处理,其实接口里就是定义了一个回调函数,共使用者去实现,当有消息来时,就会调用到使用者实现的回调函数里。
参 考View.java public void setOnTouchListener(OnTouchListener l) { mOnTouchListener = l; } public interface OnTouchListener { boolean onTouch(View v, MotionEvent event); }
public boolean dispatchTouchEvent(MotionEvent event) { if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && mOnTouchListener.onTouch(this, event)) { return true; } return onTouchEvent(event); }
参考usr.java usrTouchView. setOnTouchListener(new OnTouchListener{ onTouch() { TODO:: } })
关于消息是如何传递到dispatchTouchEvent的,在输入消息处理那一块会有详细介绍。
Graphics
上 面介绍了View是如何处理UI Event,接下来会介绍View是如何显示出来,既画出来的。前面提到过View的OnDraw函数,可以说View的所有显示都在OnDraw里面, 每当View需要刷新时,都会调用的OnDraw,它的函数声明如下: protected void onDraw(Canvas canvas)
每 一个View的子类,包括widget都对它重写,这个函数的重点就是Canvas,所有显示的画图动作都是通过该类来实现的。它位于 android.Graphics的package内,Canvas提供一些类画图的方法,如drawPath(Path path, Paint paint)。使用Canvas画图还需要一些其他资源
Bitmap:用于承载pixel,既所画的内容
Path:Region提供画的区域
Paint:描述颜色及样式
参 考Canvas.java public void drawPath(Path path, Paint paint) { native_drawPath(mNativeCanvas, path.ni(), paint.mNativePaint); }; private static native void native_drawPath(int nativeCanvas, int path, int paint);
从上面的代码片段了解到Canvs的这些画图方法,最后都是调用相对应的native API。这些native API到底层的调用流程是Skia/OpenGL====>SurfaceFlinger=====>framebuffer。
8.2 Android窗口管理
Activity如何启动的?
系 统初始化时会启动一系列服务,这其中包括Activity Manager Service和Window Manager service,这些Service属于常驻程序,应用的进程可获得Service的本地代理来与Service进行通信,一般通过Binder或 AIDL接口。
Activity启动首先要程序的进程跑起来,而一个应用程序的主线程的启动时通过AMS(Activity Manager Service的缩写)创建Activity Thread对象,并由该对象来创建进程的主线程,通过AMS的request调度该进程的Activity。并且会创建消息循环Looper来实现消息 的分发。
下图是介绍ActivityThread中的ActivityRecord与AMS中HistoryRecord联系。
ActivityThread 的成员变量mActivities是个数组,用来保存该进程中的所有的Activity,并以ActivityRecord形式存在,而在AMS中也同样 有个mHistory数组来管理所有的Activity。这个mHistory保存的是HistoryRecord类,HistoryRecord中有 token,taskid。Taskid标示是哪个task, token标示哪个Activity。
Activity启动流程
1. 发起请求startActivity(intent)
2. Activity Service Manager接收到请求执行StartActivity函数。
3. 通过app.thread.scheduleLaunchActvity在App应用中建立新的ActivityRecord。
4. 建立新的Activity对象并放入到ActivityRecord中。
5. 将ActivityRecord加入到mActivites@ActivityThread
6. 发起Activity.onCreate(..),,该onCreate就是在你的应用程序XXXActivity中的onCreate。
窗口的基本框架
Activity建立一个主窗口后,在将主窗口添加到WindowManager时,首先要建立
WindowManager 代理对象,并打开一个会话(实现IWindowSession AIDL接口),并维持该会话。Activity将通过该会话与WindowManager建立联系,这个Session是C/S体系的基 础,Client通过WindowSession将window加入到Window Manager中。客户端的Activity通过Session会话与WindowManager建立对话,而WindowManager则通过 IWindow接口访问Client,将消息传递到Client端,通过消息分发渠道,将消息传递到处理函数OnXXX。
在 ActivityThread调用performLaunchActivity时,会使用Activity.attach()建立一个 PhoneWindow主窗口。这个主窗口的建立并不是一个重点。当ActivityThread调用handleResumeActivity去真正启 动一个Activity时候,将主窗口加入到WindowManager,当然并不是将主窗口本身,而是将主窗口的DecorView加入到 WindowManager中。DecorView实际上是一个ViewGroup,DecorView是Top-Level View。
下图描述的是如何将窗口的主View既DecorView添加到window manager
图 中在添加DecorView到WM中时,会创建一个ViewRoot,每个窗口(DecorView)都会对应一个ViewRoot,他是整个View树 形结构的根,ViewRoot实际是一个Handler,View Tree消息的发送和处理都是从他开始。ViewRoot是建立主View与WindowsManger通讯的桥梁。ViewRoot与Window Manager之间的联系是IWindowSession和IWindow。ViewRoot通过IWindowSession添加窗口到Window Manager。而IWindow这是Window Manager分发消息
给Client ViewRoot的渠道。IWindowSession和IWindow都是AIDL接口,窗口与Window Manager Service就是利用这两个AIDL接口进行进程间通信。
消息机制Looper,Handle,MessageQueue
Looper只是产生一个消息循环框架,首先Looper创建了MessageQueue并把它挂接在Linux的线程上下文中,进入到取消息,并分发消息的循环当中。
Handle对象在同一个线程上下文中取得MessageQueue,最主要的就是SendMessage和担当起dispatchMessage这个实际工作。
MessageQueue就是消息队列,用来存放消息。
消 息的产生及处理流程(见上图),首先在应用程序启动时会创建ActivityThread对象来启动主线程,并管理当前应用所有的Activity,保存 到mActivities数组中(详细介绍见Activity启动介绍),在ActivityThread里会创建一个Looper对象,既该线程的所有 Activity都共用一个消息循环,Looper里会创建MessageQueue来存放Msg,Looper对象创建成功后,会调用Looper的静 态成员函数loop()开始消息循环,该静态函数中会获得当前线程的Looper对象,并从该Looper的MessageQueue中 GetMessage,获得的msg对象有个target的成员变量,该target就是要处理该msg的Handle对象,如果target为 null,则代表终止该消息循环,否则调用该handle的dispatchmessage,该函数中会调用handlemessage回调函数,该函数 会对消息进行处理,每个创建的handle对象要对handlemessage进行重写。如ViewRoot的handlemessage就是分发到 Focus View的OnXXX()。
但是消息是如何放到MessageQueue中的呢?下面介绍一下Handle类,就是刚才提到的msg 中的target域,该类中包括handlemessage,dispatchmessage以及一系列sendmessage函数,前两个函数的应用场 景前面已经讲过了。这一系列SendMessage就是最后都会调用sendMessageAtTime(),这个函数就是把发送的消息放到当前消息循环 的MessageQueue中。
对输入事件如何响应?
系统中与用户交互的程序,都是用户使用输入设备(鼠标,键盘,触控板),然后由这些 设备的驱动获得输入,并生成Event,放到Event队列中。上层的Window Manager Service会通过Native函数来读取这些Event,并分发到具体的Focus View上。
1、 输入事件的收集
WindowMangerService 启动时会创建一个KeyQ对象mQueue,这是一个消息队列,继承自抽象类KeyInputQ,在KeyInputQ中建立一个独立的线程 InputDeviceReader,使用Native函数readEvent来读取Linux Driver的数据构建Raw Event,放到KeyQ消息队列中。
2、 消息分发到Focus Window
WMS(Window Manager Service)有InputDispatcherThread的线程,该线程循环的从mQueue中获得Raw Events,并根据Event Type分别调用不通dispatch函数(dispatchKey, dispatchPointer, dispatchTrackball),在WMS的dispatch找到中的Focus Window,下面的代码片段中WindowState,是Client端Window在WMS端的对应对象,通过WindowState记录的 mClient(IWindow)接口,将Events专递到Client端。 //InputDispatcherThread DispatchKey() { Object focusObj = mKeyWaiter.waitForNextEventTarget(event, null, null, false, false, pid, uid); 。。。 WindowState focus = (WindowState)focusObj; 。。。 Focusobj.mClient.dispatchKey(event) 。。。 }
3、 应用消息队列分发
通过上面窗口的框架介绍, 我们了解到WindowState中mClient既IWindow接口,是WMS同Activity的Window沟通的接口,通过该接口把event 发到Focus Window的ViewRoot对象,ViewRoot继承自Handle,ViewRoot中的dispatchKey会调用SendMessage, 把该Event发到当前线程的消息队列里,最后会调用到ViewRoot重写handlemessage。
4、 通过FocusPath,发到FocusView
ViewRoot的handlemessage函数找到他对应DecorView并,按下图所示的Focus Path会最终传递到要处理该事件的view上,并调用相应的回调处理函数,如OnClick()。
转自:http://blog.csdn.net/googleandroider/article/details/6580929