Android Input Framework(一)

来源:互联网 发布:数据精灵有安卓版吗 编辑:程序博客网 时间:2024/05/18 03:50

http://blog.sina.com.cn/s/blog_89f592f50101394l.html


1 Input Framework概述

Android输入系统在整个图形系统框架中扮演了很重要的角色,主要负责用户消息的管理,具体职责包括以下几个方面:

 

1、  从底层驱动中获取各种原始的用户消息,包括按键、触摸屏、鼠标、滚迹球等用户事件消息。

2、  最原始消息进行预处理,包括两个方面:一方面,将消息转化成系统可以处理的消息事件;另一方面,处理一些特殊的事件,比如HOMEMENUPOWER键等处理。

3、  将处理后的消息事件分发到各个应用进程,这个需要使用IPC机制,Android系统使用管道来进行消息的传递。

 

Android系统使用InputManager类来管理消息,而具体的功能则是通过InputReaderThreadInputDispatcherThread两个线程来实现。其中InputReaderThread线程负责消息的读取,而

InputDispatcherThread则负责消息的预处理和分发到各个应用进程中。输入系统的整体框架如下图所示:

Android <wbr>Input <wbr>Framework(一)

从框图中可以看出,Android输入系统通过EventHub收集输入设备的原始数据,InputReader调用接口读取EventHub中获取的数据,然后通知InputDispatcher数据已经准备好,InputDispatcher获得数据回调InputManager的接口间接回调WMS 中的InputMonitor对输入消息进行处理,如果WMS没有消耗掉该消息,则InputDispatcher会将该消息通过管道的方式,直接发送到应用进程中,当前焦点应用的ViewrootImpl会收到该消息,并对消息进行分发处理,最终将其发送到对应的View对象中进行界面响应。

 

1 Native InputManager初始化

WindowManagerService构造函数中,经过JNI调用完成了NativeInputManager的初始化,初始化工作有如下几点。

1.1 调用时序图

Android <wbr>Input <wbr>Framework(一)

1.2 类图对象关系

Android <wbr>Input <wbr>Framework(一)

1.3Native层注册java层的CallBacks回调接口

InputManagerService类中定义了一个Callback接口:

  public interface Callbacks {

        public void notifyConfigurationChanged();

        public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen);

        public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle);

        public long notifyANR(InputApplicationHandle inputApplicationHandle,

                InputWindowHandle inputWindowHandle);

        public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn);

        public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags);

        public long interceptKeyBeforeDispatching(InputWindowHandle focus,

                KeyEvent event, int policyFlags);

        public KeyEvent dispatchUnhandledKey(InputWindowHandle focus,

                KeyEvent event, int policyFlags);

        public int getPointerLayer();

    }

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,

            boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {

final InputMonitor mInputMonitor = new InputMonitor(this);

mInputManager = new InputManagerService(context, mInputMonitor);

}

 

1.4 创建InputDispatcherInputReader线程

Android <wbr>Input <wbr>Framework(一)

InputManagerService的构造函数中,调用了

Frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp中的nativeInit()方法。

首先在JNI方法中创建了一个NativeInputManager对象,该对象内部构造函数中又创建了一个InputManager对象。注意这里的InputManagernative的。关键在于InputManager的构造函数中,创建了两个非常重要的对象,InputDispatcherInputReader,前者是作为消息派发者,后者是input消息的读取者。然后在initialize()方法中,将前面创建的两对象作为参数,创建了对应的两个线程,分别是InputReaderTheadInputDispatchThread

 

 消息传送

2.1 创建InputChannel

      前面也简单说过,InputDispatch和客户端之间是通过Pipe传递消息的,Pipelinux系统调用的一部分,我们需要关注的是Pipe所包含的读写描述符,而为了程序设计的便利,Android增加了一个InputChannel类,有两个作用,一个是保存消息端口对应的Pipe的读写描述符,另一个是通过使用InputChannel所提供的函数创建底层的Pipe对象,。Pipe为管道的意思。

2.1.1 时序图(创建和在wms中注册)

Android <wbr>Input <wbr>Framework(一)

2.1.2 创建InputChannel流程

     在上面时序图看出,创建InputChannel是从添加窗口开始的,当客户需要添加窗口的时候,会创建ViewRootImpl对象,并调用它的setView()方法,通过IPC通信调用SessionaddWindow()方法,其中就包含了一个InputChannel对象,里面没有数据的空壳,然后调用到WMSaddWindow()方法,希望Wms创建真正的InputChannel

