目录
1 概述
2 触控事件的读取
3 触控事件的分发
4 寻找触控事件的目标窗口
5 触控事件发送到目标窗口
5.1 触控事件发送到目标窗口流程
5.2 应用APP与InputDispatcher的InputChannel注册与监听
5.2.1 创建客户端与服务端的InputChannel
5.2.2 注册服务端InputChannel到InputDispatcher
5.2.3 App客户端监听触控事件
6 目标窗口内部的事件传递机制
6.1 UI线程对触控事件的分发处理
6.2 View的触控事件分发机制
7 触控问题调试机制
8 总结
用户手指点击按压屏幕后,屏幕触控驱动产生中断,Linux内核会将硬件产生的触控事件包装成Event放在设备的dev/input/目录下。此后Android系统需要解决以下几个问题以实现整个触控事件的分发处理:
下面将结合Android 11系统源码,通过分析回答这些问题来了解Android系统触控事件处理机制的全貌。
Android所有的input输入设备都会在/dev/input目录下生成对应的设备节点,一旦有任何输入事件产生,Linux内核便会将事件写到这些节点下,同时对于外部输入设备(鼠标、键盘等)的插拔还会引起这些节点的创建和删除。系统封装了一个叫EventHub的对象,它负责利用Linux的inotify和epoll机制监听/dev/input目录下的设备事件节点,通过EventHub的getEvents接口就可以监听并获取该事件。
系统开机启动system_server进程时会创建启动InputManagerService核心服务,其中会先通过JNI调用创建InputManager对象,然后进一步新建一个InputReader对象;然后通过start方法为InputReader创建一个InputThread的Loop工作线程,这个线程的工作就是通过EventHub的getEvents监听读取Input事件。详细流程如下图所示:
简要代码流程如下:
/*frameworks/base/services/java/com/android/server/SystemServer.java*/
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
...
// 1.先创建InputManagerService对象
inputManager = new InputManagerService(context);
...
// 2.start启动相关input线程
inputManager.start();
...
}
/*frameworks/base/services/core/java/com/android/server/input/InputManagerService.java*/
public InputManagerService(Context context) {
...
// 1.JNI调用native接口完成InputManager初始化动作
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
...
}
public void start() {
// 2.JNI调用native接口启动InputManager工作线程
nativeStart(mPtr);
...
}
/*frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp*/
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
...
// 创建NativeInputManager对象
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
...
}
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp& looper) :
mLooper(looper), mInteractive(true) {
...
// 1.创建InputManager对象
mInputManager = new InputManager(this, this);
...
}
static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
...
// 2.调用InputManager的start函数
status_t result = im->getInputManager()->start();
...
}
/*frameworks/native/services/inputflinger/InputManager.cpp*/
InputManager::InputManager(
const sp& readerPolicy,
const sp& dispatcherPolicy) {
// 创建InputDispatcher触控事件分发对象
mDispatcher = createInputDispatcher(dispatcherPolicy);
mClassifier = new InputClassifier(mDispatcher);
// 1.createInputReader创建InputReader事件读取对象,间接持有InputDispatcher的引用,以便事件读取完成后通知其进行事件分发
mReader = createInputReader(readerPolicy, mClassifier);
}
status_t InputManager::start() {
// 启动InputDispatcher工作线程
status_t result = mDispatcher->start();
...
// 2.启动InputReader工作线程
result = mReader->start();
...
return OK;
}
/*frameworks/native/services/inputflinger/reader/InputReader.cpp*/
InputReader::InputReader(std::shared_ptr eventHub,
const sp& policy,
const sp& listener)
: mContext(this),
// 初始化EventHub对象
mEventHub(eventHub),
mPolicy(policy),
mGlobalMetaState(0),
mGeneration(1),
mNextInputDeviceId(END_RESERVED_ID),
mDisableVirtualKeysTimeout(LLONG_MIN),
mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
// 持有InputDispatcher的引用
mQueuedListener = new QueuedInputListener(listener);
...
}
status_t InputReader::start() {
if (mThread) {
return ALREADY_EXISTS;
}
// 创建名为“InputReader”的InputThread带loop工作线程,并在其中循环执行loopOnce函数
mThread = std::make_unique(
"InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
return OK;
}
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
bool inputDevicesChanged = false;
std::vector inputDevices;
{
...
// 1\. 从EventHub中监听读取触控事件
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{
...
if (count) {
// 2.处理读取到的触控事件
processEventsLocked(mEventBuffer, count);
}
...
}
...
// 3.mQueuedListener其实就是InputDispatcher对象,flush会通知唤醒InputDispatcher分发事件
mQueuedListener->flush();
}
通过上面流程,输入事件就可以被读取,经过InputReader::processEventsLocked被初步封装成RawEvent,最后通知InputDispatcher分发事件,下节继续分析事件的分发。
从上一节的代码流程分析中可以看到:在新建InputManager对象的时候,不仅创建了事件读取对象InputReader,还创建了事件分发对象InputDispatcher。InputReader在事件读取完毕后,会交给InputDispatcher去执行事件分发的逻辑,而InputDispatcher也拥有自己的独立的工作线程。这样设计的好处在于符合单一职责的设计思想,且事件的读取与分发工作在各自的线程中,处理效率更高,避免出现事件丢失。整个架构模型参见业界大神的一张图:
接上一节代码分析,InputReader::loopOnce函数处理中,在触控事件读取完成后,最后一行会调用 mQueuedListener->flush()。mQueuedListener本质上就是InputDispatcher,所以调用flush动作就是通知InputDispatcher进行事件分发。InputDispatcher和InputReader一样,内部也有一个名为“InputDispatcher”的InputThread的Loop工作线程,当触控事件到来时其被唤醒处理事件。此处流程如下图所示:
简化的代码流程如下:
/*frameworks/native/services/inputflinger/InputListener.cpp*/
void QueuedInputListener::flush() {
size_t count = mArgsQueue.size();
for (size_t i = 0; i < count; i++) {
NotifyArgs* args = mArgsQueue[i];
// 触发调用notify
args->notify(mInnerListener);
delete args;
}
mArgsQueue.clear();
}
void NotifyMotionArgs::notify(const sp& listener) const {
// 就是调用InputDispatcher的notifyMotion
listener->notifyMotion(this);
}
/*frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp*/
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
...
// 1.可以增加业务逻辑,在事件分发前做一些事情
mPolicy->interceptMotionBeforeQueueing(args->displayId, args->eventTime, /*byref*/ policyFlags);
...
bool needWake;
{
...
if (shouldSendMotionToInputFilterLocked(args)) {
...
// 2\. filterInputEvent此处可以过滤触控事件,不再分发
if (!mPolicy->filterInputEvent(&event, policyFlags)) {
return; // event was consumed by the filter
}
}
...
// 3.将触控事件放入"iq"队列等待分发
needWake = enqueueInboundEventLocked(newEntry);
} // release lock
...
if (needWake) {
// 4.唤醒loop工作线程,触发执行一次dispatchOnce逻辑
mLooper->wake();
}
}
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
bool needWake = mInboundQueue.empty();
mInboundQueue.push_back(entry);
// 触控事件放入mInboundQueue队列后,并增加"iq"的systrace tag信息
traceInboundQueueLengthLocked();
...
return needWake;
}
void InputDispatcher::traceInboundQueueLengthLocked() {
if (ATRACE_ENABLED()) {
// mInboundQueue队列对应的systrace tag为“iq”
ATRACE_INT("iq", mInboundQueue.size());
}
}
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{
...
if (!haveCommandsLocked()) {
// 1.具体的事件分发逻辑封装在dispatchOnceInnerLocked中具体处理
dispatchOnceInnerLocked(&nextWakeupTime);
}
...
} // release lock
...
// 2.处理完本次事件分发逻辑后,loop工作线程进入休眠等待状态
mLooper->pollOnce(timeoutMillis);
}
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
...
switch (mPendingEvent->type) {
...
case EventEntry::Type::MOTION: {
...
// 以Motion event屏幕触控事件类型为例,具体的事件分发处理逻辑封装在dispatchMotionLocked函数中
done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
}
...
}
bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
ATRACE_CALL();
...
if (isPointerEvent) {
// 1\. findTouchedWindowTargetsLocked中找到具体接收处理此触控事件的目标窗口
injectionResult =
findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime,
&conflictingPointerActions);
} else {
...
}
...
// 2\. 将此次触控事件分发给目标应用窗口
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}
从以上代码可以看出,对于触控事件的处理核心逻辑分为两步:
下面将分两节来分别分析讲解这两个步骤。
Android系统的Primary Display主显示屏上同一时间总是会存在多个可见窗口,如状态栏、导航栏、应用窗口、应用界面Dialog弹