Android Input Framework(一)
来源:互联网 发布:数据精灵有安卓版吗 编辑:程序博客网 时间:2024/05/18 03:50
http://blog.sina.com.cn/s/blog_89f592f50101394l.html
1 Input Framework概述
Android输入系统在整个图形系统框架中扮演了很重要的角色,主要负责用户消息的管理,具体职责包括以下几个方面:
1、
2、
3、
Android系统使用InputManager类来管理消息,而具体的功能则是通过InputReaderThread和InputDispatcherThread两个线程来实现。其中InputReaderThread线程负责消息的读取,而
InputDispatcherThread则负责消息的预处理和分发到各个应用进程中。输入系统的整体框架如下图所示:
从框图中可以看出,Android输入系统通过EventHub收集输入设备的原始数据,InputReader调用接口读取EventHub中获取的数据,然后通知InputDispatcher数据已经准备好,InputDispatcher获得数据回调InputManager的接口间接回调WMS 中的InputMonitor对输入消息进行处理,如果WMS没有消耗掉该消息,则InputDispatcher会将该消息通过管道的方式,直接发送到应用进程中,当前焦点应用的ViewrootImpl会收到该消息,并对消息进行分发处理,最终将其发送到对应的View对象中进行界面响应。
1 Native InputManager初始化
在WindowManagerService构造函数中,经过JNI调用完成了Native层InputManager的初始化,初始化工作有如下几点。
1.1 调用时序图
1.2 类图对象关系
1.3在Native层注册java层的CallBacks回调接口
在InputManagerService类中定义了一个Callback接口:
InputMonitor实现了InputManagerService.Callbacks接口,在WindowManagerService的构造函数中创建了InputMonitor对象,并以mInputMonitor作为参数创建InputManagerService的对象,在InputManagerService构造函数中,将mInputMonitor作为参数调用了JNI函数nativeInit(),将回调接口传到JNI层,在需要的时候,JNI在回调mInputMonitor中的函数,实现数据才传递。
class InputMonitor implements InputManagerService.Callbacks{
}
private WindowManagerService(Context context, PowerManagerService pm,
final InputMonitor mInputMonitor = new InputMonitor(this);
mInputManager = new InputManagerService(context, mInputMonitor);
}
1.4 创建InputDispatcher和InputReader线程
在InputManagerService的构造函数中,调用了
Frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp中的nativeInit()方法。
首先在JNI方法中创建了一个NativeInputManager对象,该对象内部构造函数中又创建了一个InputManager对象。注意这里的InputManager是native的。关键在于InputManager的构造函数中,创建了两个非常重要的对象,InputDispatcher和InputReader,前者是作为消息派发者,后者是input消息的读取者。然后在initialize()方法中,将前面创建的两对象作为参数,创建了对应的两个线程,分别是InputReaderThead和InputDispatchThread。
2 消息传送
2.1 创建InputChannel
2.1.1 时序图(创建和在wms中注册)
2.1.2 创建InputChannel流程
Wms的addWindow():
if (outInputChannel != null && (attrs.inputFeatures
}
addWindow()内调用了InputChannel.openInputChannelPaire()方法开始创建InputChannel,过程如下图:
1)
frameworks/base/coar/jni/android_Inputview_Channel.cpp的
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPa
}
在上面代码中,新建了Native层两个通道变量,分别是serverChannel和clientChannel,然后将两个空壳传入InputChannel::openInputChannelPair()中。
2)
status_t InputChannel::openInputChannelPair(const String8& name,
}
调用Linux的socketpair()方法建立一对匿名的已经连接的套接字,然后调用setsockopt()方法为其分配内存,然后用其做为参数,创建native环境中的InputChannel对象,分别赋值给outServerChannel、和outClientChannel指针,
3)
JNI:
env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
JAVA:
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
2.2 WMS中注册InputChannel
在上面一节介绍了创建InputChannel的过程,其实的创建了两个InputChannel对象,一个作为服务端,需要Wms对其进行注册,另一个作为客户端,需要客户进程对其进行注册。这一节我们看Wms中注册InputChannel。在2.1.1的时序图中我们可以看到,在Wms的addWindow()方法中,调用InputChannel.openInputChannelPair(name);返回一对InputChannel对象,里面包含了管道的描述符等信息,然后调用InputManagerService.registerInputChannel进行注册,即时序图中第八步开始,。流程图如下:
通过上面可以看到,经过InputManagerService的调用,最终到达JNInativeRegisterInputCh
status_t NativeInputManager::registerInputChannel(JNIEnv* env,
}
NativeInputManager对象中有一个InputManager对象引用,InputManager对象中又有一个InputDispatcher对象的引用,所以将调用到InputDispatcher::registerInputChannel()进行注册:
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
}
首先需要创建一个Connection对象,即客户端与服务端连接的接口,然后将该对象加入到InputDispatcher的mConnectionsByFd列表中,当需要通过管道发送消息的时候,从该列表中取出Connection对象,该对象与客户端是向对应的,之后调用mLooper->addFd()方法,把InputChannel对应的描述符添加到mLooper内部的描述符列表中。这里完成了wms的InputChannel的注册,即serverChannel。
2.3客户进程注册InputChannel
Wms中创建了一对InputChannel,其中serverChannel被注册到了InputDispatcher线程中,另一个clientChannel则需要注册到客户进程中,从而使得客户进程可以直接接收到InputDispatcher发送的用户消息。
客户端注册InputChannel和在InputManager中注册InputChannel的本质是相同的,即告诉所在进程的native looper对象,让它监控指定的文件描述符即可。客户端的InputChannel来源于调用Wms的addWindow()时,最后一个参数是一个InputChannel类型的输出参数。下面看调用的时序图
2.3.1 时序图
2.3.2 注册流程
1)
static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
……
}
2)
3)
status_t NativeInputEventReceiver
}
2.4 Wms中获取Input消息
用户消息可以分为两类,一个是Key消息,另一个是Motion消息。对于Motion消息,InputDispatcher是通过pipe直接把消息发往客户窗口的,Wms不能对这些消息进行任何的前置处理,而对于Key消息,则会先回调Wms中的Key消息处理函数,在Wms中不处理的消息,才会把消息发往客户端。一般情况下,wms中仅仅处理一些系统的Key消息,比如”Home”键、音量键等。
2.4.1 时序图(Wms获取Key消息)
2.4.2 流程分析
nsecs_t NativeInputManager::interceptKeyBeforeDispat
}
调用JNI的env->CallLongMethod()方法,回调JAVA层方法,mServiceObj对应的是java层的InputManagerService类的实例,gServiceClassInfo.interceptKeyBeforeDispat
2.5客户窗口获取Input消息
2.5.1时序图(以Key消息分析)
2.5.2 流程分析
无论是Key消息还是Motion消息,都是通过Pipe管道传递到客户进程窗口的。所有的客户进程都有一个主线程,即ActivityThread类,该类每次开始的时候就会进入一个Looper循环中,然后就不断的从MessageQueue中读取消息,如果没有消息,则进入wait状态,直到下一个消息。
在InputDispatcher中,获取了InputReader的Key消息,经过一步步处理,调用到startDispatchCycleLocked
void InputDispatcher::startDispatchCycleLocked
}
注意到这里的connection,这是Wms注册InputChannel的时候创建的Connection对象,然后将KeyEvent消息写入管道中。
status_t InputPublisher::publishKeyEvent(
}
status_t InputChannel::sendMessage(const InputMessage* msg) {
} while (nWrite == -1 && errno == EINTR);
…….
}
消息写入管道后,经过MessageQueue通知到客户进程,然后在ViewRootImpl中,调用doConsumeBatchedInput()开始读取keyEvent消息。看看receiveMessage():
status_t InputChannel::receiveMessage(InputMessage* msg) {
}
Android Input Framework(二)---EventHub
http://blog.sina.com.cn/s/blog_89f592f50101395k.html
1 EventHub 获取输入设备数据
EventHub可以看成是输入消息的集散地,因为android支持多种输入设备,而各种设备的消息类型可能不一样,为了统一管理这些输入消息,Android提出了EventHub的概念,所有的输入事件都会通过EventHub收集,并通过EventHub传递给InputReader,这样对上层来说,就不需要关注底层设备的多样性,减少了上层使用的复杂性。EventHub同时还负责扫描和加载所有的输入设备,InputReader在第一次读取数据的时候会扫描所有的输入设备,并保存每个设备的配置信息。
1.1 打开设备
在EventHub::getEvents中,当mNeedToScanDevices为true时(当创建EventHub对象时,它就为true),即当InputReader第一次调用getEvents的时候需要打开设备,它将从/dev/input目录下查找所有设备,并进行打开,获取其相关属性,最后加入mDevices列表中。
// Register with epoll.
}
addDeviceLocked(device);
1.2 读取输入事件
n
n
n
看看epoll_event结构体:
typedef union epoll_data
{
} epoll_data_t;
struct epoll_event
{
};
每个设备被创建(在函数EventHub::openDeviceLocked中)时,都会向epoll注册,代码如下:
}
查看设备上是否有输入事件:
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
在调用epoll_wait()之后,读到的epoll_event输入事件保存在mPendingEventItems,总共的事件数保存在mPendingEventCount,当然,在调用epoll_wait()之前,mPendingEventIndex被清0。
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
}
在上面的代码中可以看到,如果没有输入事件,那么代码将在epoll_wait()阻塞,当有输入事件的时候读到数据,mPendingEventItems保存了输入事件,mPendingEventCount保存了事件的数量,而且mPendingEventIndex=0,所以此时满足了条件:mPendingEventIndex < mPendingEventCount,将进入while循环mPendingEventCount次,每次通过read()方法读取相对应的输入数据。
1.3 读取输入数据
首先,需要看看相关的数据结构:
经过1.2小节介绍,我们知道,epoll_event结构体是用来存储输入事件的,调用epoll_wait()读取输入事件,一般情况下mPendingEventCount=1,当有输入事件的时候,通过read()方法读取输入数据。
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
size_t capacity = bufferSize;
while (mPendingEventIndex < mPendingEventCount) {
}
}
return event- buffer
}
我们来分下上面的代码,先看看几个重要的变量
n
一个RawEvent结构体的一个数组,数组元素个数为bufferSize,buffer看成这个数组的指针。
n
n
一个input_event结构体的一个数组,数组元素个数为bufferSize,在read()方法中用于读取一个输入事件的数据。
n
表示读取了多少组数据。
我们模拟按下一个按键然后松开为例子,在上面代码中加了两个打印。
n
按下按键被描述才一次输入的事件,log打印如下:
mPendingEventCount=1
/dev/input/event0 got: t0=658, t1=734424, type=1, code=1, value=1
/dev/input/event0 got: t0=658, t1=734434, type=0, code=0, value=0
第二行表示按键按下的消息value=1
第三行表示该消息结束标志
n
按键松开被描述成一个输入事件,log打印如下:
mPendingEventCount=1
/dev/input/event0 got: t0=658, t1=765679, type=1, code=1, value=0
/dev/input/event0 got: t0=658, t1=765694, type=0, code=0, value=0
第一行,mPendingEventCount=1表示一个输入事件
第二行表示按键松开的消息value=0
第三行表示该消息结束标志
对上面变量的分析之后,思路应该清晰多了。在调用getEvents()的时候,将buffer作为参数传进来,并赋值给event,event用来存储输入事件的数据。将readBuffer指针传入kernel获取输入事件数据,经过for循环,将input_event数据映射到RawEvent上。在return那里返回了描述一个输入事件的RawEvent结构体数组的个数。在上面我们可以看到,描述一个按键的输入事件只需要两个RawEvent,相对简单,但是,触摸事件相对复杂些。
- Android Input framework(一)
- Android Input Framework(一)
- Android Input framework(一)
- Android Input Framework(一)
- Android Input Framework(一)
- Android TV Input Framework(Android TV 一)
- android 4.0 framework input
- Android Input Framework(二)---EventHub
- Android Framework------之Input子系统
- Android Framework------之Input子系统
- Android Framework------之Input子系统
- Android Framework------之Input子系统
- Android Input Framework(二)---EventHub
- Android Framework Input 机制分析
- Android Framework------之Input子系统
- Android Input Framework-InputReader&InputDispatcher
- Android Framework------之Input子系统
- Android Input Framework(二)---EventHub
- python中添加自己py文件所在的目录的方法
- SqlDataReader的认识
- JAVA系列-设计模式-建造者模式
- 数学之美读书笔记(四)
- 南阳理工OJ_素数距离问题
- Android Input Framework(一)
- 数学之美读书笔记(五)
- 网络摄象机常用传输协议
- Linux网络编程(5):I/O模型
- Android系统服务一览
- MinGW中的头文件路径
- 怎样把电子书TXT设置成UFT8编码
- Mysql数据类型
- Java中的线程(三)-Java的锁机制