WmsaddWindow()

   public int addWindow(Session session, IWindow client, int seq,

            WindowManager.LayoutParams attrs, int viewVisibility,

            Rect outContentInsets, InputChannel outInputChannel) {

            ……

if (outInputChannel != null && (attrs.inputFeatures

                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {

                String name = win.makeInputChannelName();

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

                win.setInputChannel(inputChannels[0]);

                inputChannels[1].transferTo(outInputChannel);

               

                mInputManager.registerInputChannel(win.mInputChannel,win.mInputWindowHandle);

            }

           ……

}

addWindow()内调用了InputChannel.openInputChannelPaire()方法开始创建InputChannel,过程如下图:

Android <wbr>Input <wbr>Framework(一)

1)        InputChannel.java的静态方法openInputChannelPai()中,调用了JNI 

frameworks/base/coar/jni/android_Inputview_Channel.cpp

 android_view_InputChannel_nativeOpenInputChannelPair()方法

static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,

        jclass clazz, jstring nameObj) {

    ……

    sp<InputChannel> serverChannel;

    sp<InputChannel> clientChannel;

    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);

    ……

    jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);

    if (env->ExceptionCheck()) {

        return NULL;

    }

 

    jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,

            new NativeInputChannel(serverChannel));

    if (env->ExceptionCheck()) {

        return NULL;

    }

 

    jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,

            new NativeInputChannel(clientChannel));

    if (env->ExceptionCheck()) {

        return NULL;

    }

 

    env->SetObjectArrayElement(channelPair, 0, serverChannelObj);

    env->SetObjectArrayElement(channelPair, 1, clientChannelObj);

    return channelPair;

}

在上面代码中,新建了Native层两个通道变量,分别是serverChannelclientChannel,然后将两个空壳传入InputChannel::openInputChannelPair()中。

2)        NativeInputChannel类是定义在InputTrasport.cpp文件中,openInputChannelPair()方法才是真正的创建管道,然后赋值给两个空壳。

   

status_t InputChannel::openInputChannelPair(const String8& name,

        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {

    int sockets[2];

    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {

        status_t result = -errno;

        ALOGE("channel '%s' ~ Could not create socket pair.  errno=%d",

                name.string(), errno);

        outServerChannel.clear();

        outClientChannel.clear();

        return result;

    }

 

    int bufferSize = SOCKET_BUFFER_SIZE;

    setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));

    setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));

    setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));

    setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));

 

    String8 serverChannelName = name;

    serverChannelName.append(" (server)");

    outServerChannel = new InputChannel(serverChannelName, sockets[0]);

 

    String8 clientChannelName = name;

    clientChannelName.append(" (client)");

    outClientChannel = new InputChannel(clientChannelName, sockets[1]);

    return OK;

}

调用Linuxsocketpair()方法建立一对匿名的已经连接的套接字,然后调用setsockopt()方法为其分配内存,然后用其做为参数,创建native环境中的InputChannel对象,分别赋值给outServerChannel、和outClientChannel指针,

3)        创建好两个native层的InputChannel对象后,存在channelPair数组中,然后通过JNI返回到JAVA环境中。

JNI

env->SetObjectArrayElement(channelPair, 0, serverChannelObj);

    env->SetObjectArrayElement(channelPair, 1, clientChannelObj);

    return channelPair;

 

JAVA

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

 

2.2 WMS中注册InputChannel

在上面一节介绍了创建InputChannel的过程,其实的创建了两个InputChannel对象,一个作为服务端,需要Wms对其进行注册,另一个作为客户端,需要客户进程对其进行注册。这一节我们看Wms中注册InputChannel。在2.1.1的时序图中我们可以看到,在WmsaddWindow()方法中,调用InputChannel.openInputChannelPair(name);返回一对InputChannel对象,里面包含了管道的描述符等信息,然后调用InputManagerService.registerInputChannel进行注册,即时序图中第八步开始,。流程图如下:

Android <wbr>Input <wbr>Framework(一)

通过上面可以看到,经过InputManagerService的调用,最终到达JNInativeRegisterInputChannel方法中,首先通过参数获取到Native层的InputChannel对象,还记得在Native InputManager初始化中,在JNI创建了一个NativeInputManager对象。这里就获取到该对象,然后调用NativeInputManager:: registerInputChannel()开始注册:

