zoukankan      html  css  js  c++  java
  • 【Android】事件输入系统-代码层次解读

      本文基于Android-4.0

    理论层次解读

      请看:www.cnblogs.com/lcw/p/3373214.html

    如何管理各种驱动设备

      在理论中谈到EventHub,这个一看就是一个做实事的,肯定不是领导,哪它的领导是谁呢?

      从以下几方面来分析此问题:

    1. 每个功能模块是怎么产生的?
    2. 读取设备输入流程?
    3. 事件分发流程?

    各个功能模块是怎么产生的?

      先看一下每个模块的工作职责:EventHub, InputReader, InputManager...

    模块功能

    EventHub

      它是系统中所有事件的中央处理站。它管理所有系统中可以识别的输入设备的输入事件,此外,当设备增加或删除时,EventHub将产生相应的输入事件给系统。

      EventHub通过getEvents函数,给系统提供一个输入事件流。它也支持查询输入设备当前的状态(如哪些键当前被按下)。而且EventHub还跟踪每个输入调入的能力,比如输入设备的类别,输入设备支持哪些按键。 

    InputReader

      InputReader从EventHub中读取原始事件数据(RawEvent),并由各个InputMapper处理之后输入对应的input listener.

      InputReader拥有一个InputMapper集合。它做的大部分工作在InputReader线程中完成,但是InputReader可以接受任意线程的查询。为了可管理性,InputReader使用一个简单的Mutex来保护它的状态。

      InputReader拥有一个EventHub对象,但这个对象不是它创建的,而是在创建InputReader时作为参数传入的。

    InputDispatcher

      InputDispatcher负责把事件分发给输入目标,其中的一些功能(如识别输入目标)由独立的policy对象控制。

    InputManager

      InputManager是系统事件处理的核心,它虽然不做具体的事,但管理工作还是要做的,比如接受我们客户的投诉和索赔要求,或者老板的出所筒。

         InputManager使用两个线程:

    1. InputReaderThread叫做"InputReader"线程,它负责读取并预处理RawEvent,applies policy并且把消息送入DispatcherThead管理的队列中。
    2. InputDispatcherThread叫做"InputDispatcher"线程,它在队列上等待新的输入事件,并且异步地把这些事件分发给应用程序。

      InputReaderThread类与InputDispatcherThread类不共享内部状态,所有的通信都是单向的,从InputReaderThread到InputDispatcherThread。两个类可以通过InputDispatchPolicy进行交互。

      InputManager类从不与Java交互,而InputDispatchPolicy负责执行所有与系统的外部交互,包括调用DVM业务。


    创建流程

    1. 在android_server_InputManager_nativeInit中创建NativeInputManager对象,并保存到gNativeInputManager中;
    2. 在创建NativeInputManager对象时,它会创建EventHub对象<且创建是其成员mNeedToScanDevices的值为true>,然后把刚创建的EventHub对象作为参数创建InputManager对象;
    3. 在创建InputManager对象时,创建InputReader对象,然后把它作为参数创建InputReaderThread;创建InputDispatcher对象,然后把它作为参数创建InputDispatcherThread对象;注意:以上两个线程对象都有自己的threadLoop函数,它将在Thread::_threadLoop中被调用,这个Thread::_threadLoop是线程入口函数,线程在Thread::run中被真正地创建)

    创建InputReader对象

    1. 把EventHub、readerPolicy<实质为NativeInputManager对象>和创建的InputDispatcher对象作为参数创建InputReader对象:mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    2. 在创建InputReader时, 保存EventHub对象到mEventHub中,并创建QueuedInputListener对象并保存在mQueuedListener中

    创建InputDispatcher对象

    1. 把传入的参数dispatcherPolicy<实质为NativeInputManager对象>作为参数创建InputDispatcher对象:mDispatcher = new InputDispatcher(dispatcherPolicy);
    2. 在创建InputDispatcher时,创建了一个looper对象:mLooper = new Looper(false);

    启动流程

    1. 在android_server_InputManager_nativeStart中调用InputManager::start,代码如下:
      result = gNativeInputManager->getInputManager()->start();
    2. 在InputManager::start中,调用mDispatcherThread->run和mReaderThread->run,代码如下:
      result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
      result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);

      在上面的Thread::run中,调用createThreadEtc函数,并以Thread::_threadLoop作为入口函数,以上面的mDispatcherThread或mReaderThread作为userdata创建线程。

      至此InputReader线程和InputDispatcher线程都已经工作,详细信息见Thread::_threadLoop,在此函数中它将调用mDispatcherThread或mReaderThread的threadLoop函数来做真正的事。

    mReaderThread->threadLoop

    bool InputReaderThread::threadLoop() {
        mReader->loopOnce();
        return true;
    }

    mDispatcherThread->threadLoop

    bool InputDispatcherThread::threadLoop() {
        mDispatcher->dispatchOnce();
        return true;
    } 

    EventInput对象关系图 

      

     


    设备操作流程

      从EventHub::getEvents读取的事件数据结构如下:

    struct RawEvent {  
        nsecs_t when;        //事件发生的时间  
        int32_t deviceId;    //产生此事件的设备,比如发送FINISHED_DEVICE_SCAN,不需要填此项  
        int32_t type;        //事件类型(如:DEVICE_ADDED,DEVICE_REMOVED,FINISHED_DEVICE_SCAN)  
        int32_t scanCode;  
        int32_t keyCode;  
        int32_t value;  
        uint32_t flags;  
    };  
    View Code

      读取事件时的调用流程为:

    Thread::_threadLoop->
    
         InputReaderThread::threadLoop->
    
              InputReader::loopOnce->
    
                   EventHub::getEvents->

    打开设备

      在EventHub::getEvents中,当mNeedToScanDevices为true时<当创建EventHub对象时,它就为true>,它将从/dev/input目录下查找所有设备,并进行打开,获取其相关属性,最后加入mDevices列表中。

    EventHub::scanDevicesLocked->

         EventHub::scanDirLocked("/dev/input")->

             EventHub::openDeviceLocked

    打开事件输入设备

      打开事件输入设备,在用户态调用open,则在kernel态中调用evdev_open函数,evdev_open处理流程如下:

    1. 首先从参数inode中获取在evdev_table中的索引,从而获取对应的evdev对象
    2. 创建evdev_client对象,创建此对象时同时为其buffer成员分配对应的内存
    3. 把新创建evdev_client对象添加到client_list链表中
    4. 把client保存在file的private_data中
    5. 调用evdev_open_device->input_open_device->input_dev.open函数打开设备。

    读取输入事件

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

    1. mPendingEventCount:调用epoll_wait时的返回值,如果没有事件,则其值为0;
    2. mPendingEventIndex:当前需要处理的事件索引
    3. mEpollFd:epoll实例,在EventHub::EventHub中初始化此例,所有输入事件通过epoll_wait来获取,每一个事件的数据结构为:struct epoll_event

      :epoll_event只表明某个设备上有事件,并不包含事件内容,具体事件内容需要通过read来读取

    查看设备上是否有事件

      在调用epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis)之后,读到的epoll_event事件保存在mPendingEventItems,总共的事件数保存在mPendingEventCount,当然,在调用epoll_event之前,mPendingEventIndex被清0,直正的事件处理在下面的代码中。

            // Grab the next input event.
            bool deviceChanged = false;
            while (mPendingEventIndex < mPendingEventCount) {
                const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
                if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
                    if (eventItem.events & EPOLLIN) {
                        mPendingINotify = true;
                    } else {
                        LOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
                    }
                    continue;
                }
    
                if (eventItem.data.u32 == EPOLL_ID_WAKE) {
                    if (eventItem.events & EPOLLIN) {
                        LOGV("awoken after wake()");
                        awoken = true;
                        char buffer[16];
                        ssize_t nRead;
                        do {
                            nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
                        } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
                    } else {
                        LOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
                                eventItem.events);
                    }
                    continue;
                }
    
                ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
                if (deviceIndex < 0) {
                    LOGW("Received unexpected epoll event 0x%08x for unknown device id %d.",
                            eventItem.events, eventItem.data.u32);
                    continue;
                }
    
                Device* device = mDevices.valueAt(deviceIndex);
                if (eventItem.events & EPOLLIN) {
                    int32_t readSize = read(device->fd, readBuffer,
                            sizeof(struct input_event) * capacity);
                    if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
                        // Device was removed before INotify noticed.
                        LOGW("could not get event, removed? (fd: %d size: %d bufferSize: %d capacity: %d errno: %d)
    ",
                             device->fd, readSize, bufferSize, capacity, errno);
                        deviceChanged = true;
                        closeDeviceLocked(device);
                    } else if (readSize < 0) {
                        if (errno != EAGAIN && errno != EINTR) {
                            LOGW("could not get event (errno=%d)", errno);
                        }
                    } else if ((readSize % sizeof(struct input_event)) != 0) {
                        LOGE("could not get event (wrong size: %d)", readSize);
                    } else {
                        int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
    
                        size_t count = size_t(readSize) / sizeof(struct input_event);
                        for (size_t i = 0; i < count; i++) {
                            const struct input_event& iev = readBuffer[i];
                            LOGV("%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);
    
    #ifdef HAVE_POSIX_CLOCKS
                            // Use the time specified in the event instead of the current time
                            // so that downstream code can get more accurate estimates of
                            // event dispatch latency from the time the event is enqueued onto
                            // the evdev client buffer.
                            //
                            // The event's timestamp fortuitously uses the same monotonic clock
                            // time base as the rest of Android.  The kernel event device driver
                            // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().
                            // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere
                            // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a
                            // system call that also queries ktime_get_ts().
                            event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
                                    + nsecs_t(iev.time.tv_usec) * 1000LL;
                            LOGV("event time %lld, now %lld", event->when, now);
    #else
                            event->when = now;
    #endif
                            event->deviceId = deviceId;
                            event->type = iev.type;
                            event->scanCode = iev.code;
                            event->value = iev.value;
                            event->keyCode = AKEYCODE_UNKNOWN;
                            event->flags = 0;
                            if (iev.type == EV_KEY && device->keyMap.haveKeyLayout()) {
                                status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code,
                                            &event->keyCode, &event->flags);
                                LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d
    ",
                                        iev.code, event->keyCode, event->flags, err);
                            }
                            event += 1;
                        }
                        capacity -= count;
                        if (capacity == 0) {
                            // The result buffer is full.  Reset the pending event index
                            // so we will try to read the device again on the next iteration.
                            mPendingEventIndex -= 1;
                            break;
                        }
                    }
                } else {
                    LOGW("Received unexpected epoll event 0x%08x for device %s.",
                            eventItem.events, device->identifier.name.string());
                }
            }
    View Code

    读取设备上真正的事件

      epoll_wait只是告诉我们Device已经有事件了,让我们去读,真正读取设备输入事件的代码如上,其流程如下:

    1. 根据eventItem.data.u32获取设备索引,从而获取对应的Device
    2. 从device->fd中读取input_event事件。

      read(device->fd, readBuffer, sizeof(struct input_event) * capacity);这些input_event是由各个注册的input_device报告给input子系统的。

      至此,事件已经读取到用户态,现在看看EventHub怎么处理这些事件?

     


    处理输入事件

      首先通过epoll_wait查看哪些设备有事件,然后通过read从有事件的设备中读取事件,现在事件已经读取到用户态,且数据结构为input_event,保存在EventHub::getEvents的readBuffer中。

      下面就看看这些事件下一步的东家是谁?

          1)首先把input_event的信息填入RawEvent中,其相关代码如下:

    #ifdef HAVE_POSIX_CLOCKS
                            // Use the time specified in the event instead of the current time
                            // so that downstream code can get more accurate estimates of
                            // event dispatch latency from the time the event is enqueued onto
                            // the evdev client buffer.
                            //
                            // The event's timestamp fortuitously uses the same monotonic clock
                            // time base as the rest of Android.  The kernel event device driver
                            // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().
                            // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere
                            // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a
                            // system call that also queries ktime_get_ts().
                            event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
                                    + nsecs_t(iev.time.tv_usec) * 1000LL;
                            LOGV("event time %lld, now %lld", event->when, now);
    #else
                            event->when = now;
    #endif
                            event->deviceId = deviceId;
                            event->type = iev.type;
                            event->scanCode = iev.code;
                            event->value = iev.value;
                            event->keyCode = AKEYCODE_UNKNOWN;
                            event->flags = 0;
                            if (iev.type == EV_KEY && device->keyMap.haveKeyLayout()) {
                                status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code,
                                            &event->keyCode, &event->flags);
                                LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d
    ",
                                        iev.code, event->keyCode, event->flags, err);
                            }
    View Code

      2)如果是input_event的类型为EV_KEY,则需要调用device->keyMap.keyLayoutMap->mapKey函数把iput_event.code映射为RawEvent.keyCode。

      相关数据结构关系如下图所示:

      

      至此,EventHub::getEvents读取事件的任务已经完成。

      下面看看RawEvent的命运如何呢?

    InputReader::loopOnce如何处理RawEvent?

      先温习一下读取事件时的调用流程为:

    Thread::_threadLoop->

         InputReaderThread::threadLoop->

              InputReader::loopOnce->

                   EventHub::getEvents->

      在InputReader::loopOnce中,当调用EventHub->getEvents获取到RawEvent之后,调用InputReader::processEventsLocked来处理这些事件,然后调用mQueuedListener->flush()把这些队列中的事件发送到Listener。

    InputReader::processEventsLocked

      在InputReader::processEventsLocked主要分两步处理:

    1. 处理来自于事件驱动设备的事件(processEventsForDeviceLocked)
    2. 处理设备增加、删除和修改事件

      增加事件的流程为:为一个新增的Device创建一个InputDevice,并增加到InputReader::mDevices中;根据新增加设备的class类别,创建对应的消息转换器(InputMapper),然后此消息转换器加入InputDevice::mMappers中。消息转换器负责把读取的RawEvent转换成特定的事件,以供应用程序使用。

      InputMapper关系如下图所示

      

    EventHub与InputReader各自管理功能
    1. EventHub管理一堆Device,每一个Device与Kernel中一个事件输入设备对应
    2. InputReader管理一堆InputDevice,每一个InputDevice与EventHub中的Device对应
    3. InputDevice管理一些与之相关的InputMapper,每一个InputMapper与一个特定的应用事件相对应,如:SingleTouchInputMapper。
    InputReader::mQueuedListener->flush()

      

      processEventsLocked已经把来自于事件设备的事件煮熟之后放入到各种NotifyArgs(如NotifyMotionArgs)之中,然后把这些各种NotifyArgs加入InputReader::mQueuedListener::mArgsQueue链表中。本Flush函数就是要把mArgsQueue中的所有NotifyArgs进行处理。

    void QueuedInputListener::flush() {  
        size_t count = mArgsQueue.size();  
        for (size_t i = 0; i < count; i++) {  
            NotifyArgs* args = mArgsQueue[i];  
            args->notify(mInnerListener);  
            delete args;  
        }  
        mArgsQueue.clear();  
    }  
    View Code

      调用链表中每个NotifyArgs的notify函数,且有一个有意思的参数 mInnerListener,这个参数在前面多次提到过,它是在创建mQueuedListener时提供的,它其实就是InputManager中的mDispatcher,前面一直在InputReader中打转转,现在终于看到InputDispatcher登场了,说明事件很快就可以谢幕了。

      再向下看一下,这么多类NotifyArgs,为描述方便,以NotifyMotionArgs为例,其代码为: 

    void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {  
        listener->notifyMotion(this);  
    }  

      下面就看看InputDispatcher(mDispatcher)的notifyMotion函数做了些什么。

      这个InputDispatcher::notifyMotion(const NotifyMotionArgs* args)可就不简单了。

      在InputDispatcher::notifyMotion中:

    1. 根据NotifyMotionArgs提供的信息,构造一个MotionEvent,再调用mPolicy->filterInputEvent看是否需要丢弃此事件,如果需要丢弃则马上返加。其中mPolicy为NativeInputManager实例,在构造InputDispatcher时提供的参数。
    2. 对于AMOTION_EVENT_ACTION_UP或AMOTION_EVENT_ACTION_DOWN事件,则直接根据NotifyMotionArgs提供的信息,构造一个MotionEntry。
    3. 调用InputDispatcher::enqueueInboundEventLocked把新构造的MotionEntry添加到InputDispatcher::mInboundQueue,并返回是否需要唤醒mLooper<向pipe中写入数据>的标识。

      以上操作都是在InputReader线程中完成的,现在应该InputDispatcher线程开始工作了。

     


    分发输入事件

      InputDispatcherThread主循环如下:

    Thread::_threadLoop->

       InputDispatcherThread::threadLoop->

          mDispatcher->dispatchOnce(InputDispatcher::dispatchOnce)->

              dispatchOnceInnerLocked then

              mLooper->pollOnce

      先看看简单的mLooper->pollOnce

    mLooper->pollOnce 

      其功能为等待超时或被pipe唤醒(InputReader线程调用InputDispatcher::notifyMotion时, InputDispatcher::notifyMotion根据情况调用mLooper->wake)。

      其调用流程如下:

    mLooper->pollOnce(int timeoutMillis)->

      Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData)->

    dispatchOnceInnerLocked  

    1. 从mInboundQueue从中依次取出EventEntry<MotionEntry的基类>,
    2. 调用InputDispatcher::dispatchMotionLocked处理此MotionEntry
    3. 调用InputDispatcher::dispatchEventToCurrentInputTargetsLocked

      对于InputDispatcher::mCurrentInputTargets中的每一个InputTarget,并获取对应的Connection,调用InputDispatcher::prepareDispatchCycleLocked,

      其相关代码如下:

    for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {  
            const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);  
      
            ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);  
            if (connectionIndex >= 0) {  
                sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);  
                prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,  
                        resumeWithAppendedMotionSample);  
            } else {  
    #if DEBUG_FOCUS  
                LOGD("Dropping event delivery to target with channel '%s' because it "  
                        "is no longer registered with the input dispatcher.",  
                        inputTarget.inputChannel->getName().string());  
    #endif  
            }  
        }
    View Code

    InputDispatcher::prepareDispatchCycleLocked

    1. 调用enqueueDispatchEntryLocked创建DispatchEntry对象,并把它增加到Connection::outboundQueue队列中。
    2. 调用activateConnectionLocked把当前Connection增加到InputDispatcher::mActiveConnections链表中
    3. 调用InputDispatcher::startDispatchCycleLocked,接着它调用Connection::inputPublisher.publishMotionEvent来发布事件到ashmem buffer中调用Connection::inputPublisher.sendDispatchSignal发送一个dispatch信号到InputConsumer通知它有一个新的消息到了,快来消费吧!

      至此,整个Android事件处理系统就介绍完了。


    分析总结

      下面是我做"电视棒远程万能遥控器"项目时写的分析,只列出了关键步骤

    public class VirtualService
    {
        private final static String logString ="VirtualServices";
        
        /*1.最先执行静态模块方法*/
        static
        {
            /*加载虚拟键盘设备JNI库文件*/
            /*Android.mk:LOCAL_MODULE:= libvirtual_key_jni*/
            Log.i(logString, "load virtual_key_jni.so");
            System.loadLibrary("virtual_key_jni");
            
            /*加载虚拟鼠标设备JNI库文件*/
            /*Android.mk:LOCAL_MODULE:= libvirtual_mouse_jni*/
            Log.i(logString, "load virtual_mouse_jni.so");
            System.loadLibrary("virtual_mouse_jni");
    
        }
        /*2.然后执行构造函数方法*/
        public VirtualService()
        {
            /*初始化虚拟键盘设备*/
            Log.i(logString, "init virtual_key Device");
            virtual_key_init();
            
            /*打开虚拟鼠标设备*/
            /*open('/dev/virtual_dev', O_RDWR)*/
            Log.i(logString, "open virtual_mouse Device");
            virtual_mouse_open();
        }
        
        /*
         * 提供JNI本地方法的向上接口
         * @arg :按键码,code
         * */
        public String key_write(int arg)
        {
            virtual_key_write(arg);
            Log.i(logString, "KEY" +String.valueOf(arg) +" writed");
            
            return "KEY " + String.valueOf(arg) + "writed";
        }
    
        /*初始化虚拟键盘设备*/
        private static native boolean virtual_key_init();
    
        /* @author:成鹏致远
         * Android层调用virtual_key_write方法,此方法为JNI向上提供的接口
         * virtual_key_write方法被映射到JNI层的virtualkey_write方法
         * JNI层的virtualkey_write方法调用HAL层的write_key
         * HAL层的write_key是一个函数指针,指向HAL层的virtualkey_write方法
         * 在HAL层的virtualkey_write方法中,通过系统调用write进入内核,通知虚拟设备驱动
         * 然后进入到驱动的virtual_dev_write方法,通过input_report_key上报事件,到达linux输入子系统事件处理层,唤醒read
         * 同时Android事件系统的WMS::InputManager->InputReaderThread->loopOnce->getEvent->epoll_wait通过轮询监测到事件发生
         * 然后通过系统调用read从device->fd中读取input_event,即刚才发生的事件
         * 至此就完成了Linux输入子系统与Android输入子系统的完整对接,事件已经传递到Android输入子系统中
         * 将事件保存到getEvent的readBuffer结构中,事件本身为input_event结构体
         * 将input_event填充到RawEvent中
         * 获取到RawEvent之后,调用InputReader->processEventsLocked来处理事件
         * 根据事件设备的class类别,调用对应的消息转换器(InputDevice->InputMapper)如evice->keyMap.keyLayoutMap->mapKey
         * InputMapper把读取的RawEvent转换成特定的事件
         * 将特定的事件放入到各种NotifyArgs(如NotifyMotionArgs)之中,然后加入InputReader::mQueuedListener::mArgsQueue链表中
         * 调用mQueuedListener->flush()把这些队列中的事件发送到Listener
         * 然后调用链表中每个NotifyArgs的notify函数,然后经过一些预处理(比如是否丢弃)
         * 再调用InputDispatcher::enqueueInboundEventLocked把新构造的事件添加到InputDispatcher::mInboundQueue队列中
         * 然后通知此事件到InputManager中的mDispatcher
         * 至此就顺利的从InputReader线程跳转到InputDispatcher线程
         * 然后WMS::InputManager->InputDispatcherThread->dispatchOnce->dispatchOnceInnerLocked->pollOnce被唤醒
         * dispatchOnceInnerLocked从mInboundQueue从中依次取出EventEntry,即刚才添加进来的事件
         * InputDispatcher调用不同的dispatch处理函数来处理此事件
         * 然后就要准备DispatchEntry对象了,即通知Android的UI的原始事件结构
         * 首先调用enqueueDispatchEntryLocked创建DispatchEntry对象,并把它增加到Connection::outboundQueue队列中
         * 接着调用activateConnectionLocked把当前Connection增加到InputDispatcher::mActiveConnections链表中
         * 然后调用InputDispatcher::startDispatchCycleLocked->Connection::inputPublisher.publishMotionEvent,发布事件到ashmem buffer中
         * 至此事件已经放在Android的共享内存了,接着就需要通知DecroWindows(Android第四层Activity)的viewRoot(一个Hander)有新事件产生
         * 调用Connection::inputPublisher.sendDispatchSignal发送一个dispatch信号到InputConsumer通知它有一个新的事件消息到了(通过管道通信)
         * 然后viewRoot的inputChannel接收管道便收到这个事件消息
         * 调用Connection::inputCustomer去共享内存中取得这个事件消息
         * inputCustomer取得这个事件,将这个事件发送到具有当前焦点窗口的viewRoot中
         * viewRoot更新DecroWindows,然后就是我们看到的窗口界面事件响应了
         * */
        
        /*虚拟键盘写操作方法*/
        /*{ "virtual_key_write", "(I)I",(void*)virtualkey_write }*/
        private static native int virtual_key_write(int arg);
        
        /*声明虚拟鼠标设备向上提供的JNI本地方法*/
        /*{"virtual_mouse_open", "()I" , (void*)virtual_mouse_open}*/
        private static native int virtual_mouse_open();
        /*{"virtual_mouse_write", "(IIII)I" , (void *)virtual_mouse_write}*/
        public native int virtual_mouse_write(int type, int x, int y, int size);
    }
    View Code

     参考文章

      http://blog.csdn.net/myarrow

  • 相关阅读:
    ubuntu删除django和安装django
    linux shell 统计文件单词出现次数
    linux shell $$、$!特殊符号
    linux安装zabbix需要php两个模块php-bcmach与php-mbstring
    linux 源码编译与卸载
    Job for dhcpd.service failed because the control process exited with error code. See "systemctl status dhcpd.service" and "journalctl -xe" for details.问题
    Linux中部署DNS分离解析技术
    NFS网络文件系统服务搭建
    Samba服务搭建
    Linux RAID磁盘阵列技术
  • 原文地址:https://www.cnblogs.com/lcw/p/3374466.html
Copyright © 2011-2022 走看看