当前位置: 首页 > news >正文

Android7 Input(四)InputReader

概述

本文主要描述了Android Input框架中的InputReader的功能,InputReader模块的功能,总结成一句话就是InputReader获取输入设备的事件并将事件进行加工处理,然后传递给QueuedInputListener,最终QueuedInputListener将事件传递给InputDispatcher模块处理,事件处理的方向就是:

InputReader -> QueuedInputListener -> InputDispatcher

本文涉及的源码路径

frameworks/native/services/inputflinger/EventHub.cpp

frameworks/native/services/inputflinger/EventHub.h

frameworks/native/services/inputflinger/InputReader.cpp

frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

InputReader的初始化

InputReader对象由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();
}

继续深入InputReader类的构造函数,如下所示:

InputReader::InputReader(const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& policy,
        const sp<InputListenerInterface>& listener) :
        ......
        mConfigurationChangesToRefresh(0) {
    // 初始化事件监听器
    mQueuedListener = new QueuedInputListener(listener);
    ......
}

Inputreader的构造函数比较简单,在这里我们关注构造函数的第一个参数和第三个参数。

eventHub: 获取事件来源的对象,InputReader通过EventHub获取输入设备上报的事件;

listener: InputReader处理完事件后的传递目的地,后面的章节有描述处理流程;

InputReader的运行

在InputManager的初始化过程中,会创建一个InputReadThread线程,如下所示:

void InputManager::initialize() {
    /* 创建事件获取线程 */
    mReaderThread = new InputReaderThread(mReader);
    ......
}

InputManager初始化完成开始运行的时候会调用线程的运行方法,对InputReader来说最终调用InputReader::loopOnce, 如下所示:

void InputReader::loopOnce() {
    ......
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

    { // acquire lock
       ......
        // 处理获取到的intput事件
        if (count) {
            processEventsLocked(mEventBuffer, count);
        }
        ......
    } // release lock
   ......
    // 刷新监听队列,将InputReader的事件处理结果,传递给InputDispatcher处理
    mQueuedListener->flush();
}

InputReader的事件获取

InputReader在Thread线程中调用EventHub的getEvents方法获取输入事件,如下所示:

 size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

该方法将获取到input事件保存在mEventBuffer中,并返回获取到的事件个数

InputReader的事件处理

InputReader从EventHub获取到输入事件后,调用processEventsLocked方法,该方法的核心代码如下所示:

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;
            }
#if DEBUG_RAW_EVENTS
            ALOGD("BatchSize: %d Count: %d", batchSize, count);
#endif
            /**
             * 处理deviceID设备的上报的事件
             * @batchSize: 事件个数
             */
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {
            // 处理EventHub上报的输入设备增加和删除
            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;
    }
}

processEventsLocked:方法主要完成如下的功能:

1、处理系统已经存在的输入设备上报的事件;

2、处理系统输入设备的增加和删除事件;

设备添加和删除

本章节主要描述当系统增加一个输入设备时的InputReader处理流程,对于系统设备的删除和扫描的处理流程与增加处理流程类似,这里不再描述。下面我们从系统增加输入设备的处理流程进行描述。系统增加输入设备调用addDeviceLocked方法,addDeviceLocked的核心代码如下所示:

void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    // 设备已经增加进来了,直接返回
    if (deviceIndex >= 0) {
        ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
        return;
    }

    ......
    // 创建InputDevice设备管理对象
    // @deviceId: 设备ID
    // @classes: 输入设备类型
    InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
    device->configure(when, &mConfig, 0);
    device->reset(when);
    ......
    mDevices.add(deviceId, device);
    ......
}

addDeviceLocked主要完成如下功能:

a、检测该设备是否已经增加过了,如果是,直接返回;

b、根据输入设备的类型,创建InputDevice设备;

c、将创建的InputDevice设备加入到InputReader设备管理容器中;

接着我们分析createDeviceLocked方法,后续我们主要分析多点触控设备的事件处理,因此我们只保留与多点触控相关的核心实现如下所示:

InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
        const InputDeviceIdentifier& identifier, uint32_t classes) {
    InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
            controllerNumber, identifier, classes);

   ......
    // 多点触控
    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
        device->addMapper(new MultiTouchInputMapper(device));
    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
        device->addMapper(new SingleTouchInputMapper(device));
    }

    return device;
}

createDeviceLocked方法的核心功能比较简单:

a、创建InputDevice设备;

b、根据输入设备的类型安装对应的事件处理器,Android系统可以处理的输入设备种类很多,对于每一类输入设备Android系统都抽象出了一个事件处理器处理该类设备上报的输入事件,对于多点触控设备来说,就是安装MultiTouchInputMapper事件处理器;

c、返回InputDevice设备;

d、这里没有描述InputMapper的实现机制,它的主要作用就是定义事件处理的接口,比如鼠标设备的处理使用CursorButtonAccumulator类,单点触控类设备使用SingleTouchInputMapper,多点触控设备使用MultiTouchInputMapper等,感兴趣的同学可以阅读Android源码去分析它们之间的关系;

设备上报事件

设备上报的输入事件处理函数是processEventsForDeviceLocked,该方法的核心实现,如下所示:

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
   ......
    device->process(rawEvents, count);
}

进一步调用InputDevice中的process方法,实现如下:

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
   ......
    size_t numMappers = mMappers.size();
    for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
        ......
        } else {
            for (size_t i = 0; i < numMappers; i++) {
                InputMapper* mapper = mMappers[i];
                mapper->process(rawEvent);
            }
        }
    }
}

该方法的核心功能就是遍历每一个事件,调用设备自己注册的事件处理器进行处理,对于多点触控设备来说,调用如下的方法:

void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
    TouchInputMapper::process(rawEvent);
    /* 处理多点触控坐标信息 */
    mMultiTouchMotionAccumulator.process(rawEvent);
}

继续调用TouchInputMapper类中的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);
    }
}

事件处理的具体细节,我们不再深入谈论,该方法当事件处理完成后,调用sync进行事件同步,核心如下所示:

void TouchInputMapper::sync(nsecs_t when) {
   ......
    // Sync touch
    // 同步触摸坐标
    syncTouch(when, next);
    ......
    processRawTouches(false /*timeout*/);
}

该方法会调用syncTouch这个虚拟方法,对于多点触控设备来说,最终调用MultiTouchInputMapper子类实现syncTouch, 这里不再详细展开描述。然后调用processRawTouches开始事件的处理,实现方法如下所示:

void TouchInputMapper::processRawTouches(bool timeout) {
  ......
    const size_t N = mRawStatesPending.size();
    size_t count;
    // 遍历每一个上报的事件
    for(count = 0; count < N; count++) {
        const RawState& next = mRawStatesPending[count];
       ......
        mCurrentRawState.copyFrom(next);
        if (mCurrentRawState.when < mLastRawState.when) {
            mCurrentRawState.when = mLastRawState.when;
        }
        cookAndDispatch(mCurrentRawState.when);
    }
    // 事件处理完成,清空mRawStatesPending
    if (count != 0) {
        mRawStatesPending.removeItemsAt(0, count);
    }
    .......
}

接着调用cookAndDispatch方法,核心实现如下所示:

void TouchInputMapper::cookAndDispatch(nsecs_t when) {
    // Always start with a clean state.
    mCurrentCookedState.clear();
   ......
    cookPointerData();

    ......
    } else {
        // 触摸屏
       ......
        if (!mCurrentMotionAborted) {
            dispatchButtonRelease(when, policyFlags);
            dispatchHoverExit(when, policyFlags);
            dispatchTouches(when, policyFlags);
            dispatchHoverEnterAndMove(when, policyFlags);
            dispatchButtonPress(when, policyFlags);
        }
       ......
    }
   ......
}

