通过第一篇文章的学习分析,知道触摸事件最终会走到InputDispatcher的notifyMotion方法。
InputDispatcher.cpp#
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
// Just enqueue a new motion event.
//将事件进一步封装
MotionEntry* newEntry = new MotionEntry(args->eventTime,
args->deviceId, args->source, policyFlags,
args->action, args->actionButton, args->flags,
args->metaState, args->buttonState,
args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,
args->displayId,
args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0);
//将事件插入队列
needWake = enqueueInboundEventLocked(newEntry);
if (needWake) {
//如果需要就唤醒InputDispatcherThread
mLooper->wake();
}
}
在notifyMotion方法中,会把事件插入到队列中,然后如果需要就会唤醒InputDispatcherThread线程。
// --- InputDispatcherThread ---
InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) :
Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {
}
InputDispatcherThread::~InputDispatcherThread() {
}
bool InputDispatcherThread::threadLoop() {
//调用InputDispatcher的dispatchOnce方法
mDispatcher->dispatchOnce();
return true;
}
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
AutoMutex _l(mLock);
mDispatcherIsAliveCondition.broadcast();
// Run a dispatch loop if there are no pending commands.
// The dispatch loop might enqueue commands to run afterwards.
if (!haveCommandsLocked()) {
//这里分发输入事件给window
dispatchOnceInnerLocked(&nextWakeupTime);
}
// Run all pending commands if there are any.
// If any commands were run then force the next poll to wake up immediately.
if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN;
}
} // release lock
// Wait for callback or timeout or wake. (make sure we round up, not down)
nsecs_t currentTime = now();
int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
//休眠timeoutMillis时间
mLooper->pollOnce(timeoutMillis);
}
这两个代码都是第一篇文章分析过的,这篇文章的重点是看事件是如何分发给具体的window的,也就是分析dispatchOnceInnerLocked方法。
InputDispatcher.cpp#
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
if (! mPendingEvent) {
if (mInboundQueue.isEmpty()) {
// 事件队列(mInboundQueue)是空的并且没有待处理的事件(mPendingEvent)就直接返回
if (!mPendingEvent) {
return;
}
} else {
//事件队列不为空,就取出队列开头的第一个事件,赋值给mPendingEvent,就是待处理的事件
mPendingEvent = mInboundQueue.dequeueAtHead();
traceInboundQueueLengthLocked();
}
}
// Now we have an event to dispatch.
// All events are eventually dequeued and processed this way, even if we intend to drop them.
bool done = false;
//事件被丢弃的原因,默认是DROP_REASON_NOT_DROPPED不丢弃
DropReason dropReason = DROP_REASON_NOT_DROPPED;
switch (mPendingEvent->type) {
//....
//其他的事件类型都不看了,这里只看触摸事件相关的逻辑
case EventEntry::TYPE_MOTION: {
MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
//继续分发事件
done = dispatchMotionLocked(currentTime, typedEntry,
&dropReason, nextWakeupTime);
break;
}
}
if (done) {
//释放本次分发的mPendingEvent对象
releasePendingEventLocked();
//把nextWakeupTime设置为LONG_LONG_MIN,会使得InputDispatcherThread被尽快的唤醒以便执行下 //一次的分发,nextWakeupTime就是阻塞的超时时间,
*nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
}
}
继续往下看dispatchMotionLocked方法
InputDispatcher.cpp#
bool InputDispatcher::dispatchMotionLocked(
nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
// Preprocessing.
if (! entry->dispatchInProgress) {
//设置一个标记,分发中~
entry->dispatchInProgress = true;
}
// Clean up if dropping the event.
//如果事件是需要被丢弃的,直接返回true
if (*dropReason != DROP_REASON_NOT_DROPPED) {
setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
return true;
}
bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
// inputTargets存储所有的目标窗口信息
Vector<InputTarget> inputTargets;
bool conflictingPointerActions = false;
int32_t injectionResult;
if (isPointerEvent) {
// Pointer event. (eg. touchscreen)
//寻找合适的目标窗口以便处理点击事件,(比如触摸屏幕)
injectionResult = findTouchedWindowTargetsLocked(currentTime,
entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
} else {
// Non touch event. (eg. trackball)
//寻找合适的目标窗口以便处理非触摸形式的事件,(比如轨迹球)
injectionResult = findFocusedWindowTargetsLocked(currentTime,
entry, inputTargets, nextWakeupTime);
}
//输入事件被挂起,说明找到了窗口并且窗口无响应,直接返回false
if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
return false;
}
setInjectionResultLocked(entry, injectionResult);
//返回值不为INPUT_EVENT_INJECTION_SUCCEEDED,表示无法找到合适窗口。例如窗口未获取焦点,或者点击 //位置没落在任何一个窗口内,直接丢弃
if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
//...
return true;
}
//将所有找到的目标窗口都添加到inputTargets中
addMonitoringTargetsLocked(inputTargets);
// Dispatch the motion.
//将事件分发给inputTargets中的窗口
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}
那么是如何为一个事件找到合适的窗口的呢,这里以点击事件为例,分析findTouchedWindowTargetsLocked方法。
InputDispatcher.cpp#
int32_t InputDispatcher::findTouchedWindowTargetsLocked(const MotionEntry* entry, Vector<InputTarget>& inputTargets...) {
if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
/* Case 1: New splittable pointer going down, or need target for hover or scroll. */
// 从MotionEntry中获取事件坐标点
int32_t pointerIndex =getMotionEventActionPointerIndex(action);
int32_t x = int32_t(entry->pointerCoords[pointerIndex].
getAxisValue(AMOTION_EVENT_AXIS_X));
int32_t y = int32_t(entry->pointerCoords[pointerIndex].
getAxisValue(AMOTION_EVENT_AXIS_Y));
// Traverse windows from front to back to find touched window and outside targets.
size_t numWindows = mWindowHandles.size();
//遍历窗口信息找到合适的窗口。
for (size_t i = 0; i < numWindows; i++) {
//InputWindowHandle就是一个窗口对应的信息
sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
const InputWindowInfo* windowInfo = windowHandle->getInfo();
//获取窗口的标记
int32_t flags = windowInfo->layoutParamsFlags;
//如果窗口可见
if (windowInfo->visible) {
//如果窗口的标记是可触摸的会进入这个if分支
if (! (flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
| InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
//窗口状态可触摸并且我们点击的位置落在这个窗口
if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
//发现了,退出循环
newTouchedWindowHandle = windowHandle;
break; // found touched window, exit window loop
}
}
}
}
//如果没有找到合适的窗口
if (newTouchedWindowHandle == NULL) {
//尝试将指针指向找到的第一个前台窗口(如果有)。
newTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle();
if (newTouchedWindowHandle == NULL) {
injectionResult = INPUT_EVENT_INJECTION_FAILED;
goto Failed;
}
}
//把找到的窗口保存到TempTouchState中,以便后续处理
mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
}
//检查mTempTouchState中所有目标窗口是否准备好接受新的输入事件
for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
// 检查窗口是否准备好接受更多输入
String8 reason = checkWindowReadyForMoreInputLocked(currentTime,
touchedWindow.windowHandle, entry, "touched");
if (!reason.isEmpty()) {
// 如果窗口不能接收新事件,则将不能的原因赋值给injectionResult
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
NULL, touchedWindow.windowHandle, nextWakeupTime, reason.string());
goto Unresponsive;
}
}
}
// Success! Output targets.
//走到这里就是找到了窗口!将injectionResult设置为INPUT_EVENT_INJECTION_SUCCEEDED表示成功
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
//为mTempTouchState中的每个窗口信息生成全局的InputTargets
for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
const TouchedWindow& touchedWindow = mTempTouchState.windows.itemAt(i);
addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
touchedWindow.pointerIds, inputTargets);
}
// Drop the outside or hover touch windows since we will not care about them
// in the next iteration.
mTempTouchState.filterNonAsIsTouchWindows();
Failed:
//...
Unresponsive:.
//重置mTempTouchState
mTempTouchState.reset();
return injectionResult;
}
获取到所有的InputTargets后继续分析dispatchEventLocked方法==》
InputDispatcher.cpp#
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
pokeUserActivityLocked(eventEntry);
for (size_t i = 0; i < inputTargets.size(); i++) {
//遍历输入目标
const InputTarget& inputTarget = inputTargets.itemAt(i);
//获取连接索引
ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
if (connectionIndex >= 0) {
//Connection可以理解为InputDispatcher和目标窗口的连接,是用来跨进程通信的
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
//开始事件分发循环,事件最终会被分发到目标窗口
prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
} else {
}
}
}
继续分析~
InputDispatcher.cpp#
void InputDispatcher::prepareDispatchCycleLocked(...) {
enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}
void InputDispatcher::enqueueDispatchEntriesLocked(...) {
startDispatchCycleLocked(currentTime, connection);
}
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection) {
while (connection->status == Connection::STATUS_NORMAL
&& !connection->outboundQueue.isEmpty()) {
// Publish the event.
status_t status;
EventEntry* eventEntry = dispatchEntry->eventEntry;
switch (eventEntry->type) {
case EventEntry::TYPE_MOTION: {
MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
// Publish the motion event.
//发送触摸事件
status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,
motionEntry->deviceId, motionEntry->source,
dispatchEntry->resolvedAction, motionEntry->actionButton,
dispatchEntry->resolvedFlags, motionEntry->edgeFlags,
motionEntry->metaState, motionEntry->buttonState,
xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision,
motionEntry->downTime, motionEntry->eventTime,
motionEntry->pointerCount, motionEntry->pointerProperties,
usingCoords);
break;
}
}
}
最终通过 connection->inputPublisher.publishMotionEven方法将触摸事件跨进程分发到我们的app的目标窗口的根view。