您的位置:首页 > 移动开发 > Android开发

Android 2.3 input输入事件处理

2012-08-24 09:13 399 查看
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 端建立管道:

mInputChannel = new InputChannel();

try {

res = sWindowSession.add(mWindow, mWindowAttributes,

getHostVisibility(), mAttachInfo.mContentInsets,

mInputChannel);

} catch (RemoteException e)

WindowManagerService.java 建立管道:

if (outInputChannel != null) {

String name = win.makeInputChannelName();

InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);

win.mInputChannel = inputChannels[0];

inputChannels[1].transferToBinderOutParameter(outInputChannel);

mInputManager.registerInputChannel(win.mInputChannel);

}

创建一对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));
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: