`
yanlijun250
  • 浏览: 750494 次
文章分类
社区版块
存档分类
最新评论

Android 2.3 input输入事件处理

 
阅读更多
linux内核提供了一个Input子系统来实现的,Input子系统会在/dev/input/路径下创建我们硬件输入设备的节点,一般情况下在我们的手机中这些节点是以eventXX来命名的,如event0,event1等等,可以利用EVIOCGNAME获取此事件结点名称。这就是android中对于input事件处理数据的来源点,至于驱动写入数据这块就不说了。
首先,简而言之的介绍一下android事件传递的流程,按键,触屏等事件是经由WindowManagerService获取,并通过共享内存和管道的方式传递给ViewRoot,ViewRoot再dispatch给Application的View。当有事件从硬件设备输入时,system_server端在检测到事件发生时,通过管道(pipe)通知ViewRoot事件发生,此时ViewRoot再去的内存中读取这个事件信息。下面以一个模块划分图了解一下整个过程:

下面详细介绍一个各个模块主要处理流程:
1、建立通读通道初始化:
A、WindowManagerService与ViewRoot建立管道初始化
WindowManagerService : 主要负责事件传递,运行于system_server中,主要利用inputmanager启动input事件启动线程
读取event数据
WindowManagerService--->ViewRoot方向的管道通信,表示WMS通知ViewRoot有新事件被写入到共享内存;

ViewRoot-->WindowManagerService方向的管道通信,表示ViewRoot已经消化完共享内存中的新事件,特此通知WMS。
ViewRoot和WindowManagerService的管道的文件描述符都是被存储在一个名为InputChannel的类中,这个InputChannel类是
管道通信的载体。而这两者间通过ashmem_create_region创建匿名内存进行数据的传递 。
ViewRoot.java 端建立管道:


WindowManagerService.java 建立管道:


创建一对InputChannel,这一对InputChannel中实现了一组全双工管道及申请共享内存,outInputChannel为ViewRoot传递来的
InputChannel对象,在addWindow中对其进行赋值。如此两者利用pipe建立控制通道,利用共享内存建立数据通道。
这是涉及到的文件如下:
frameworks/base/service/java/com/android/server/WindowManagerService.java
frameworks/base/core/java/android/view/ViewRoot.java
--> jni android_view_InputChannel.cpp
--> InputTransport.cpp

B、InputChannel的注册过程
一个管道通信只是对应一个Activity的事件处理,也就是当前系统中有多少个Activity就会有多少个全双工管道,那么系统需要一个管理者来管理以及调度每一个管道通信,因此我们在创建完InputChannel对象后,需要将其注册到这个InputManager管理中。
采用Looper来轮询是否有事件发生,InputManager启动了2个进程来管理事件发生与传递,InputReaderThread和InputDispatcherThread,InputReaderThread进程负责轮询事件发生;InputDispatcherThread负责dispatch事件。
2、数据处理流程:

Eventhub从设备/dev/input/eventX中读取数据:
bool EventHub::getEvent(RawEvent* outEvent)
{
int pollResult = poll(mFDs, mFDCount, -1);
if (pfd.revents & POLLIN) {
int32_t readSize = read(pfd.fd, mInputBufferData,
sizeof(struct input_event) * INPUT_BUFFER_SIZE);
...
}
InputReader根据mapper处理不同的数据,这里有SwitchInputMapper、KeyboardInputMapper,TrackballInputMapper,TouchInputMapper,MouseInputMapper 等处理mapper过滤数据
void InputReader::loopOnce() {
RawEvent rawEvent;

mEventHub->getEvent(& rawEvent);
process(& rawEvent);
}
}
void InputReader::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
case EventHubInterface::DEVICE_ADDED:
addDevice(rawEvent->deviceId);
break;

case EventHubInterface::DEVICE_REMOVED:
removeDevice(rawEvent->deviceId);
break;

case EventHubInterface::FINISHED_DEVICE_SCAN:
handleConfigurationChanged(rawEvent->when);
break;

default:
consumeEvent(rawEvent);
break;
}
}
分别通过:
virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source,
uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0;
virtual void notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t source,
uint32_t policyFlags, int32_t action, int32_t flags,
int32_t metaState, int32_t edgeFlags,
uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords,
float xPrecision, float yPrecision, nsecs_t downTime) = 0;
virtual void notifySwitch(nsecs_t when,
int32_t switchCode, int32_t switchValue, uint32_t policyFlags) = 0;
将数据回调给inputDisplatcher模块

InputDispatcher 对于接收到的数据进行分发:
InputDispatcherThread线程的轮询过程dispatchOnce()-->dispatchOnceInnerLocked(),InputDispatcherThread线程不停的执行该操作,以达到轮询的目的。
处理进行分发给不同的应用
bool InputDispatcherThread::threadLoop() {
dispatchOnceInnerLocked{
case EventEntry::TYPE_KEY:
dispatchKeyLocked(); --> dispatchEventToCurrentInputTargetsLocked
--> prepareDispatchCycleLocked
case EventEntry::TYPE_MOTION:
dispatchMotionLocked();
}
}
dispatchKeyLocked函数,它接下来就调用dispatchEventToCurrentInputTargetsLocked来进一步处理了。把当前需要接受键盘事件的Activity窗口添加到mCurrentInputTargets中去了,因此,这里就分别把它们取出来,然后调用prepareDispatchCycleLocked函数把键盘事件分发给它们处理。
inputDispatcherThread处理流程:
inputDispatcherThread的主要操作是分两块同时进行的,
一部分是对InputReader传递过来的事件进行dispatch前处理,比如确定focus window,特殊按键处理如HOME/ENDCALL等,在预处理完成后,InputDispatcher会将事件存储到对应的focus window的outBoundQueue,这个outBoundQueue队列是InputDispatcher::Connection的成员函数,因此它是和ViewRoot相关的。
一部分是对looper的轮询,这个轮询过程是检查NativeInputQueue是否处理完成上一个事件,如果NativeInputQueue处理完成事件,它就会向通过管道向InputDispatcher发送消息指示consume完成,只有NativeInputQueue consume完成一个事件,InputDispatcher才会向共享内存写入另一个事件。
这里主要用到了两个Queue队列:
Queue<DispatchEntry> outboundQueue; 利用handleReceiveCallback 处理收到的数据
利用inputPublisher中的publishKeyEvent及publishMotionEvent将event写入到共享内存中。
Queue<EventEntry> mInboundQueue; 处理notify回调来的数据
针对客户端数据处理逻辑:
InputConsumer 用于消费数据 (InputChannel.cpp中),其中核心的函数是:
status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** outEvent) {
switch (mSharedMessage->type) {
case AINPUT_EVENT_TYPE_KEY: {
KeyEvent* keyEvent = factory->createKeyEvent();
if (! keyEvent) return NO_MEMORY;

populateKeyEvent(keyEvent);

*outEvent = keyEvent;
break;
}

case AINPUT_EVENT_TYPE_MOTION: {
MotionEvent* motionEvent = factory->createMotionEvent();
if (! motionEvent) return NO_MEMORY;

populateMotionEvent(motionEvent);

*outEvent = motionEvent;
break;
}
...
}

JNI 函数处理数据: android_view_InputQueue.cpp
int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* data) {
/* 先收到信号 */
status_t status = connection->inputConsumer.receiveDispatchSignal();

/* 再处理数据 */
status = connection->inputConsumer.consume(& connection->inputEventFactory, & inputEvent);
/* 转换成java对象native数据 */
android_view_KeyEvent_fromNative 及android_view_MotionEvent_fromNative
/* 回调数据给Java层*/
env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,
dispatchMethodId, inputHandlerObjLocal, inputEventObj,
jlong(finishedToken));
}
对于最后C++调用Java的方法说明一下:
首先注册两个方法:
env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,
dispatchMethodId, inputHandlerObjLocal, inputEventObj,
jlong(finishedToken));
GET_STATIC_METHOD_ID(gInputQueueClassInfo.dispatchKeyEvent, gInputQueueClassInfo.clazz,
"dispatchKeyEvent",
"(Landroid/view/InputHandler;Landroid/view/KeyEvent;J)V");

GET_STATIC_METHOD_ID(gInputQueueClassInfo.dispatchMotionEvent, gInputQueueClassInfo.clazz,
"dispatchMotionEvent",
"(Landroid/view/InputHandler;Landroid/view/MotionEvent;J)V");
然后在InputQueue.java中有这两个方法的定义:
env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,
dispatchMethodId, inputHandlerObjLocal, inputEventObj,
jlong(finishedToken));
private static void dispatchKeyEvent(InputHandler inputHandler,
KeyEvent event, long finishedToken) {
Runnable finishedCallback = FinishedCallback.obtain(finishedToken);
inputHandler.handleKey(event, finishedCallback);
}

@SuppressWarnings("unused")
private static void dispatchMotionEvent(InputHandler inputHandler,
MotionEvent event, long finishedToken) {
Runnable finishedCallback = FinishedCallback.obtain(finishedToken);
inputHandler.handleMotion(event, finishedCallback);
}
env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,
dispatchMethodId, inputHandlerObjLocal, inputEventObj,
jlong(finishedToken));
handleKey最后由InputHandler 处理keyevent及motionevent事件
客户端数据回调机制:
对于每个客户端注册了一个InputChannel
status_t NativeInputQueue::registerInputChannel(JNIEnv* env, jobject inputChannelObj,
添加notify回调函数
looper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);

env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,
dispatchMethodId, inputHandlerObjLocal, inputEventObj,
jlong(finishedToken));

转自:http://blog.csdn.net/andyhuabing/article/details/7006688

分享到:
评论

相关推荐

    Android技术内幕.系统卷(扫描版)

    2.3 android对linux内核的增强 /42 2.3.1 alarm(硬件时钟)/43 2.3.2 ashmem(匿名内存共享)/46 2.3.3 low memory killer(低内存管理)/52 2.3.4 logger(日志设备)/56 2.3.5 android pmem /65 2.3.6 switch /79...

    《Google Android SDK开发范例大全(第3版)》.pdf

    2.3 android应用程序架构——从此开始 15 2.4 可视化的界面开发工具 18 2.5 部署应用程序到android手机 19 第3章 用户人机界面 22 3.1 更改与显示文字标签 23 3.2 更改手机窗口画面底色 24 3.3 ...

    Android 4游戏编程入门经典

     2.3 运行和调试android应用程序  2.3.1 连接设备  2.3.2 创建一个android虚拟设备  2.3.3 运行应用程序  2.3.4 调试应用程序  2.3.5 logcat和ddms  2.3.6 使用adb  2.4 小结 第3章 游戏开发基础  3.1 游戏...

    Google Android SDK开发范例大全(第3版) 1/5

    《Google Android SDK开发范例大全(第3版)》:全新2.3以上版本精彩范例。新增HTML5手机应用程序范例。影音功能大突破,领先业界的影音播放功能详解。易于阅读的架构设计,边看边学,每个范例均搭配步骤及完成画面。...

    Google Android SDK开发范例大全(第3版) 4/5

    《Google Android SDK开发范例大全(第3版)》:全新2.3以上版本精彩范例。新增HTML5手机应用程序范例。影音功能大突破,领先业界的影音播放功能详解。易于阅读的架构设计,边看边学,每个范例均搭配步骤及完成画面。...

    Google Android SDK开发范例大全(第3版) 3/5

    《Google Android SDK开发范例大全(第3版)》:全新2.3以上版本精彩范例。新增HTML5手机应用程序范例。影音功能大突破,领先业界的影音播放功能详解。易于阅读的架构设计,边看边学,每个范例均搭配步骤及完成画面。...

    Google Android SDK开发范例大全(第3版) 5/5

    《Google Android SDK开发范例大全(第3版)》:全新2.3以上版本精彩范例。新增HTML5手机应用程序范例。影音功能大突破,领先业界的影音播放功能详解。易于阅读的架构设计,边看边学,每个范例均搭配步骤及完成画面。...

    Google Android SDK开发范例大全(第3版)part2

     2.3 Android应用程序架构——从此开始  2.4 可视化的界面开发工具  2.5 部署应用程序到Android手机  第3章 用户人机界面  3.1 更改与显示文字标签  3.2 更改手机窗口画面底色  3.3 更改TextView文字...

    Android技术内幕.系统卷 pdf

    2.3 android对linux内核的增强 /42 2.3.1 alarm(硬件时钟)/43 2.3.2 ashmem(匿名内存共享)/46 2.3.3 low memory killer(低内存管理)/52 2.3.4 logger(日志设备)/56 2.3.5 android pmem /65 2.3.6 ...

    Android底层开发技术实战详解--内核、移植和驱动.(电子工业.王振丽).part1

    2.3 分析并编译Android源代码 35 2.3.1 Android源代码的结构 35 2.3.2 编译Android源代码 40 2.3.3 运行Android源代码 42 2.3.4 实践演练——演示编译Android程序的两种方法 43 2.4 编译...

    Android底层开发技术实战详解--内核、移植和驱动.(电子工业.王振丽).part3

    2.3 分析并编译Android源代码 35 2.3.1 Android源代码的结构 35 2.3.2 编译Android源代码 40 2.3.3 运行Android源代码 42 2.3.4 实践演练——演示编译Android程序的两种方法 43 2.4 编译...

    android游戏编程入门

     2.3 运行和调试Android应用  程序 29  2.3.1 连接设备 29  2.3.2 创建一个Android虚拟  设备 29  2.3.3 运行应用程序 30  2.3.4 调试应用程序 32  2.3.5 LogCat和DDMS 34  2.3.6 使用ADB 36  2.4 小结 37...

    Android底层开发技术实战详解--内核、移植和驱动.(电子工业.王振丽).part2

    2.3 分析并编译Android源代码 35 2.3.1 Android源代码的结构 35 2.3.2 编译Android源代码 40 2.3.3 运行Android源代码 42 2.3.4 实践演练——演示编译Android程序的两种方法 43 2.4 编译...

    一个可自定义的Android ViewGroup,用于显示切屑(在《材料设计指南》中指定)。-Android开发

    maven {url“ https://jitpack.io”}}}在您的应用程序级别build.gradle:依赖项{compile'c​​om .github.tylersuehr7:chips-input-layout:2.3'}该库的核心功能:使用库(Glide,Picasso等)加载芯片化身作为用户...

Global site tag (gtag.js) - Google Analytics