了解更多,移步Android触摸事件传递机制系列详解
EventHub的作用是将来源不同的各种信息,转化成为一种类型的信息,然后将这些信息提交到上层,给上层做处理
1 EventHub初始化
- EventHub对象是在NativeInputManager构造函数中创建的.
- EventHub构造函数中都做了些什么
- 初始化一些成员变量
- 创建epoll对象,EPOLL_SIZE_HINT = 8代表最大监听数为8.
- 创建inotify对象,监听/dev/input下设备节点的增删。
- 将mINotifyFd添加到epoll中,作为一个监控对象。
- 创建管道,将管道读取端的可读事件添加到epoll中。使epoll_wait()返回,唤醒InputReader线程。
EventHub::EventHub(void) :
mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1),
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对象的描述符
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno);
//创建inotify对象,mINotifyFd为inotify对象的描述符
mINotifyFd = inotify_init();
//DEVICE_PATH值为"/dev/input",监听该目录下的设备节点创建与删除操作。通过read函数读取事件。
int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s. errno=%d",
DEVICE_PATH, errno);
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN; //监听可读事件
eventItem.data.u32 = EPOLL_ID_INOTIFY;
//EPOLL_CTL_ADD表示增加事件
//epoll_ctl将事件监听添加到epoll对象中去。
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno);
int wakeFds[2];
result = pipe(wakeFds);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno);
mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d",
errno);
result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d",
errno);
eventItem.data.u32 = EPOLL_ID_WAKE;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d",
errno);
}
2 EventHub::getEvents()
EventHub
的主要工作都是在getEvents函数中,InputReaderThread通过循环调用EventHub的getEvents()函数获取输入事件。getEvents中做了些什么,现在看一看。通过epoll_wait,得到相应设备文件发生变化的事件
根据事件类型获取相应设备id
从相应设备文件中读取内容,对读出内容进行判断,判断是否有错误,是否可用
判断无误,对事件内容进行包装,加入到返回事件中
getEvents()是阻塞的,只有当有事件或者被wake才会被唤醒向下执行。
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
AutoMutex _l(mLock); //加锁
struct input_event readBuffer[bufferSize];
RawEvent* event = buffer; //原始事件
size_t capacity = bufferSize; //容量大小为256
bool awoken = false;
for (;;) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
...
if (mNeedToScanDevices) {
mNeedToScanDevices = false;
scanDevicesLocked(); //扫描设备【见小节2.2】
mNeedToSendFinishedDeviceScan = true;
}
while (mOpeningDevices != NULL) {
Device* device = mOpeningDevices;
mOpeningDevices = device->next;
event->when = now;
event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
event->type = DEVICE_ADDED; //添加设备的事件
event += 1;
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break;
}
}
...
bool deviceChanged = false;
while (mPendingEventIndex < mPendingEventCount) {
//从mPendingEventItems读取事件项
const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
.....
//获取设备ID所对应的device
ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
if (eventItem.events & EPOLLIN) {
//从设备不断读取事件,放入到readBuffer
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++) {
//获取readBuffer的数据
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。这里采取一系列的防范
//将input_event信息, 封装成RawEvent
.........
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;
//等待input事件的到来
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;
}
EventHub采用INotify + epoll机制实现监听目录/dev/input下的设备节点,经过EventHub将input_event结构体 + deviceId 转换成RawEvent结构体,如下:
3 RawEvent
[-> InputEventReader.h]
struct input_event {
struct timeval time; //事件发生的时间点
__u16 type;
__u16 code;
__s32 value;
};
struct RawEvent {
nsecs_t when; //事件发生的时间店
int32_t deviceId; //产生事件的设备Id
int32_t type; // 事件类型
int32_t code;
int32_t value;
};
此处事件类型:
- DEVICE_ADDED(添加)
- DEVICE_REMOVED(删除)
- FINISHED_DEVICE_SCAN(扫描完成)
- type<FIRST_SYNTHETIC_EVENT(其他事件)
getEvents()已完成转换事件转换工作,
4 设备扫描
4.1 scanDevicesLocked
void EventHub::scanDevicesLocked() {
//此处DEVICE_PATH="/dev/input"【见小节2.3】
status_t res = scanDirLocked(DEVICE_PATH);
...
}
4.2 scanDirLocked
status_t EventHub::scanDirLocked(const char *dirname)
{
char devname[PATH_MAX];
char *filename;
DIR *dir;
struct dirent *de;
dir = opendir(dirname);
strcpy(devname, dirname);
filename = devname + strlen(devname);
*filename++ = '/';
//读取/dev/input/目录下所有的设备节点
while((de = readdir(dir))) {
if(de->d_name[0] == '.' &&
(de->d_name[1] == '\0' ||
(de->d_name[1] == '.' && de->d_name[2] == '\0')))
continue;
strcpy(filename, de->d_name);
//打开相应的设备节点【2.2.3】
openDeviceLocked(devname);
}
closedir(dir);
return 0;
}
4.3 openDeviceLocked
status_t EventHub::openDeviceLocked(const char *devicePath) {
char buffer[80];
//打开设备文件
int fd = open(devicePath, O_RDWR | O_CLOEXEC);
InputDeviceIdentifier identifier;
//获取设备名
if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1){
} else {
buffer[sizeof(buffer) - 1] = '\0';
identifier.name.setTo(buffer);
}
identifier.bus = inputId.bustype;
identifier.product = inputId.product;
identifier.vendor = inputId.vendor;
identifier.version = inputId.version;
//获取设备物理地址
if(ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) {
} else {
buffer[sizeof(buffer) - 1] = '\0';
identifier.location.setTo(buffer);
}
//获取设备唯一ID
if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) {
} else {
buffer[sizeof(buffer) - 1] = '\0';
identifier.uniqueId.setTo(buffer);
}
//将identifier信息填充到fd
assignDescriptorLocked(identifier);
//设置fd为非阻塞方式
fcntl(fd, F_SETFL, O_NONBLOCK);
//获取设备ID,分配设备对象内存
int32_t deviceId = mNextDeviceId++;
Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
...
//注册epoll
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
if (mUsingEpollWakeup) {
eventItem.events |= EPOLLWAKEUP;
}
eventItem.data.u32 = deviceId;
if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
delete device; //添加失败则删除该设备
return -1;
}
...
addDeviceLocked(device);
}
4.4 addDeviceLocked
void EventHub::addDeviceLocked(Device* device) {
mDevices.add(device->id, device); //添加到mDevices队列
device->next = mOpeningDevices;
mOpeningDevices = device;
}
介绍了EventHub从设备节点获取事件的流程,当收到事件后接下里便开始处理事件。
参考
Android 输入系统(二)EventHub
Android系统源码分析-事件收集
Input系统—InputReader线程