status_t NativeInputManager::registerInputChannel(JNIEnv* env,

        const sp<InputChannel>& inputChannel,

        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {

    return mInputManager->getDispatcher()->registerInputChannel(

            inputChannel, inputWindowHandle, monitor);

}

NativeInputManager对象中有一个InputManager对象引用,InputManager对象中又有一个InputDispatcher对象的引用,所以将调用到InputDispatcher::registerInputChannel()进行注册:

status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,

        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {

        ……

        sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);

        int fd = inputChannel->getFd();

        mConnectionsByFd.add(fd, connection);

        if (monitor) {

            mMonitoringChannels.push(inputChannel);

        }

        ……

        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);

    } // release lock

    return OK;

}

首先需要创建一个Connection对象,即客户端与服务端连接的接口,然后将该对象加入到InputDispatchermConnectionsByFd列表中,当需要通过管道发送消息的时候,从该列表中取出Connection对象,该对象与客户端是向对应的,之后调用mLooper->addFd()方法,把InputChannel对应的描述符添加到mLooper内部的描述符列表中。这里完成了wmsInputChannel的注册,即serverChannel

2.3客户进程注册InputChannel

Wms中创建了一对InputChannel,其中serverChannel被注册到了InputDispatcher线程中,另一个clientChannel则需要注册到客户进程中,从而使得客户进程可以直接接收到InputDispatcher发送的用户消息。

客户端注册InputChannel和在InputManager中注册InputChannel的本质是相同的,即告诉所在进程的native looper对象,让它监控指定的文件描述符即可。客户端的InputChannel来源于调用WmsaddWindow()时,最后一个参数是一个InputChannel类型的输出参数。下面看调用的时序图

2.3.1 时序图

Android <wbr>Input <wbr>Framework(一)

2.3.2 注册流程

    2.3.1时序图中,我们看到setView()方法,该方法是在客户创建新的窗口时候调用,前面我们也看到了,在这个方法中通过IPC通信调用了Wms.addWindow(),创建了一对管道InputChannel,并在Wms中注册了serverChannel,完成了这两步之后,那么就需要对客户进程注册clientChannel了。

1)        客户进程是创建的新的进程,那么为改进程创建一个用于接收消息的WindowInputEventReceiver对象,它继承于InputEventReceiver类。在InputEventReceive构造函数中,调用了JNI方法nativeInit()

static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,

        jobject inputChannelObj, jobject messageQueueObj) {

    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,

            inputChannelObj);

    ……

    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,

            receiverObj, inputChannel, messageQueue);

    status_t status = receiver->initialize();

……

 return reinterpret_cast<jint>(receiver.get());

}

 

2)        调用android_view_InputChannel_getInputChannel获取native层的InputChannel对象,参数创建一个NativeInputEventReceiver对象,把InputChannel作为该对象的内部参数。

3)        initialize()方法中,把文件描述符添加到内部的接收描述符列表中,使得客户进程窗口可以接收到发往该文件描述符的消息

status_t NativeInputEventReceiver::initialize() {

    int receiveFd = mInputConsumer.getChannel()->getFd();

    mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL);

    return OK;

}

2.4 Wms中获取Input消息

用户消息可以分为两类,一个是Key消息,另一个是Motion消息。对于Motion消息,InputDispatcher是通过pipe直接把消息发往客户窗口的,Wms不能对这些消息进行任何的前置处理,而对于Key消息,则会先回调Wms中的Key消息处理函数,在Wms中不处理的消息,才会把消息发往客户端。一般情况下,wms中仅仅处理一些系统的Key消息,比如”Home”键、音量键等。

2.4.1 时序图(Wms获取Key消息)

Android <wbr>Input <wbr>Framework(一)

2.4.2 流程分析

    InputDispatcher中,收到InputReader发送过来的Event消息,最终调用到InputDispatcher::dispatchKeyLocked()方法,开始派发按键消息,接着消息传到NativeInputManager中。在第一节 Native InputManager初始化中,我们向JNI注册了java层的一些回调接口,这时候就用到了的。

