写点什么

Android Input 底层机制

作者:江湖修行
  • 2024-03-06
    北京
  • 本文字数:9203 字

    阅读完需:约 30 分钟

Android Input底层机制

前序

一般情况下很多同学对于点击事件的认识都只存在于从 Activity 开始的,然后从 Window 中进行分发,或者有些人有过自定义 View 的经验可能会涉及事件拦截处理等,会用到 onTouchEvent 和 dispatchTouchEvetn 这几个方法,会处理屏幕滑动,Down 事件,Up 事件等,但仅停留于对于 View 层的了解。


自定义 View 的事件处理其实在整个 Android 输入系统中只能算是最上层的,但这些事件是怎么产生的,上层 View 是怎么获得到这些事件的呢?喜欢研究底层实现原理的我,今天就给大家讲讲。

输入系统



事件产生的底层主要是输入子系统,Android 中的输入设备有很多,例如屏幕,鼠标,键盘等都是输入设备,对于应用开发者,接触最多的也就是屏幕了。当输入设备可用时,Linux 会在 /dev/input 中创建对应的设备节点。用户操作输入设备就会产生各种事件,这些事件的原始信息就会被 Linux 内核中的输入子系统采集,原始信息由 Kernel space 的驱动层一直传递到设备结点。


Android 提供了一些 api 可以让开发者在设备节点(dev/input/)中读取内核写入的事件。

InputManagerService

IMS 的工作就是监听 /dev/input 下所有设备的节点,当有数据时就会进行加工处理,并交给中间层的 WMS 派发给合适的 Window。后面简称为 IMS。

WindowManagerService

Android 的 WMS 主要是负责窗口管理,窗口的启动、添加、删除,管理窗口大小、层级等,WMS 与 IMS 息息相关。IMS 将底层封装好的事件通过 Socket pair 传递给 WMS,WMS 通过绑定好的 InputChannel 找到对应的窗口进行 app 层的事件分发,这时候就到我们熟悉的 Activity 的事件分发流程了。


下面借张图,整体流程画的非常详细:




今天我们主要讲底层硬件驱动产生的事件的获取及分发流程,至于硬件驱动层就不展开介绍了。而这里面最重要的就是 IMS 相关的流程,下面开始补足知识盲区。

IMS 的初始化

IMS 的构造

和 WMS 一样,IMS 也是在 SystemServer 中创建的 ,也是 startOtherServers 方法中。


private void startOtherServices(@NonNull TimingsTraceAndSlog t) {        ...        inputManager = new InputManagerService(context);        wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,                new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);        ...        inputManager.start();    }
复制代码


主要做了三件事:


  • 调用了 IMS 的构造方法

  • 将 IMS 作为参数传递给 WMS

  • 调用了 IMS 的启动方法 start


IMS 构造函数