该方法中,我们为了叙述的简单,只保留了触摸屏幕相关的处理,cookPointerData方法的实现,这里不详细描述,主要就是处理坐标信息,对于触摸屏幕设备来说,就是调用dispatchTouches方法接着处理,核心实现如下所示:

void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
   ......
    if (currentIdBits == lastIdBits) {
        if (!currentIdBits.isEmpty()) {
           ......
            // 滑动
            dispatchMotion(when, policyFlags, mSource,
                    AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,
                    AMOTION_EVENT_EDGE_FLAG_NONE,
                    mCurrentCookedState.cookedPointerData.pointerProperties,
                    mCurrentCookedState.cookedPointerData.pointerCoords,
                    mCurrentCookedState.cookedPointerData.idToIndex,
                    currentIdBits, -1,
                    mOrientedXPrecision, mOrientedYPrecision, mDownTime);
        }
    } else {
       ......
        // Dispatch pointer up events.
        // 触点抬起
        while (!upIdBits.isEmpty()) {
            uint32_t upId = upIdBits.clearFirstMarkedBit();

            dispatchMotion(when, policyFlags, mSource,
                    AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, metaState, buttonState, 0,
                    mLastCookedState.cookedPointerData.pointerProperties,
                    mLastCookedState.cookedPointerData.pointerCoords,
                    mLastCookedState.cookedPointerData.idToIndex,
                    dispatchedIdBits, upId, mOrientedXPrecision, mOrientedYPrecision, mDownTime);
            dispatchedIdBits.clearBit(upId);
        }
       ......
        if (moveNeeded && !moveIdBits.isEmpty()) {
            ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);
            dispatchMotion(when, policyFlags, mSource,
                    AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, 0,
                    mCurrentCookedState.cookedPointerData.pointerProperties,
                    mCurrentCookedState.cookedPointerData.pointerCoords,
                    mCurrentCookedState.cookedPointerData.idToIndex,
                    dispatchedIdBits, -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime);
        }

        // Dispatch pointer down events using the new pointer locations.
        // 触点按下
        while (!downIdBits.isEmpty()) {
           ......
            dispatchMotion(when, policyFlags, mSource,
                    AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState, 0,
                    mCurrentCookedState.cookedPointerData.pointerProperties,
                    mCurrentCookedState.cookedPointerData.pointerCoords,
                    mCurrentCookedState.cookedPointerData.idToIndex,
                    dispatchedIdBits, downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime);
        }
    }
}

该方法中会处理三种触摸动作,滑动,抬起和按下,最终调用dispatchMotion方法进行处理,核心实现如下所示:

void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
      ......
        float xPrecision, float yPrecision, nsecs_t downTime) {
  ......

    // 根据触摸屏幕的坐标事件,初始化NotifyMotionArgs对象
    NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,
            action, actionButton, flags, metaState, buttonState, edgeFlags,
            mViewport.displayId, pointerCount, pointerProperties, pointerCoords,
            xPrecision, yPrecision, downTime);
    // 找到InputReader的Listen,在初始时指定,最终指向是QueuedInputListener
    getListener()->notifyMotion(&args);
}

该方法的核心,就是构造一个NotifyMotionArgs,然后调用InputReader自己的listen中的方法,最终调用QueuedInputListener中的notifyMotion,核心实现如下:

// 加入到内部mArgsQueue队列中
void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
    mArgsQueue.push(new NotifyMotionArgs(*args));
}

该方法的实现非常简单,构造一个NotifyMotionArgs,然后加入mArgsQueue队列中。

TouchInputMapper方法的process处理流程方法,讲述完成,然后我们回到MultiTouchInputMapper,接着调用了mMultiTouchMotionAccumulator中的process方法,这里不再展开叙述。

void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
    TouchInputMapper::process(rawEvent);
    /* 处理多点触控坐标信息 */
    mMultiTouchMotionAccumulator.process(rawEvent);
}

至此,一个输入设备的事件处理流程讲述完成,最后,我们回到InputReader线程执行函数的,如下所示:

