Framework之View的工作原理(一)

来源:互联网 发布:车载电子狗软件 编辑:程序博客网 时间:2024/05/21 06:41

前言:前段时间踏上了android框架研究的不归路,不得不说,真的是很辛苦。但是学起来之后又会有一种豁然开朗的感觉,还是很值得的。陆陆续续看了许多,觉得还是先从View的工作原理开始讲起吧。Framework中的三大巨头是AmS,WmS和View。选择从它开始的原因是它是跟我们打交道最多的。这一系列我打算这样安排一个主体的脉络,View如何从输入设备获取消息View的事件派发体系View绘制的三大流程。之后或许还会多加一些View的滑动冲突等知识。话不多说,我们切入正题。这一系列的知识点多来自于《Android内核剖析》和《Android开发艺术探索》。


一、从输入设备获取消息指的是什么?

首先我们知道,当客户程序创建View之后,需要向WmS申请添加窗口,由WmS同意之后才能最终将View显示出来,就是我们平时操作的时候看见的视图了。根据我们日常的经验,我们可以通过手指的触控,系统按键(音量,“home”键等)来使得View发生一些变化,那么系统是如何检测到我们的动作并作出响应的呢?这便是从输入设备获取消息的含义。


二、需要解决的三个问题

  • 获得原始的用户信息,按键,触摸屏,鼠标等。
  • 对原始信息进行必要的加工,使之转换为程序可以理解识别的信息。比方说,不同的设备按下“home”键产生的码值不同,但是系统有责任把它们统一起来,这样程序才能在不同的设备上产生一致的行为。
  • 把转换后的消息发送给对应的用户窗口。
因此Android系统的设计就必须围绕这三个方面来进行。


三、Android2.2及之前版本的消息获取(过时)

先来看看Android2.2及其之前的消息获取方式。


先来了解一下各个名称的含义:

  • KeyInputQueue:KeyQ的基类。
  • KeyQ:WmS的内部类,这个类对象内部包含一个线程对象,即KeyQ是一个线程,其任务是调用native方法(JNI调用)从输入设备读取用户信息,并将消息保存到QueueEvent队列中。
  • QueueEvent:用户消息队列。
  • InputDispatcherThread:WmS的内部类,也是一个线程类。其任务是从QueueEvent中取出用户消息并进行一定的加工,然后判断把该消息发送给哪一个窗口。
  • ViewRoot(W类):每当应用程序创建一个窗口的时候,都会顺带创建一个ViewRoot对象,ViewRoot对象中又会实例化其内部类W类的对象,从本质上来看,ViewRoot是一个Handler对象,W是一个Binder对象。W对象负责IPC调用,ViewRoot负责将远程异步调用转换为本地的一个异步调用。因为ViewRoot中含有W类对象,因此利用其与InputDispatcherThread远程对象进行IPC通信就不足为奇了。

上面的逻辑不难看懂,关键是我们要知道为什么它会被抛弃呢?它存在什么致命的缺点吗?是的,主要存在以下两个不足:
  1. 对所有原始消息的加工都放在Java代码中完成,这就大大加大了消息处理的延迟。
  2. InputDispatcherThread和客户端ViewRoot之间的通信采用Binder进行,Binder的延迟也会大大增加用户操作的延迟。
因此,Android的开发工程师也在Android2.3之后也针对这两点提出了改进的措施。

四、Android2.3做出的改进

我们先来看看改进后的情况。

还是先来了解一下各个名称的含义:
InputReader:从名称我们就几乎可以判断,这是相当于上面说到的KeyQ的一个类,用途自然也是持续从输入设备读取用户输入信息,在系统进程system_process空间中运行。
InputDispatcher:同样我们很容易可以知道,这是InputDispatcherThread的替代品,用于将用户消息发送给WmS或是客户端。
InputMonitor:WmS保存各个窗口信息的地方。
InputManager:提供InputMonitor和JNI层交互的接口。
NativeInputManager:JNI层保存窗口信息的地方。这里需要注意一下,NativeInputManager中的窗口信息来自于WmS,因为WmS本身就是管理用户窗口的,所以有窗口信息不足为奇。其次,NativeInputManager拥有窗口信息的意义在于可以为InputDispatcher提供选择发送窗口的信息。


最后,我们再来看看,Google的工程师们是怎么解决Android2.0之前存在的两个问题的。
  1. 针对问题一,我们发现,InputReader和InputDispatcher均位于JNI层,也就是我们可以采用速度优于Java的C++程序来解决消息的加工问题了。
  2. 针对问题二,我们发现InputDispatcher和ViewRoot之间的通信不再是采用Binder进行通信,而是采用Pipe(管道机制)进行通信,这也在一定程度上提高了IPC速度。

五、各类消息的派发流程

  • 按键消息:InputDispatcher会先回调InputManager中定义的回调函数,其次是InputMonitor和WmS中的回调函数。此时客户窗口能否收到消息就取决于上述这些回调函数的返回值了,若返回值指明消息未被消耗,则客户端可以接受处理。
  • 系统按键消息:比方说“home”键,电话按键等,WmS内部按照默认的方式处理,并返回false,从而InputDispatcher不会将消息派发给客户窗口。这是有道理的,因为客户窗口无权也不应该随意处理系统的按键行为。这种机制给了WmS在客户窗口处理消息之前处理之的一种的可能。
  • 触摸屏消息:对于这类消息,InputDispatcher直接将消息通过Pipe发送给客户窗口,不带含糊的。


注:最后一点点疑惑,就是第一张图的正确性,我发现书里的几张图可能是不对的,应该还需要加上WmS(书中没有)。但是按照上述的第二种解决方案,肯定是要预留WmS在客户窗口处理消息前进行消息处理的能力的,否则一些系统按键怎么办?还有一个问题就是当WmS不想处理该消息之后,消息传递给客户窗口的ViewRoot时是WmS直接利用ViewRoow中的W类进行IPC调用吗?抑或是回溯到InputDispatcher/InputDispatcherThread之后再度进行派发。


0 0
原创粉丝点击