本章将继续探究InputReader线程,该线程负责从eventhub获取事件,转换成EventEntry事件并将事件加入到InputDispatcher的mInboundQueue。
threadLoop
InputReaderThread线程启动后,将调用一个thredLoop
方法,该方法不断从eventhub获取事件,并进行后续处理,相关代码位于frameworks/native/services/inputflinger/reader/InputReader.cpp
:
// 启动InputReader,调用loopOnce
status_t InputReader::start() {
if (mThread) {
return ALREADY_EXISTS;
}
mThread = std::make_unique<InputThread>(
"InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
return OK;
}
void InputReader::loopOnce() {
...
// 从eventhub获取事件(EVENT_BUFFER_SIZE = 256)
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{
AutoMutex _l(mLock);
mReaderIsAliveCondition.broadcast();
if (count) {
// 处理事件
processEventsLocked(mEventBuffer, count);
}
...
// 如果输入设备发生改变,则重新获取当前输入设备
if (oldGeneration != mGeneration) {
inputDevicesChanged = true;
getInputDevicesLocked(inputDevices);
}
} // release lock
// 发送输入设备改变的通知
if (inputDevicesChanged) {
mPolicy->notifyInputDevicesChanged(inputDevices);
}
// 发送事件到nputDispatcher
mQueuedListener->flush();
}
在上述代码中,有三个关键节点需要重点关注,他们分别是:mEventHub->getEvents
,processEventsLocked
,mQueuedListener->flush
,下面将重点介绍下这几个方法。
mEventHub->getEvents [获取事件]
getEvents方法主要负责获取kernel的event, 这里事件不仅包括input,还包括输入设备的add/remove等相关事件。
加入的输入设备在没有事件的时候,会block在EventHub中的epoll处,当有事件产生后,epoll_wait就会返回,然后针对变化的事件进行处理。相关代码位于frameworks/native/services/inputflinger/reader/EventHub.cpp
:
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
...
for (;;) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
// 扫描设备
if (mNeedToScanDevices) {
mNeedToScanDevices = false;
scanDevicesLocked();
mNeedToSendFinishedDeviceScan = true;
}
while (mOpeningDevices != NULL) {
Device* device = mOpeningDevices;
ALOGV("Reporting device opened: id=%d, name=%s\n",
device->id, device->path.string());
mOpeningDevices = device->next;
event->when = now;
event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
// 添加设备事件
event->type = DEVICE_ADDED;
event += 1;
}
...
Device* device = mDevices.valueAt(deviceIndex);
if (eventItem.events & EPOLLIN) {
// 从设备读取事件,放入到readBuffer
int32_t readSize = read(device->fd, readBuffer,
sizeof(struct input_event) * capacity);
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];
// 将input_event信息封装成RawEvent
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;
}
}
...
}
mLock.unlock(); // release lock before poll, must be before release_wake_lock
release_wake_lock(WAKE_LOCK_ID);
//poll之前先释放锁 等待input事件的到来
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
} else {
// Some events occurred.
mPendingEventCount = size_t(pollResult);
}
}
// All done, return the number of events we read.
return event - buffer;
}
EventHub采用INotify + epoll机制实现监听目录/dev/input下的设备节点,经过EventHub将input_event结构体 + deviceId 转换成RawEvent结构体,RawEvent结构体的声明位于frameworks/native/services/inputflinger/reader/include/EventHub.h
,其结构如下:
RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
struct RawEvent {
// Time when the event happened
nsecs_t when;
// Time when the event was read by EventHub. Only populated for input events.
// For other events (device added/removed/etc), this value is undefined and should not be read.
nsecs_t readTime;
int32_t deviceId;
int32_t type;
int32_t code;
int32_t value;
};
struct input_event {
struct timeval time; //事件发生的时间点
__u16 type;
__u16 code;
__s32 value;
};
processEventsLocked [处理事件]
processEventsLocked
方法负责设备的添加、移除与删除,以及事件的处理,相关代码位于frameworks/av/media/libstrming/input/key_injector.cpp
:
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;
}
}
processEventsForDeviceLocked
processEventsForDeviceLocked
方法用于处理输入设备事件,更具体的来说,是对输入事件的相关信息进行核验,该方法位于frameworks/native/services/inputflinger/reader/InputReader.cpp
:
void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,
size_t count) {
// 使用eventHubId查找是哪一个设备发送的事件
auto deviceIt = mDevices.find(eventHubId);
if (deviceIt == mDevices.end()) {
ALOGW("Discarding event for unknown eventHubId %d.", eventHubId);
return;
}
std::shared_ptr<InputDevice>& device = deviceIt->second;
if (device->isIgnored()) {
// ALOGD("Discarding event for ignored deviceId %d.", deviceId);
return;
}
device->process(rawEvents, count);
}
InputDevice::process
InputDevice::process
方法负责将输入事件进行映射处理,按设备顺序处理所有事件,这是因为每个设备处理之间可能有依赖关系。相关代码位于frameworks/native/services/inputflinger/reader/InputDevice.cpp
:
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;
}
}
KeyboardInputMapper::process
上面提到InputDevice::process
方法会使用不同的处理方式处理输入事件,这些事件包括但不限于触控事件、键盘事件、传感器事件等等,总结下来共有以下几种处理方法
class SwitchInputMapper : public InputMapper {
class VibratorInputMapper : public InputMapper {
class KeyboardInputMapper : public InputMapper {
class CursorInputMapper : public InputMapper {
class RotaryEncoderInputMapper : public InputMapper {
class TouchInputMapper : public InputMapper {
class ExternalStylusInputMapper : public InputMapper {
class JoystickInputMapper : public InputMapper {
在这其中,键盘事件处理最为简单,也最容易分析,触控事件最复杂,所以这里以键盘事件为例进行分析,相关代码位于frameworks/native/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
:
// 键盘事件处理
void KeyboardInputMapper::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
case EV_KEY: {
// 处理按键事件
int32_t scanCode = rawEvent->code;
int32_t usageCode = mCurrentHidUsage;
mCurrentHidUsage = 0;
if (isKeyboardOrGamepadKey(scanCode)) {
processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0, scanCode,
usageCode);
}
break;
}
case EV_MSC: {
// 处理不能被分类到其他种类下的事件
if (rawEvent->code == MSC_SCAN) {
mCurrentHidUsage = rawEvent->value;
}
break;
}
case EV_SYN: {
// 上报事件
if (rawEvent->code == SYN_REPORT) {
mCurrentHidUsage = 0;
}
}
}
}
KeyboardInputMapper::processKey
KeyboardInputMapper::processKey
方法用于处理按键相关事件,相关代码位于frameworks/native/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
:
void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
int32_t usageCode) {
int32_t keyCode;
int32_t keyMetaState;
uint32_t policyFlags;
// 将事件的扫描码(scanCode)转换成键盘码(Keycode)
if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState,
&keyCode, &keyMetaState, &policyFlags)) {
keyCode = AKEYCODE_UNKNOWN;
keyMetaState = mMetaState;
policyFlags = 0;
}
if (down) {
...
mDownTime = when;
} else {
...
}
// 创建NotifyKeyArgs对象, when记录eventTime, downTime记录按下时间
NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
// 发送key事件
getListener()->notifyKey(&args);
}
EventHub::mapKey
EventHub::mapKey
负责将事件的扫描码(scanCode)转换成键盘码(Keycode),相关代码位于frameworks/native/services/inputflinger/reader/EventHub.cpp
:
status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
status_t status = NAME_NOT_FOUND;
if (device != nullptr) {
// Check the key character map first.
const std::shared_ptr<KeyCharacterMap> kcm = device->getKeyCharacterMap();
if (kcm) {
if (!kcm->mapKey(scanCode, usageCode, outKeycode)) {
*outFlags = 0;
status = NO_ERROR;
}
}
// Check the key layout next.
if (status != NO_ERROR && device->keyMap.haveKeyLayout()) {
if (!device->keyMap.keyLayoutMap->mapKey(scanCode, usageCode, outKeycode, outFlags)) {
status = NO_ERROR;
}
}
if (status == NO_ERROR) {
if (kcm) {
kcm->tryRemapKey(*outKeycode, metaState, outKeycode, outMetaState);
} else {
*outMetaState = metaState;
}
}
}
if (status != NO_ERROR) {
*outKeycode = 0;
*outFlags = 0;
*outMetaState = metaState;
}
return status;
}
文末
要想成为架构师,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
![在这里插入图片描述](https://img-blog.csdnimg.cn/06e41b3932164f0db07014d54e6e5626.png) 相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。一、架构师筑基必备技能
1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……
二、Android百大框架源码解析
1.Retrofit 2.0源码解析
2.Okhttp3源码解析
3.ButterKnife源码解析
4.MPAndroidChart 源码解析
5.Glide源码解析
6.Leakcanary 源码解析
7.Universal-lmage-Loader源码解析
8.EventBus 3.0源码解析
9.zxing源码分析
10.Picasso源码解析
11.LottieAndroid使用详解及源码解析
12.Fresco 源码分析——图片加载流程
三、Android性能优化实战解析
- 腾讯Bugly:对字符串匹配算法的一点理解
- 爱奇艺:安卓APP崩溃捕获方案——xCrash
- 字节跳动:深入理解Gradle框架之一:Plugin, Extension, buildSrc
- 百度APP技术:Android H5首屏优化实践
- 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」
- 携程:从智行 Android 项目看组件化架构实践
- 网易新闻构建优化:如何让你的构建速度“势如闪电”?
- …
四、高级kotlin强化实战
1、Kotlin入门教程
2、Kotlin 实战避坑指南
3、项目实战《Kotlin Jetpack 实战》
-
从一个膜拜大神的 Demo 开始
-
Kotlin 写 Gradle 脚本是一种什么体验?
-
Kotlin 编程的三重境界
-
Kotlin 高阶函数
-
Kotlin 泛型
-
Kotlin 扩展
-
Kotlin 委托
-
协程“不为人知”的调试技巧
-
图解协程:suspend
五、Android高级UI开源框架进阶解密
1.SmartRefreshLayout的使用
2.Android之PullToRefresh控件源码解析
3.Android-PullToRefresh下拉刷新库基本用法
4.LoadSir-高效易用的加载反馈页管理框架
5.Android通用LoadingView加载框架详解
6.MPAndroidChart实现LineChart(折线图)
7.hellocharts-android使用指南
8.SmartTable使用指南
9.开源项目android-uitableview介绍
10.ExcelPanel 使用指南
11.Android开源项目SlidingMenu深切解析
12.MaterialDrawer使用指南
六、NDK模块开发
1、NDK 模块开发
2、JNI 模块
3、Native 开发工具
4、Linux 编程
5、底层图片处理
6、音视频开发
7、机器学习
七、Flutter技术进阶
1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter开发环境搭建和调试
5、Dart语法篇之基础语法(一)
6、Dart语法篇之集合的使用与源码解析(二)
7、Dart语法篇之集合操作符函数与源码分析(三)
…
八、微信小程序开发
1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战……
全套视频资料:
一、面试合集
二、源码解析合集
三、开源框架合集
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取【保证100%免费】↓↓↓