nsecs_t NativeInputManager::interceptKeyBeforeDispatching(

        const sp<InputWindowHandle>& inputWindowHandle,

        const KeyEvent* keyEvent, uint32_t policyFlags) {

        ……

        if (keyEventObj) {

            jlong delayMillis = env->CallLongMethod(mServiceObj,

                    gServiceClassInfo.interceptKeyBeforeDispatching,

                    inputWindowHandleObj, keyEventObj, policyFlags);

        ……

    return result;

}

调用JNIenv->CallLongMethod()方法,回调JAVA层方法,mServiceObj对应的是java层的InputManagerService类的实例,gServiceClassInfo.interceptKeyBeforeDispatching指的是InputManagerServiceinterceptKeyBeforeDispatching()函数,于是消息就从JNI传递到了JAVA层。最终消息就传到了PhoneWindowManager中。

2.5客户窗口获取Input消息

2.5.1时序图(Key消息分析)

  Android <wbr>Input <wbr>Framework(一)


2.5.2 流程分析

无论是Key消息还是Motion消息,都是通过Pipe管道传递到客户进程窗口的。所有的客户进程都有一个主线程,即ActivityThread类,该类每次开始的时候就会进入一个Looper循环中,然后就不断的从MessageQueue中读取消息,如果没有消息,则进入wait状态,直到下一个消息。

InputDispatcher中,获取了InputReaderKey消息,经过一步步处理,调用到startDispatchCycleLocked()方法:

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,

        const sp<Connection>& connection) {

       ……

       case EventEntry::TYPE_KEY: {

            KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);

 

            // Publish the key event.

            status = connection->inputPublisher.publishKeyEvent(……);

            break;

        }

        ……

}

注意到这里的connection,这是Wms注册InputChannel的时候创建的Connection对象,然后将KeyEvent消息写入管道中。

status_t InputPublisher::publishKeyEvent(

    ….

    return mChannel->sendMessage(&msg);

}

 

status_t InputChannel::sendMessage(const InputMessage* msg) {

    size_t msgLength = msg->size();

    ssize_t nWrite;

    do {

        nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);

} while (nWrite == -1 && errno == EINTR);

…….

    return OK;

}

 

消息写入管道后,经过MessageQueue通知到客户进程,然后在ViewRootImpl中,调用doConsumeBatchedInput()开始读取keyEvent消息。看看receiveMessage():

status_t InputChannel::receiveMessage(InputMessage* msg) {

    ssize_t nRead;

    do {

        nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);

    } while (nRead == -1 && errno == EINTR);

    ……

    return OK;

}

 


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中,当mNeedToScanDevicestrue(当创建EventHub对象时,它就为true),即当InputReader第一次调用getEvents的时候需要打开设备,它将从/dev/input目录下查找所有设备,并进行打开,获取其相关属性,最后加入mDevices列表中。

Android <wbr>Input <wbr>Framework(二)---EventHub

     openDeviceLocked()方法中,首先调用open()打开设备, ioctl()获取设备名字,识别打开设备是哪个classs的,即按键、单点触摸屏、多点触摸屏等等。如果设备是认为是合法的,创建了设备,然后向epoll注册该设备,并添加到mDevices列表中:

// Register with epoll.

    struct epoll_event eventItem;

    memset(&eventItem, 0, sizeof(eventItem));

    eventItem.events = EPOLLIN;

    eventItem.data.u32 = deviceId;

    if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {

        ALOGE("Could not add device fd to epoll instance.  errno=%d", errno);

        delete device;

        return -1;

}

addDeviceLocked(device);

1.2 读取输入事件

      要说EventHub::getEvents如何获取输入事件,不得不先说说它的几个相关的成员变量:

n         mPendingEventCount:调用epoll_wait时的返回值,当然如果没有事件,则其值为0

n         mPendingEventIndex:当前需要处理的事件索引

n         mEpollFdepoll实例,在EventHub::EventHub中调用epoll_create(EPOLL_SIZE_HINT)初始化此例,所有输入事件通过epoll_wait来获取,每一个事件的数据结构为:struct epoll_event。注:epoll_event只表明某个设备上有事件,并不包含事件内容,具体事件内容需要通过read来读取。

看看epoll_event结构体:

typedef union epoll_data

{

    void *ptr;

    int fd;

    unsigned int u32;

    unsigned long long u64;

} epoll_data_t;

struct epoll_event

{

    unsigned int events;

    epoll_data_t data;

};

 