void InputReader::loopOnce() {
  ......

    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

    { // acquire lock
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast();

        // 处理获取到的intput事件
        if (count) {
            processEventsLocked(mEventBuffer, count);
        }

     ......
    } // release lock

   ......
    // 刷新监听队列,将InputReader的事件处理结果,传递给InputDispatcher处理
    mQueuedListener->flush();
}

我们可以看到,设备上报的所有输入事件处理完成后,最后调用mQueuedListener的flush方法。这个方法的实现,下一个章节进行描述。

InputReader事件传递

上一个章节,我们描述了InputReader处理输入设备上报事件的处理流程,当InputReader处理完上报的事件后调用mQueuedListener的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();
}

该方法的实现比较简单,调用notify虚拟方法,对于触摸设备来说就是调用子类实现如下所示:

// 直接传递给InputDispatcher
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
    listener->notifyMotion(this);
}

直接调用listener的notifyMotion进行处理,这里的listener其实就是InputDispatcher,这在InputReader初始化的时候进行传递的,如下所示:

InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    /* 创建事件分发对象 */
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    /* 创建事件获取对象 */
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
}

InputReader::InputReader(const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& policy,
        const sp<InputListenerInterface>& listener) :
        mContext(this), mEventHub(eventHub), mPolicy(policy),
        mGlobalMetaState(0), mGeneration(1),
        mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
        mConfigurationChangesToRefresh(0) {
    // 初始化事件监听器
    mQueuedListener = new QueuedInputListener(listener);

    { // acquire lock
        AutoMutex _l(mLock);

        refreshConfigurationLocked(0);
        updateGlobalMetaStateLocked();
    } // release lock
}

将InputDispatcher当做QueuedInputListener的listener。最终listener->notifyMotion(this)调用到,如下所示:

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
......
}

这里不再详解展开描述,在InputDispatcher模块进行详细描述。

总结

本文描述了InputReader对EventHub事件的处理,InputReader对于每一个不同类型的输入设备调用对应的事件处理器进行处理,当事件处理完成后最终将处理结果传递给InputDispatcher。下一章我们描述InputDispatcher的功能。

相关文章:

  • vue3中,element-plus中el-select隐藏下拉箭头
  • DAY 42 leetcode 151--哈希表.反转字符串中的单词
  • 危化品安全员岗位注意事项有哪些?
  • Downlink Sensing in 5G-Advanced and 6G: SIB1-assisted SSB Approach
  • 深度剖析SSD多段L2P表查找加速技术
  • 解锁AI未来,开启创新之旅——《GPTs开发详解》与《ChatGPT 4应用详解》两本书的深度解析
  • dbVisitor 规则怎么用?
  • VSCode会击败Cursor和Windsurf吗?
  • 【Python技术生态全景:十大核心应用领域深度解析】
  • [Android] PDF编辑器 Xodo PDF Reader 9.13.3 (不完全汉化,能用)
  • 04-算法打卡-数组-二分查找-leetcode(69)-第四天
  • 技术随笔《二》:人形机器人模仿学习与传统控制方法概述
  • 关于 Spring Boot 部署到 Docker 容器的详细说明,涵盖核心概念、配置步骤及关键命令,并附上表格总结
  • Python-控制语句
  • 热管理设计与 PCBA 长期可靠性的深度关联
  • 让DeepSeek API支持联网搜索
  • Lock 与 Synchronized:Java 并发控制的深度对比与实践
  • 什么是继承?js中有哪儿些继承?
  • 基于RISC-V内核的嵌入式系统在机器人关节控制中的应用研究
  • 1. CEF 下载及wrapper编译
  • 万网做网站花多少钱/搜索引擎优化的缺点包括
  • 遵义网上房地产/网站seo重庆
  • 建站公司建的网站能改动吗/兰州快速seo整站优化招商
  • 免费自制主题app/seo系统教程
  • 个性网站设计/温州seo教程
  • 南漳县建设局网站/如何做自己的网站