public InputManagerService(Context context) {        this.mContext = context;        //注释1        this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());        mStaticAssociations = loadStaticInputPortAssociations();        mUseDevInputEventForAudioJack =                context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);        Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="                + mUseDevInputEventForAudioJack);        //注释2        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());    }
复制代码


我们重点关注 1 创建了一个在 android.display 线程的 InputManagerHandler,所以后面这个 handler 处理的消息都是执行在 android.display 线程中,android.display 线程是系统共享的单例前台线程,可以用做一些低延时显示的相关操作,WMS 的创建也是在 android.display 中创建的。


注释 2 的话,调用了 nativeInit 方法,进入 native 侧创建了 NativeInputManager ,并将该对象指针返回给了 java 层,方便后续调用处理。


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


接下来看下 NativeInputManager 的创建


NativeInputManager::NativeInputManager(jobject contextObj,                                           jobject serviceObj, const sp<Looper>& looper) :        mLooper(looper), mInteractive(true) {        JNIEnv* env = jniEnv();        mContextObj = env->NewGlobalRef(contextObj);//注释1        mServiceObj = env->NewGlobalRef(serviceObj);//注释2        ...        sp<EventHub> eventHub = new EventHub();//注释3        mInputManager = new InputManager(eventHub, this, this);//4    }
复制代码


  • 注释 1:将 java 层的 Context 上下文保存在 mContextObj 中

  • 注释 2:将 java 层传递下来的 InputManagerService 对象保存在 mServiceObj 中。

  • 注释 3:创建了一个 EventHub,EventHub 通过 Linux 内核的 Notify 与 Epoll 机制监听设备节点,通过 EventHub 的 getEvent 函数读取设备节点的增删事件和原始输入事件。

  • 最后创建了 InputManager 对象


InputManager::InputManager(            const sp<InputReaderPolicyInterface>& readerPolicy,            const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {        //注释1        mDispatcher = createInputDispatcher(dispatcherPolicy);        mClassifier = new InputClassifier(mDispatcher);        //注释2        mReader = createInputReader(readerPolicy, mClassifier);    }    sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy,        const sp<InputListenerInterface>& listener) {        return new InputReader(std::make_unique<EventHub>(), policy, listener);    }
复制代码


其内部会创建 InputDispatcher 和 InputReader。


  • InputDispatcher,该类主要用于对原始事件的分发,传递给 WMS。

  • InputReader,会不断的读取 EventHub 中的原始信息进行加工并交给 InputDispatcher ,InputDispatcher 中保存了 Window 的信息(WMS 会将窗口的信息实时更新到 InputDispatcher 中),可以将事件信息派发到合适的窗口,InputReader 和 InputDispatcher 都是耗时操作,会在单独线程中执行。


IMS 构造主要是创建了 NativeInputManager 的 native 对象并返回给 java 层,将 java 层的 IMS 对象保存在 native 层的 mServiceObj 中,创建用于读取设备节点事件的 EventHub 对象。在 InputManager 构造中,创建了一个 InputDispatcher 和 InputReader 对象,以及用于读取事件的 InputReaderThread 线程和分发事件的 InputDispatcherThread 线程。

IMS 启动

在 IMS 创建完成之后,就会调用他的 start 方法进行启动


public void start() {        nativeStart(mPtr);}
复制代码


调用 native 的 start 并传入 native 层 NativeInputManager 对象的指针,会调 native 的 InputManager 的 start 方法,该方法内部分别会 IputDispatcher 和 InputReader 的 start 方法。


status_t InputManager::start() {        //注释1        status_t result = mDispatcher->start();        if (result) {            ALOGE("Could not start InputDispatcher thread due to error %d.", result);            return result;        }        //注释2        result = mReader->start();        if (result) {            ALOGE("Could not start InputReader due to error %d.", result);            mDispatcher->stop();            return result;        }        return OK;    }
复制代码


  • 调用 IputDispatcher 的 start 方法,用于对事件进行分发

  • 调用 InputReader 的 start 方法,用于从 EventHub 中获取原始事件进行处理。

InputReader 读取事件

status_t InputReader::start() {        if (mThread) {            return ALREADY_EXISTS;        }        mThread = std::make_unique<InputThread>(                "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });        return OK;    }
复制代码


InputReader 中实例化了一个 InputThread ,该类继承了 Thread,其内部有一个循环,当线程运行时,会调用 threadloop 函数,如果返回了 true 并且没有调用 requestExit 函数,就会循环调用 threadloop 函数,线程中真正执行的是 loopOnce


void InputReader::loopOnce() {        ......        //1        size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);        { // acquire lock            if (count) {                //2                processEventsLocked(mEventBuffer, count);            }        } // release lock        ......        //3          mQueuedListener->flush();    }
复制代码


主要做三件事:


  • 调用 EventHub 的 getEvents 函数来获取设备节点的信息到 mEventBuffer 中,事件信息主要有两种,一种是设备的增删事件(设备事件),一种是原始的输入事件,注:方法内部在没有输入事件的时候会进行休眠,并不会持续占用 CPU​

  • 对 mEventBuffer 中的输入事件信息进行加工处理,加工处理后的事件会交给 InputDispatcher 来处理

  • 如果无事件就调用 flush 方法,一次性将所有事件给 InputDispatcher 处理


我们继续看下怎么对事件进行加工处理的,


void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {        for (const RawEvent* rawEvent = rawEvents; count;) {            int32_t type = rawEvent->type;            size_t batchSize = 1;            //注释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;                }                //注释2                processEventsForDeviceLocked(deviceId, rawEvent, batchSize);            } else {                switch (rawEvent->type) {//3                    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;        }    }    void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) {        ...        std::shared_ptr<InputDevice> device = createDeviceLocked(eventHubId, identifier);        ...        mDevices.emplace(eventHubId, device);        ...    }
复制代码


所有事件用 RawEvent 封装,但对原始原始事件和设备事件分开处理事件和设备事件分开处理,其中设备事件有三种类型,分别是节点的添加,删除、扫描等操作,原始事件主要是交给 processEventsForDeviceLocked 处理,先来看下 processEventsForDeviceLocked 逻辑。


void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,                                                   size_t count) {        //注释1        auto deviceIt = mDevices.find(eventHubId);        if (deviceIt == mDevices.end()) {            ALOGW("Discarding event for unknown eventHubId %d.", eventHubId);            return;        }        //注释2        std::shared_ptr<InputDevice>& device = deviceIt->second;        if (device->isIgnored()) {            // ALOGD("Discarding event for ignored deviceId %d.", deviceId);            return;        }        device->process(rawEvents, count);    }
复制代码


mDevices 中存的 key 是 eventHub,而 value 就是设备 InputDevice,查到后进行处理


void InputDevice::process(const RawEvent* rawEvents, size_t count) {        for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {            if (mDropUntilNextSync) {                if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {                    mDropUntilNextSync = false;                } else {              .....                }            } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {                //缓冲区溢出                ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());                mDropUntilNextSync = true;                reset(rawEvent->when);            } else {                for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {                        mapper.process(rawEvent);            });            }            --count;        }    }    inline void for_each_mapper_in_subdevice(int32_t eventHubDevice,                                             std::function<void(InputMapper&)> f) {        auto deviceIt = mDevices.find(eventHubDevice);        if (deviceIt != mDevices.end()) {            auto& devicePair = deviceIt->second;            auto& mappers = devicePair.second;            for (auto& mapperPtr : mappers) {                f(*mapperPtr);            }        }
复制代码


最终是使用不同的 InputMapper 进行处理,原始输入事件的类型很多,因此 InputMapper 有很多子类用于加工不同的原始输入事件,例如 TouchInputMapper 用于处理触摸输入事件,KeyboardInputMapper 处理键盘输入事件等。以触摸事件为例,看下 TouchInputMapper 执行。


void TouchInputMapper::process(const RawEvent* rawEvent) {        mCursorButtonAccumulator.process(rawEvent); //处理鼠标事件,type == EV_KEY 进行处理        mCursorScrollAccumulator.process(rawEvent); //处理鼠标滚轮事件,type == EV_REL 进行处理        mTouchButtonAccumulator.process(rawEvent);  //处理屏幕触摸事件,type == EV_KEY 进行处理        if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {            sync(rawEvent->when, rawEvent->readTime);        }    }
复制代码


void TouchInputMapper::processRawTouches(bool timeout) {    ....    cookAndDispatch(when, readTime);    ....   }
复制代码


 void TouchInputMapper::dispatchTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {        BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;        BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits;        int32_t metaState = getContext()->getGlobalMetaState();        int32_t buttonState = mCurrentCookedState.buttonState;        // Dispatch pointer up events.        while (!upIdBits.isEmpty()) {            .....            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0,                    isCanceled ? AMOTION_EVENT_FLAG_CANCELED : 0, metaState, buttonState, 0,                    mLastCookedState.cookedPointerData.pointerProperties,                    mLastCookedState.cookedPointerData.pointerCoords,                    mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId,                    mOrientedXPrecision, mOrientedYPrecision, mDownTime);            dispatchedIdBits.clearBit(upId);            mCurrentCookedState.cookedPointerData.canceledIdBits.clearBit(upId);        }        // Dispatch move events if any of the remaining pointers moved from their old locations.        // Although applications receive new locations as part of individual pointer up        // events, they do not generally handle them except when presented in a move event.        if (moveNeeded && !moveIdBits.isEmpty()) {            ...            dispatchMotion(when, readTime, 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, readTime, 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);        }    }
复制代码


会根据记录的上一次触摸位置,根据事件类型做相应的分发。事件类型有 Up,Down,Move 等,最终调用


 void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,                                          uint32_t source, int32_t action, int32_t actionButton,                                          int32_t flags, int32_t metaState, int32_t buttonState,                                          int32_t edgeFlags, const PointerProperties* properties,                                          const PointerCoords* coords, const uint32_t* idToIndex,                                          BitSet32 idBits, int32_t changedId, float xPrecision,                                          float yPrecision, nsecs_t downTime) {        //最终形成的 NoteifyMotionArgs 对象        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, deviceId, source, displayId,                policyFlags, action, actionButton, flags, metaState, buttonState,                MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,                pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,downTime, std::move(frames));        //回调到 InputDispatcher 的 notifyMotion 方法中        getListener()->notifyMotion(&args);    }
复制代码


void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {        .......        bool needWake;        {            // Just enqueue a new motion event.            std::unique_ptr<MotionEntry> newEntry =                    std::make_unique<MotionEntry>(args->id, args->eventTime, args->deviceId,                                              ......);            needWake = enqueueInboundEventLocked(std::move(newEntry));            mLock.unlock();        } // release lock        if (needWake) {            mLooper->wake();        }    }        bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newEntry) {        bool needWake = mInboundQueue.empty();            //将事件添加到 minboundQueue 中        mInboundQueue.push_back(std::move(newEntry));        EventEntry& entry = *(mInboundQueue.back());        traceInboundQueueLengthLocked();        ...        return needWake;    }
复制代码


最终调用 enqueueInboundEventLocked 方法并将 MotionEntry 添加到队列,然后决定是否唤醒 InputDispatcher 线程​。


获取事件的流程至此结束,​我们总结下:


  • InputReader 只是调用 EventHub 的 getEvent 获取了原始事件,获取到事件后,就会根据原始事件找到对应的 InputDevice(设备对象)。

  • 在 InputDevice 中,根据事件获取到对应的 InputMapper 用于加工事件。InputMapper 子类分别对应了不同的事件类型,例如触摸事件,多点触摸事件,按键事件等。

  • 最终事件被加工结束后都会通过 getListener 回调掉 InputDispatcher 中对应的两个方法,比如触摸事件被封装成了 MotionEntry 对象。最后调用 enqueueInboundEventLocked 方法,决定是否需要唤醒 InputDispatcher 线程

总结

由于篇幅过长,为避免带来阅读不适,事件分发后面再​讲。感谢关注,感谢一键三连​。


微信公众号首发,第一时间获取新知识请关注,感谢支持,欢迎大家关注、转发、评论。

发布于: 刚刚阅读数: 3
用户头像

江湖修行

关注

还未添加个人签名 2021-12-05 加入

还未添加个人简介

评论

发布
暂无评论
Android Input底层机制_android_江湖修行_InfoQ写作社区