美文网首页
Touch 事件原理分析 (一)

Touch 事件原理分析 (一)

作者: 莫库施勒 | 来源:发表于2019-02-19 14:54 被阅读0次

    文章内容主要从 https://segmentfault.com/a/1190000011826846 而来

    1,InputManagerService

    Android Framework 层的 service,大部分都是在 SystemServer 进程中创建,InputManagerService 即是如此,创建完 Service 后,便把它加入到 ServiceManager 中,并同时设置了 InputMonitor,如下

    inputManager = new InputManagerService(context);
    WindowManagerService wm = WindowManagerService.main(context, inputManager,
                        mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                        !mFirstBoot, mOnlyCore, new PhoneWindowManager());
    ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                        DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
    ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
                        /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
    mActivityManagerService.setWindowManager(wm);
    inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
    inputManager.start();
    

    看一下InputManagerservice 的构造函数

        public InputManagerService(Context context) {
            this.mContext = context;
            this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
    
            mUseDevInputEventForAudioJack =
                    context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
            Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
                    + mUseDevInputEventForAudioJack);
            mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
    
            String doubleTouchGestureEnablePath = context.getResources().getString(
                    R.string.config_doubleTouchGestureEnableFile);
            mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :
                new File(doubleTouchGestureEnablePath);
    
            LocalServices.addService(InputManagerInternal.class, new LocalService());
        }
    

    nativeInit 的实现如下

    static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
            jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
        sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
        if (messageQueue == NULL) {
            jniThrowRuntimeException(env, "MessageQueue is not initialized.");
            return 0;
        }
    
        NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
                messageQueue->getLooper());
        im->incStrong(0);
        return reinterpret_cast<jlong>(im);
    }
    

    首先是获取 MessageQueue, 接着创建 NativeInputManager ,NativeInputManager 的实现如下

    NativeInputManager::NativeInputManager(jobject contextObj,
            jobject serviceObj, const sp<Looper>& looper) :
            mLooper(looper), mInteractive(true) {
        JNIEnv* env = jniEnv();
         ...
    
        mInteractive = true;
        sp<EventHub> eventHub = new EventHub();
        mInputManager = new InputManager(eventHub, this, this);
    }
    

    NativeInputManager 中创建了 EventHub,并利用 EventHub 创建了InputManger, InputManager 实现如下

    InputManager::InputManager(
            const sp<EventHubInterface>& eventHub,
            const sp<InputReaderPolicyInterface>& readerPolicy,
            const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
        mDispatcher = new InputDispatcher(dispatcherPolicy);
        mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
        initialize();
    }
    

    代码中创建了 InputDispatcher,接着创建了 InputReader,接着是 initialize 方法

    void InputManager::initialize() 
    {
        mReaderThread = new InputReaderThread(mReader);
        mDispatcherThread = new InputDispatcherThread(mDispatcher);
    }
    

    分别将 reader 和 dispatcher 封装到相应的 Thread 中,此时对 SystemServer 来说,InputManager 基本创建完成,接着便是 start Service ,对应的 native 方法便是 nativeStart(mPtr)

    static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
        NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    
        status_t result = im->getInputManager()->start();
    }
    
    status_t InputManager::start() {
        status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
        result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
    }
    

    2,小结

    整体流程如下


    整体流程图

    InputManger 通过 InputReaderThread 读和处理未加工的输入事件然后分发到 DispatcherThread 队列中, InputDispatcherThread 将接收的队列发送给相应的应用程序

    3,epoll

    这里首先了解一下 epoll,之前我们处理输入流的时候,我们会对每一个流进行遍历,然后检测到有修改的数据,将其取出来,这其中存在大量的资源消耗,尤其是在流比较多的时候,epoll 便在这里优化,当无数据的时候会阻塞队列,当有数据的时候,只将其中有变化的进行分发。
    epoll更详细的分析

    4,EventHub

    在 NativeInputManager 中,曾经创建的EventHub,并作为参数传递给了 InputManger。EventHub是将不同来源的消息转化为统一类型并交给上层处理。先查看其构造函数

    EventHub::EventHub(void) :
          mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
          mOpeningDevices(0), mClosingDevices(0),
          mNeedToSendFinishedDeviceScan(false),
          mNeedToReopenDevices(false), mNeedToScanDevices(true),
          mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
    
        acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
        //创建一个epoll句柄
        mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    
        mINotifyFd = inotify_init();
    
        //监视dev/input目录的变化删除和创建变化
        int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
    
        struct epoll_event eventItem;
        memset(&eventItem, 0, sizeof(eventItem));
        eventItem.events = EPOLLIN;
        eventItem.data.u32 = EPOLL_ID_INOTIFY;
    
        //把inotify的句柄加入到epoll监测
        result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
    
        //创建匿名管道
        int wakeFds[2];
        result = pipe(wakeFds);
        mWakeReadPipeFd = wakeFds[0];
        mWakeWritePipeFd = wakeFds[1];
    
        //将管道的读写端设置为非阻塞
        result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
        result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
    
        eventItem.data.u32 = EPOLL_ID_WAKE;
    
        //将管道的读端加入到epoll监测
        result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
    
        int major, minor;
        getLinuxRelease(&major, &minor);
        // EPOLLWAKEUP was introduced in kerel 3.5
        mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5);
    }
    

    其中创建了 epoll 句柄、inotify 句柄、匿名管道(非阻塞),inotify负责监控目录和文件的变化,这里监控的是/dev/input 目录。
    EventHub 负责监控,相关事件的处理由 ReaderThread 处理。ReaderThread 是继承Android 的 Thread

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

    这个方法返回 true , 表示 threadLoop会被循环调用,也就是 loopOnce 会被循环调用,下面是 loopOnce

    void InputReader::loopOnce() {
          .....
        //从EventHub中获取事件
        size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    
        { // acquire lock
            AutoMutex _l(mLock);
            mReaderIsAliveCondition.broadcast();
            //如果读到数据,处理事件数据
            if (count) {
                processEventsLocked(mEventBuffer, count);
            }
    
            if (mNextTimeout != LLONG_MAX) {
                nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
                if (now >= mNextTimeout) {
    #if DEBUG_RAW_EVENTS
                    ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
    #endif
                    mNextTimeout = LLONG_MAX;
                    timeoutExpiredLocked(now);
                }
            }
    
            if (oldGeneration != mGeneration) {
                inputDevicesChanged = true;
                getInputDevicesLocked(inputDevices);
            }
        } // release lock
    
       //将排队的事件队列发送给监听者,实际上这个监听者就是Input dispatcher
        mQueuedListener->flush();
    }
    

    我们要分析的是其中的两个方法 getEvents 和 processEventsLocked

    size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
        RawEvent* event = buffer;
        size_t capacity = bufferSize;
         for(;;) {
            ....
          while (mPendingEventIndex < mPendingEventCount) {
             const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
            .....
           ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
         if (eventItem.events & EPOLLIN) {
             int32_t readSize = read(device->fd, readBuffer,
                            sizeof(struct input_event) * capacity);
            if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
               // 设备被移除,关闭设备
               deviceChanged = true;
               closeDeviceLocked(device);
             } else if (readSize < 0) {
                 //无法获得事件
                 if (errno != EAGAIN && errno != EINTR) {
                     ALOGW("could not get event (errno=%d)", errno);
                 }
             } else if ((readSize % sizeof(struct input_event)) != 0) {
                //获得事件的大小非事件类型整数倍
                ALOGE("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++) {
                   struct input_event& iev = readBuffer[i];
                   if (iev.type == EV_MSC) {
                     if (iev.code == MSC_ANDROID_TIME_SEC) {
                         device->timestampOverrideSec = iev.value;
                         continue;
                      } else if (iev.code == MSC_ANDROID_TIME_USEC) {
                         device->timestampOverrideUsec = iev.value;
                         continue;
                      }
                   }
                  //事件时间相关计算,时间的错误可能会导致ANR和一些bug。这里采取一系列的防范 
                   .........
                 event->deviceId = deviceId;
                 event->type = iev.type;
                 event->code = iev.code;
                 event->value = iev.value;
                 event += 1;
                 capacity -= 1;
              }
            if (capacity == 0) {
              //每到我们计算完一个事件,capacity就会减1,如果为0。则表示  结果缓冲区已经满了,
          //需要重置开始读取时间的索引值,来读取下一个事件迭代                    
               mPendingEventIndex -= 1;
               break;
          }
     } 
        //表明读到事件了,跳出循环
        if (event != buffer || awoken) {
                break;
         }
         mPendingEventIndex = 0;
         int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
           if (pollResult == 0) {
              mPendingEventCount = 0;
              break;
           }
          //判断是否有事件发生
           if (pollResult < 0) {
              mPendingEventCount = 0;
            } else {
                //产生的事件的数目
              mPendingEventCount = size_t(pollResult);
            }
        }
        //产生的事件数目
        return event - buffer;
    }
    
    void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
        for (const RawEvent* rawEvent = rawEvents; count;) {
            int32_t type = rawEvent->type;
            size_t batchSize = 1;
            if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
                int32_t deviceId = rawEvent->deviceId;
                while (batchSize < count) {
                    if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
                            || rawEvent[batchSize].deviceId != deviceId) {
                        break;
                    }
                    batchSize += 1;
                }
                processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
            } else {
                switch (rawEvent->type) {
                case EventHubInterface::DEVICE_ADDED:
                    addDeviceLocked(rawEvent->when, rawEvent->deviceId);
                    break;
                case EventHubInterface::DEVICE_REMOVED:
                    removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
                    break;
                case EventHubInterface::FINISHED_DEVICE_SCAN:
                    handleConfigurationChangedLocked(rawEvent->when);
                    break;
                default:
                    ALOG_ASSERT(false); // can't happen
                    break;
                }
            }
            count -= batchSize;
            rawEvent += batchSize;
        }
    }
    

    getEvents 方法会进行一些新增设备和移除设备的更新操作。至于点击事件是通过指针参数 RawEvent, 其作为起始地址记录事件,在循环体中,处理获取时间、检测相关设备类型、读取事件,如果检测到事件,则跳出循环。更新 mPendingEventCount 和 mPendingEventIndex 来控制事件的读取,epoll_wait 来得到事件的来源。
    在 looperOnce 获取到事件后,就会调用 processEventsLocked。其负责事件添加、设备移除等,事件相关还是 processEventsForDeviceLocked 方法,根据事件获取相应的设备类型,并交给相应的设备处理,即 InputMapper 。


    InputMapper及其子类

    在这里,事件会被 mapper->process 处理

    void TouchInputMapper::process(const RawEvent* rawEvent) {
        mCursorButtonAccumulator.process(rawEvent);
        mCursorScrollAccumulator.process(rawEvent);
        mTouchButtonAccumulator.process(rawEvent);
    
        if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
            sync(rawEvent->when);
        }
    }
    void TouchInputMapper::sync(nsecs_t when) {
        .....
        processRawTouches(false /*timeout*/);
    }
    void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
          ....
        dispatchMotion();
          ....
    }
    void TouchInputMapper::dispatchMotion() {
       ....
       NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,
                action, actionButton, flags, metaState, buttonState, edgeFlags,
                mViewport.displayId, pointerCount, pointerProperties, pointerCoords,
                xPrecision, yPrecision, downTime);
        getListener()->notifyMotion(&args);
    }
    
    InputListenerInterface* InputReader::ContextImpl::getListener() {
        return mReader->mQueuedListener.get();
    }
    
    void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
        mArgsQueue.push(new NotifyMotionArgs(*args));
    }
    

    此时数据被加入到参数队列中,此时回到 loopOnce 中,调用 QueuedInputListener 的 flush 方法

    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();
    }
    
    void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
        listener->notifyMotion(this);
    }
    
    void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
        .....
       MotionEvent event;
       event.initialize(args->deviceId, args->source, args->action, args->actionButton,
                        args->flags, args->edgeFlags, args->metaState, args->buttonState,
                        0, 0, args->xPrecision, args->yPrecision,
                        args->downTime, args->eventTime,
                        args->pointerCount, args->pointerProperties, args->pointerCoords);
        ....
      MotionEntry* newEntry = new MotionEntry(args->eventTime,
                    args->deviceId, args->source, policyFlags,
                    args->action, args->actionButton, args->flags,
                    args->metaState, args->buttonState,
                    args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,
                    args->displayId,
                    args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0);
       needWake = enqueueInboundEventLocked(newEntry);
        ....
       if (needWake) {
          mLooper->wake();
       }
    }
    

    在 notifyMotion 中将参数包装成 MotionEntry,加入到 enqueueInboundEventLocked 中,然后唤醒 looper。
    Dispatcher 下的 dispatchOnce

    bool InputDispatcherThread::threadLoop() {
        mDispatcher->dispatchOnce();
        return true;
    }
    void InputDispatcher::dispatchOnce() {
        ...
       dispatchOnceInnerLocked(&nextWakeupTime);
        ...
    }
    void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
       ....
       mPendingEvent = mInboundQueue.dequeueAtHead();
       ....
       switch (mPendingEvent->type) {
            case EventEntry::TYPE_MOTION: {
            MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
            if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
                dropReason = DROP_REASON_APP_SWITCH;
            }
            if (dropReason == DROP_REASON_NOT_DROPPED
                    && isStaleEventLocked(currentTime, typedEntry)) {
                dropReason = DROP_REASON_STALE;
            }
            if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
                dropReason = DROP_REASON_BLOCKED;
            }
            done = dispatchMotionLocked(currentTime, typedEntry,
                    &dropReason, nextWakeupTime);
            break;
        }
        ....
       }
    }
    void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
        EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
        ....
        pokeUserActivityLocked(eventEntry);
        .....
        for (size_t i = 0; i < inputTargets.size(); i++) {
            const InputTarget& inputTarget = inputTargets.itemAt(i);
    
            ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
            if (connectionIndex >= 0) {
                sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
                prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
            } 
        }
    }
    

    prepareDispatchCycleLocked 调用 enqueueDispatchEntriesLocked 调用 startDispatchCycleLocked

    void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
            const sp<Connection>& connection) {
       EventEntry* eventEntry = dispatchEntry->eventEntry;
        ....
       switch (eventEntry->type) {
          ....
        case EventEntry::TYPE_MOTION: {
          status = connection->inputPublisher.publishMotionEvent( ....);    
          break;
        }
        ....
       }
        ...
    }
    
    status_t InputPublisher::publishMotionEvent(...) {
      ....
      InputMessage msg;
      msg.header.type = InputMessage::TYPE_MOTION;
      msg.body.motion.seq = seq;
      msg.body.motion.deviceId = deviceId;
      msg.body.motion.source = source;
      msg.body.motion.action = action;
      msg.body.motion.actionButton = actionButton;
      msg.body.motion.flags = flags;
      msg.body.motion.edgeFlags = edgeFlags;
      msg.body.motion.metaState = metaState;
      msg.body.motion.buttonState = buttonState;
      msg.body.motion.xOffset = xOffset;
      msg.body.motion.yOffset = yOffset;
      msg.body.motion.xPrecision = xPrecision;
      msg.body.motion.yPrecision = yPrecision;
      msg.body.motion.downTime = downTime;
      msg.body.motion.eventTime = eventTime;
      msg.body.motion.pointerCount = pointerCount;
      for (uint32_t i = 0; i < pointerCount; i++) {
          msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
          msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
      }
        return mChannel->sendMessage(&msg);
    }
    
    具体流程图 整体流程图

    ReaderThread 开启后会从EventHub轮训获取时间,获取事件后,经过一系列的封装,通过 InputChannel 发送出去

    Touch 事件原理分析 (下)

    相关文章

      网友评论

          本文标题:Touch 事件原理分析 (一)

          本文链接:https://www.haomeiwen.com/subject/hdmxyqtx.html