每个设备被创建(在函数EventHub::openDeviceLocked中)时,都会向epoll注册,代码如下:

    struct epoll_event eventItem;

    memset(&eventItem, 0, sizeof(eventItem));

    eventItem.events = EPOLLIN;

    eventItem.data.u32 = deviceId;

    if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {

     ……

}

查看设备上是否有输入事件:

int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

        if (pollResult < 0) {

         ……

        } else {

            // Some events occurred.

            mPendingEventCount = size_t(pollResult);

        }

在调用epoll_wait()之后,读到的epoll_event输入事件保存在mPendingEventItems,总共的事件数保存在mPendingEventCount,当然,在调用epoll_wait()之前,mPendingEventIndex被清0

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {

    struct input_event readBuffer[bufferSize];

    RawEvent* event = buffer;

    size_t capacity = bufferSize;

    bool awoken = false;

    for (;;) {

        ……

        while (mPendingEventIndex < mPendingEventCount) {

            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];

            ……

            ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);

            Device* device = mDevices.valueAt(deviceIndex);

            if (eventItem.events & EPOLLIN) {

                int32_t readSize = read(device->fd, readBuffer,

                        sizeof(struct input_event) * capacity);

               …….

             }

        ……

        mPendingEventIndex = 0;

        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

 

        if (pollResult < 0) {

         ……

        } else {

            // Some events occurred.

            mPendingEventCount = size_t(pollResult);

        }

    }

    return event - buffer;

}

在上面的代码中可以看到,如果没有输入事件,那么代码将在epoll_wait()阻塞,当有输入事件的时候读到数据,mPendingEventItems保存了输入事件,mPendingEventCount保存了事件的数量,而且mPendingEventIndex=0,所以此时满足了条件:mPendingEventIndex < mPendingEventCount,将进入while循环mPendingEventCount次,每次通过read()方法读取相对应的输入数据。


 

1.3 读取输入数据

首先,需要看看相关的数据结构:

Android <wbr>Input <wbr>Framework(二)---EventHub

经过1.2小节介绍,我们知道,epoll_event结构体是用来存储输入事件的,调用epoll_wait()读取输入事件,一般情况下mPendingEventCount=1,当有输入事件的时候,通过read()方法读取输入数据。

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {

 

    struct input_event readBuffer[bufferSize];

    RawEvent* event = buffer;

size_t capacity = bufferSize;

while (mPendingEventIndex < mPendingEventCount) {

    ALOGE(“mPendingEventCount=%d”, mPendingEventCount);

    int32_t readSize = read(device->fd, readBuffer,

                        sizeof(struct input_event) * capacity);

    ize_t count = size_t(readSize) / sizeof(struct input_event);

                    for (size_t i = 0; i < count; i++) {

                        ALOGE("%s got: t0=%d, t1=%d, type=%d, code=%d, value=%d",

                                device->path.string(),

                                (int) iev.time.tv_sec, (int) iev.time.tv_usec,

                                iev.type, iev.code, iev.value);

                        const struct input_event& iev = readBuffer[i];

                        event->when = now;

                        event->deviceId = deviceId;

                        event->type = iev.type;

                        event->code = iev.code;

                        event->value = iev.value;

                        event += 1;

}

}

return event- buffer

}

我们来分下上面的代码,先看看几个重要的变量

n         buffer

一个RawEvent结构体的一个数组,数组元素个数为bufferSizebuffer看成这个数组的指针。

n         event

    一个RawEvent结构体的一个数组,数组元素个数为bufferSize,刚开始被赋值为buffer

n         readBuffer

一个input_event结构体的一个数组,数组元素个数为bufferSize,在read()方法中用于读取一个输入事件的数据。

n         count

表示读取了多少组数据。

我们模拟按下一个按键然后松开为例子,在上面代码中加了两个打印。

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

   第一行,mPendingEventCount=1表示一个输入事件,

第二行表示按键按下的消息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作为参数传进来,并赋值给eventevent用来存储输入事件的数据。将readBuffer指针传入kernel获取输入事件数据,经过for循环,将input_event数据映射到RawEvent上。在return那里返回了描述一个输入事件的RawEvent结构体数组的个数。在上面我们可以看到,描述一个按键的输入事件只需要两个RawEvent,相对简单,但是,触摸事件相对复杂些。

 到此,EventHub完成了数据的读取,那么将在InputReader中对RawEvent数据进行处理。