[Android] Input事件分发流程之InputReader(2)
最佳答案 问答题库808位专家为你答疑解惑
继IMS构造方法分析完成后,看看IMS中的start方法
public void start() {Slog.i(TAG, "Starting input manager");// 之前初始化了InputManager->inputDispatcher&&inputReader// 这里开始start它们,并且会创建InputThread线程,也就是InputReaderThread和InputDispatcherThread// InputThread是一个自定义的类,它接受三个参数:一个字符串作为线程名,// 一个lambda表达式作为线程函数,另一个lambda表达式在线程结束时被调用nativeStart(mPtr);// Add ourself to the Watchdog monitors.Watchdog.getInstance().addMonitor(this);//注册触摸点速度和是否显示功能的观察者registerPointerSpeedSettingObserver();registerShowTouchesSettingObserver();...// 更新触摸点的速度updatePointerSpeedFromSettings();// 是否在屏幕上显示触摸点updateShowTouchesFromSettings();updateAccessibilityLargePointerFromSettings();updateDeepPressStatusFromSettings("just booted");updateMaximumObscuringOpacityForTouchFromSettings();updateBlockUntrustedTouchesModeFromSettings();}
以上只关注nativeStart(mPtr);,mPtr指向的就是NativeInputManager,之前初始化了InputManager->inputDispatcher&&inputReader,这里开始start它们,并且会创建InputThread线程,也就是InputReaderThread和InputDispatcherThread
// **com_android_server_input_InputManagerService.cpp**
static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);// 获取InputManager,调用它的startstatus_t result = im->getInputManager()->start();if (result) {jniThrowRuntimeException(env, "Input manager could not be started.");}
}// **InputManager.cpp**
status_t InputManager::start() {// 调用inputDispatcher的start()status_t result = mDispatcher->start();if (result) {ALOGE("Could not start InputDispatcher thread due to error %d.", result);return result;}// 调用inputReader的start()result = mReader->start();if (result) {ALOGE("Could not start InputReader due to error %d.", result);mDispatcher->stop();return result;}return OK;
}
InputReader线程
也就是在InputManager中调用了mDispatcher->start();和mReader->start();去分别创建对应的线程,并让Inputeader和InputDispatcher执行具体逻辑。先来看看InputReader中的start方法
status_t InputReader::start() {if (mThread) {return ALREADY_EXISTS;}// InputThread是一个自定义的类,它接受三个参数:一个字符串作为线程名,一个lambda表达式作为线程函数,// 另一个lambda表达式在线程结束时被调用mThread = std::make_unique<InputThread>("InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });return OK;
}// 这里就是start后的主要内容
// 读取eventHub里面的设备节点内容
// 调用的是 mEventHub->getEvents
void InputReader::loopOnce() {int32_t oldGeneration;int32_t timeoutMillis;bool inputDevicesChanged = false;...// 读取设备里的所有事件,存放到了mEventBuffer// RawEvent mEventBuffer[EVENT_BUFFER_SIZE] GUARDED_BY(mLock);size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);{ // acquire lock...if (count) {// 关键代码processEventsLocked(mEventBuffer, count);}...mQueuedListener->flush();
}
首先调用了mEventHub中的getEvents方法其获取输入的原始事件,并存放到RawEvent中,所以该实体类中存放的是原始输入事件,然后调用了之前初始化创建的mQueuedListener,调用flush()将事件发送给InputDispatcher中,继续看看EventHub中的getEvents
方法
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer/*缓存事件*/, size_t bufferSize/*256*/) {struct input_event readBuffer[bufferSize];// 原始事件RawEvent* event = buffer;// 256size_t capacity = bufferSize;bool awoken = false;// 循环读取设备节点for (;;) {...// std::vector<std::unique_ptr<Device>> mOpeningDevices;// 初始化动作,主要是打开设备和扫描设备// 扫描设备,并打开每个设备目录/dev/input/...scanDevicesLocked();while (!mOpeningDevices.empty()) {// 获取mOpeningDevices中的设备/dev/input/目录下的文件:event1/2/3/4等std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());mOpeningDevices.pop_back();ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());// 将每个设备打包生成RawEvent(event)形式进行存储对应的信息event->when = now;event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;// 添加设备的事件event->type = DEVICE_ADDED;event += 1;...}// 这里是开始处理每个设备中的事件while (mPendingEventIndex < mPendingEventCount) {// 从mPendingEventCount读取事件const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];...// 打开/dev/input/... event1/2/3等设备文件Device* device = getDeviceByFdLocked(eventItem.data.fd);if (device == nullptr) {ALOGE("Received unexpected epoll event 0x%08x for unknown fd %d.", eventItem.events,eventItem.data.fd);ALOG_ASSERT(!DEBUG);continue;}...// This must be an input event// 读取设备中的输入事件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.ALOGW("could not get event, removed? (fd: %d size: %" PRId32" bufferSize: %zu capacity: %zu errno: %d)\n",device->fd, readSize, bufferSize, capacity, errno);deviceChanged = true;// 没有事件则关闭设备closeDeviceLocked(*device);} ... 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++) {// 将每个事件打包生成RawEventstruct input_event& iev = readBuffer[i];event->when = processEventTimestamp(iev);event->readTime = systemTime(SYSTEM_TIME_MONOTONIC);event->deviceId = deviceId;event->type = iev.type;event->code = iev.code;event->value = iev.value;event += 1;capacity -= 1;}...}} ...}...// poll之前先释放锁mLock.unlock(); // release lock before poll// 等待input事件的到来int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);// 事件到来就请求锁mLock.lock(); // reacquire lock after poll...// All done, return the number of events we read.// 返回所读取的事件个数return event - buffer;
}
以上内容就是scanDevicesLocked
做初始化动作,扫描设备,打开设备,将设备保存到mOpeningDevices中,然后循环读取每个设备中的事件,将每个事件封装成RawEvent并加上了DEVICE_ADDED
这个标志(后面会用到,来决定是移除还是新增),再保存到buffer(mEventBuffer)
里面,如果设备中没有事件了则会关闭设备,然后通过epoll_wait
来一直等等下一次的input事件到来,所以我们回到InputReader.cpp#loopOnce中继续分析processEventsLocked(mEventBuffer, count);
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {// rawEvents保存了通过EventHub中获取的设备里的事件// count是事件数量for (const RawEvent* rawEvent = rawEvents; count;) {int32_t type = rawEvent->type;size_t batchSize = 1;if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {...// 事件的处理processEventsForDeviceLocked(deviceId, rawEvent, batchSize);} else {// 在EventHub.getEvents中有处理,每个事件都会封装为RawEvent,并且而加上了DEVICE_ADDED标志switch (rawEvent->type) {case EventHubInterface::DEVICE_ADDED:// 创建设备,eventHubId设备唯一id,通过id可获取设备// 这里主要是创建InputDevice,根据device判断是鼠标类型触摸事件,还是键盘类,或触摸屏类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 happenbreak;}}// 处理完事件,count减少count -= batchSize;// 处理完事件,指针向后移动一个rawEvent += batchSize;}
}void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,size_t count) {auto deviceIt = mDevices.find(eventHubId);...// 调用InputDevice的process// 传递原始事件+事件数量device->process(rawEvents, count);
}
首先会走addDeviceLocked来创建InputDevice,因为/dev/input里每个device的功能都不同,有的处理指纹事件,有的处理鼠标键盘类数据,有的处理屏幕触摸事件,然后再执行到processEventsForDeviceLocked
,因为processEventsForDeviceLocked
执行之前已经创建了各个类型的InputDevice
,这里就是调用对应类型的InputDevice.process方法,那随便找一个TouchInputMapper.cpp
来分析吧,触摸事件
frameworks/native/services/inputflinger/reader/mapper/TouchInputMapper.cpp
void TouchInputMapper::process(const RawEvent* rawEvent/*原始事件*/) {// 鼠标按下,滚动,触摸mCursorButtonAccumulator.process(rawEvent);mCursorScrollAccumulator.process(rawEvent);mTouchButtonAccumulator.process(rawEvent);if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {// 主要关注syncsync(rawEvent->when, rawEvent->readTime);}
}
sync会继续调用processRawTouches
,接着看
// 它处理原始触摸输入数据并将其转换为可读的触摸事件。
// 首先,它检查设备模式是否为DISABLED。如果是,它将取消当前的触摸,并清空当前的状态(mCurrentCookedState),然后更新触摸点。
// 接下来,它会处理任何待处理的原始触摸状态。这些状态被存储在mRawStatesPending中。
// 如果当前状态是一个stylus(触笔)状态,则会检查是否需要等待stylus数据。
// 如果需要等待,它将退出循环。否则,它会将当前状态设为下一个待处理状态,并通过cookAndDispatch函数将其转换为可读的触摸事件
void TouchInputMapper::processRawTouches(bool timeout) {// 检查设备是否处于disabled状态if (mDeviceMode == DeviceMode::DISABLED) {// Drop all input if the device is disabled.cancelTouch(mCurrentRawState.when, mCurrentRawState.readTime);...return;}// 它会处理任何待处理的原始触摸状态,这些状态被存储在mRawStatesPending中。const size_t N = mRawStatesPending.size();size_t count;// 待处理的原始事件 RawEventfor (count = 0; count < N; count++) {...// 继续调用,主要就是将原始事件转换为可读的触摸事件cookAndDispatch(mCurrentRawState.when, mCurrentRawState.readTime);}...
}
简要说明一下cookAndDispatch
方法的作用,判断事件的类型属于手势触摸还是鼠标等类型,假设是手势触摸则调用dispatchPointerGestures
进行事件分发,然后判断事件是否移动,tap按下等状态,进行单独处理和事件分发,并把当前手指通过markBit(id);
存入一个整型变量中,所以不管有多少个手指触摸,都以进制形式存放在一个变量中,内存开销小,最后调用dispatchMotion
方法将事件的状态,例如是否按下,按下的时间,事件动作,以及事件id封装到NotifyMotionArgs
中,然后进行分发(也就是通知消息并携带该args给InputDispatcher),实际是调用getListener()->notifyMotion(&args);
所以继续看看InputDispatcher.cpp中的notifyMotion方法,进入下一个阶段
总结:
- 从InputReader线程创建开始,第一时间就是从EventHub中的getEvents方法中打开/dev/input中的设备,然后循环读取设备中的事件,获取事件,并转换为原始事件(RawEvent),然后再将原始事件,此次事件处理完成后继续循环等待input事件的到来
2.然后再继续调用了processEventsLocked
判断原始事件属于什么类型,例如触摸,鼠标,键盘等,创建对应的InputDevice
- 接着继续调用
processEventsForDeviceLocked
来处理InputDevice→process
,然后继续分析了TouchInputMapper.cpp,因为这个mapper就是InputDevice的其中一种类型,根据事件类型,判断是否点击,滑动,多个手势,将事件信息封装成NotifyMotionArgs
,将手指封装到整型变量中,代表有多少个手指,然后分发给InputDispatcher
99%的人还看了
相似问题
猜你感兴趣
版权申明
本文"[Android] Input事件分发流程之InputReader(2)":http://eshow365.cn/6-18359-0.html 内容来自互联网,请自行判断内容的正确性。如有侵权请联系我们,立即删除!
- 上一篇: 【改造中序遍历算法】1038. 从二叉搜索树到更大和树
- 下一篇: 312.戳气球