Android触控事件处理机制(基于Android 11)
Posted 冬天的毛毛雨
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android触控事件处理机制(基于Android 11)相关的知识,希望对你有一定的参考价值。
作者:安卓M豆先生
1 概述
用户手指点击按压屏幕后,屏幕触控驱动产生中断,Linux内核会将硬件产生的触控事件包装成Event放在设备的dev/input/目录下。此后android系统需要解决以下几个问题以实现整个触控事件的分发处理:
- 如何从设备上读取触控事件?
- 读取到触控事件后该如何派发事件?
- 派发事件时如何找到接收事件的目标应用窗口?
- 找到目标应用窗口后如何将事件传递到目标窗口?
- 目标应用窗口内部中的事件如何处理?
下面将结合最新Android 11系统源码,通过分析回答这些问题来了解Android系统触控事件处理机制的全貌。
2 触控事件的读取
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>& 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<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& 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<EventHubInterface> eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& 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<InputThread>(
"InputReader", [this]() loopOnce(); , [this]() mEventHub->wake(); );
return OK;
void InputReader::loopOnce()
int32_t oldGeneration;
int32_t timeoutMillis;
bool inputDevicesChanged = false;
std::vector<InputDeviceInfo> 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分发事件,下节继续分析事件的分发。
3 触控事件的分发
从上一节的代码流程分析中可以看到:在新建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<InputListenerInterface>& 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;
从以上代码可以看出,对于触控事件的处理核心逻辑分为两步:
- 首先通过findTouchedWindowTargetsLocked找到处理此次触控事件的目标应用窗口;
- 下来通过dispatchEventLocked将触控事件发送到目标窗口。
下面将分两节来分别分析讲解这两个步骤。
4 寻找触控事件的目标窗口
Android系统的Primary Display主显示屏上同一时间总是会存在多个可见窗口,如状态栏、导航栏、应用窗口、应用界面Dialog弹框窗口等。用adb shell dumpsys SurfaceFlinger命令可以看到:
Display 19260578257609346 HWC layers:
-----------------------------------------------------------------------------------------------------------------------------------------------
Layer name
Z | Window Type | Layer Class | Comp Type | Transform | Disp Frame (LTRB) | Source Crop (LTRB) | Frame Rate (Explicit) [Focused]
-----------------------------------------------------------------------------------------------------------------------------------------------
com.android.systemui.ImageWallpaper#0
rel 0 | 2013 | 0 | DEVICE | 0 | 0 0 1080 2400 | 0.0 0.0 1080.0 2400.0 | [ ]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
cn.nubia.launcher/com.android.launcher3.Launcher#0
rel 0 | 1 | 0 | DEVICE | 0 | 0 0 1080 2400 | 0.0 0.0 1080.0 2400.0 | [*]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FloatBar#0
rel 0 | 2038 | 0 | DEVICE | 0 | 1065 423 1080 623 | 0.0 0.0 15.0 200.0 | [ ]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
StatusBar#0
rel 0 | 2000 | 0 | DEVICE | 0 | 0 0 1080 111 | 0.0 0.0 1080.0 111.0 | [ ]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Android事件处理