文章目录
Android Input系统事件获取与分发简单总结
1.Input系统的角色和重要性
Input是Android系统中的一个重要模块,它是负责处理用户输入操作的核心组件。该系统从各种输入设备获取原始输入事件,并将其转换为Android应用可以理解和消费的KeyEvent或MotionEvent对象。当文档中详细解释了Input系统的角色和重要性,以及它的主要组成部分,包括InputReader、InputDispatcher等。同时,还简单介绍了输入事件的来源,包括硬件设备和软件模拟,以及不同类型的输入事件,如按键事件和触摸事件。
2.Input简介
Input是Android系统中的一个重要模块,它是负责处理用户输入操作的核心组件。该系统从各种输入设备获取原始输入事件,并将其转换为Android应用可以理解和消费的KeyEvent或MotionEvent对象。当文档中详细解释了Input系统的角色和重要性,以及它的主要组成部分,包括InputReader、InputDispatcher等。同时,还简单介绍了输入事件的来源,包括硬件设备和软件模拟,以及不同类型的输入事件,如按键事件和触摸事件。
具体来说,InputManagerService通过以下几个方面实现了对用户输入事件的管理和处理:
- Input系统是Android系统中负责处理用户输入操作的核心组件,它负责从各种输入设备(如屏幕、键盘、鼠标等)获取原始的输入事件(如按键、触摸、滑动等),并将其转换为Android应用可以理解和消费的KeyEvent或MotionEvent对象。
- Input系统对于提供流畅、灵敏和一致的用户交互体验至关重要,它需要在不同的设备、场景和应用中保证输入事件的正确性、及时性和安全性。
- 输入事件处理:InputManagerService会对接收到的输入事件进行预处理和解析,以便提取出有用的信息,并根据需要进行转换或修正。例如,对于触摸事件,它会计算出触摸点的坐标和压力值。
- 输入焦点管理:InputManagerService跟踪当前具有输入焦点的应用程序或窗口,并确保只有具有焦点的应用程序才能接收和处理输入事件。它还负责处理焦点的切换和转移。
3.Input系统的主要组成部分
类名 | 功能 | 位置 |
---|---|---|
InputReader | 从EventHub读取事件,进行预处理和分类,生成Event对象,通过notify方法通知InputDispatcher | Native |
InputDispatcher | 接收InputReader数据,进行策略判断与分发,将事件分发给对应的窗口或应用 | Native |
InputEventReceiver | 接收natvie层inputdispatcher数据,进一步分发 | java |
View | 通过dispatchPointerEvent将事件分发给应用 | java |
ViewGroup | 获取view状态并根据情况判断是否继续分发 | java |
Activity | 点击事件处理 | java |
- EventHub将所有的输入事件通过一个接口getEvents()把从多个输入设备节点中读取的事件交给InputReader,它是输入系统最底层。偏驱动侧。本文档不做过多介绍。
- InputReader是Android系统中的一个重要组件,用于接收用户输入事件并将其传递给应用程序。它负责从输入设备(如触摸屏、键盘、鼠标等)读取输入事件,并将其转换为Android系统能够理解的格式。
InputReader通过调用底层输入设备驱动程序来读取输入事件,然后将其传递给InputDispatcher。InputDispatcher负责将输入事件分发给相应的应用程序或系统组件。
InputReader还负责处理输入设备的各种参数设置,如采样率、灵敏度、校准等。它还能够识别多点触控、手势等高级输入事件,并将其传递给系统进行处理。- InputDispatcher是Android系统中的一个关键组件,负责管理和分发用户输入事件。它接收来自InputReader的输入事件,并将其分发给相应的应用程序或系统组件进行处理。
InputDispatcher会根据输入事件的类型和目标应用程序的焦点状态,将事件发送到正确的目标。例如,如果用户点击了屏幕上的一个按钮,InputDispatcher会将点击事件发送给当前具有焦点的应用程序,以触发相应的操作。
InputDispatcher还负责处理多个应用程序同时请求焦点的情况,以及处理输入事件的优先级和顺序。它确保用户输入事件按照正确的顺序被处理,以确保系统的稳定性和响应性。
总的来说,InputDispatcher在Android系统中扮演着重要的角色,协调和管理用户输入事件的传递,以确保系统能够准确、高效地响应用户操作。- Java层各组件,InputEventReceiver是一个用于接收来自InputDispatcher(输入调度器)的输入事件的类。它的主要作用是接收原始输入事件,然后将其发送到指定的HandlerThread进行处理。
4.InputReader详解
4.1.InputReader时序图
4.2.InputReader流程分析
InputReader线程启动后,调用loopOnce方法
//frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::loopOnce() {
//省略
//通过getevent获取数据
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{ // acquire lock
AutoMutex _l(mLock);
mReaderIsAliveCondition.broadcast();
if (count) {
processEventsLocked(mEventBuffer, count);//处理数据
}
...
} // release lock
// Send out a message that the describes the changed input devices.
if (inputDevicesChanged) {
mPolicy->notifyInputDevicesChanged(inputDevices);
}
...
mQueuedListener->flush();//数据交给InputDispatcher线程
getEvent中主要就是扫描设备,获取设备节点,从节点中读取数据buffer,这块偏向驱动侧,此文档不过多介绍。
//frameworks\native\services\inputflinger\reader\InputReader.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) {//输入事件的type肯定远小于0x10000000,满足条件
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);//1
//frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,
size_t count) {
auto deviceIt = mDevices.find(eventHubId);//取出InputDevice
std::shared_ptr<InputDevice>& device = deviceIt->second;
device->process(rawEvents, count);//1
}
//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) {
//省略
} else {
//省略
}
} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
//省略
} else {
for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {
mapper.process(rawEvent);
});
}
--count;
}
}
找出InputDevice对应的mapper(之前在处理输入设备的时候,设置过mappers集合),调用这些mapper的process方法。对于单指摸屏,mapper为TouchInputMapper.
TouchInputMapper中所有上报的数据中,仅有id能代表手指,所有从驱动上报的数据均保存在PointerCoords和PointerProperties数组中,
//frameworks\native\services\inputflinger\reader\mapper\TouchInputMapper.cpp
void TouchInputMapper::process(const RawEvent* rawEvent) {
//省略
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
sync(rawEvent->when);
}
}
//frameworks\native\services\inputflinger\reader\mapper\TouchInputMapper.cpp
void TouchInputMapper::sync(nsecs_t when) {
const RawState* last =
mRawStatesPending.empty() ? &mCurrentRawState : &mRawStatesPending.back();
// Push a new state.
mRawStatesPending.emplace_back();//往mRawStatesPending的尾部插入一个空的RawState
RawState* next = &mRawStatesPending.back();//将next指向mRawStatesPending的最后一个
next->clear();
next->when = when;
//省略
// Sync touch
syncTouch(when, next);//写数据 RawPointerData
// Assign pointer ids.
if (!mHavePointerIds) {
assignPointerIds(last, next);
}
processRawTouches(false /*timeout*/);//处理数据
}
//frameworks\native\services\inputflinger\reader\mapper\MultiTouchInputMapper.cpp
void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
size_t outCount = 0;
BitSet32 newPointerIdBits;
mHavePointerIds = true;
for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
const MultiTouchMotionAccumulator::Slot* inSlot =
mMultiTouchMotionAccumulator.getSlot(inIndex);//遍历取出Slot
if (!inSlot->isInUse()) {
continue;
}
//省略
/*根据slot的数据,开始填充RawState*/
RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
outPointer.x = inSlot->getX();
outPointer.y = inSlot->getY();
outPointer.pressure = inSlot->getPressure();
outPointer.touchMajor = inSlot->getTouchMajor();
outPointer.touchMinor = inSlot->getTouchMinor();
outPointer.toolMajor = inSlot->getToolMajor();
outPointer.toolMinor = inSlot->getToolMinor();
outPointer.orientation = inSlot->getOrientation();
outPointer.distance = inSlot->getDistance();
outPointer.tiltX = 0;
outPointer.tiltY = 0;
outPointer.toolType = inSlot->getToolType();
//省略
/*开始处理id和index*/
// Assign pointer id using tracking id if available.
if (mHavePointerIds) {
int32_t trackingId = inSlot->getTrackingId();
int32_t id = -1;
if (trackingId >= 0) {
for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
uint32_t n = idBits.clearFirstMarkedBit();
if (mPointerTrackingIdMap[n] == trackingId) {
id = n;
}
}
if (id < 0 && !mPointerIdBits.isFull()) {
id = mPointerIdBits.markFirstUnmarkedBit();
mPointerTrackingIdMap[id] = trackingId;
}
}
if (id < 0) {
mHavePointerIds = false;
outState->rawPointerData.clearIdBits();
newPointerIdBits.clear();
} else {
outPointer.id = id;
outState->rawPointerData.idToIndex[id] = outCount;
outState->rawPointerData.markIdBit(id, isHovering);
newPointerIdBits.markBit(id);
}
}
outCount += 1;
}
outState->rawPointerData.pointerCount = outCount;
mPointerIdBits = newPointerIdBits;
mMultiTouchMotionAccumulator.finishSync();//清空Slot
}
syncTouch 方法中主要做的就是数据填充,将从驱动获取的源数据加入到集合中。
继续回到sync方法,syncTouch处理完成后,调用processRawTouches继续处理数据
//frameworks\native\services\inputflinger\reader\mapper\TouchInputMapper.cpp
void TouchInputMapper::processRawTouches(bool timeout) {
//省略
const size_t N = mRawStatesPending.size();//前面已经填充过mRawStatesPending了,这里取出来
size_t count;
for (count = 0; count < N; count++) {
const RawState& next = mRawStatesPending[count];//遍历取出RawState
//省略
mCurrentRawState.copyFrom(next);//将数据拷贝到mCurrentRawState中
if (mCurrentRawState.when < mLastRawState.when) {
mCurrentRawState.when = mLastRawState.when;
}
cookAndDispatch(mCurrentRawState.when);//数据处理及分发
}
if (count != 0) {
mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count);//处理完之后要擦除
}
//省略
//frameworks\native\services\inputflinger\reader\mapper\TouchInputMapper.cpp
void TouchInputMapper::cookAndDispatch(nsecs_t when) {
mCurrentCookedState.clear();
cookPointerData();//数据处理,一套算法将处理x,y轴计算成显示屏对应坐标
if (mDeviceMode == DEVICE_MODE_POINTER) {
//省略
}else{
if (!mCurrentMotionAborted) {
dispatchButtonRelease(when, policyFlags);
dispatchHoverExit(when, policyFlags);
dispatchTouches(when, policyFlags);//分发
dispatchHoverEnterAndMove(when, policyFlags);
dispatchButtonPress(when, policyFlags);
}
}
//省略
// Copy current touch to last touch in preparation for the next cycle.
mLastRawState.copyFrom(mCurrentRawState);//将这次的数据拷贝到mLastRawState
mLastCookedState.copyFrom(mCurrentCookedState);//拷贝数据
}
cookPointerData方法比较长,主要是对数据进行加工,加工后的数据放入mCurrentCookedState中。为什么要进行加工呢?因为当前的数据,比如X,Y坐标还是针对触摸屏的,我们要加工成显示屏上对应的坐标。处理完后,将数据放入mCurrentCookedState中。
dispatchTouches中主要调用dispatchMotion方法,将mCurrentCookedState等数据传递过去。用于数据分发。
void TouchInputMapper::dispatchMotion123(nsecs_t when, uint32_t policyFlags, uint32_t source,
int32_t action, int32_t actionButton, int32_t flags,
int32_t metaState, int32_t buttonState, int32_t edgeFlags,
const PointerProperties* properties,
const PointerCoords* coords, const uint32_t* idToIndex,
BitSet32 idBits, int32_t changedId, float xPrecision,
float yPrecision, nsecs_t downTime) {
// ALOGE("houyangInputTouch---start!!!---dispatchMotion123---action:%d---changedId:%d", action,changedId);
/*
char propDoubelScreenTouched[100];
int mDoubleTouchFlag = 1;
property_get("persist.sys.clickscreen", propDoubelScreenTouched, 0);
mDoubleTouchFlag = atoi(propDoubelScreenTouched);
*/
//获取分屏属性
char propVirturalDisplayStatus[100];
int mVirtualDisplayFlag = 0;
property_get("persist.sys.virtualdisplay", propVirturalDisplayStatus, 0);
mVirtualDisplayFlag = atoi(propVirturalDisplayStatus);
PointerCoords pointerCoords[MAX_POINTERS];
PointerProperties pointerProperties[MAX_POINTERS];
uint32_t pointerCount = 0;
PointerCoords display2PointerCoords[MAX_POINTERS];
PointerProperties display2PointerProperties[MAX_POINTERS];
uint32_t display2PointerCount = 0;
int32_t display2Action = -1;
uint32_t touchId = -1;
//changeId: 区分当前事件类型,upid,downid,moveid。 默认为-1,从cook中传递下来
//index 代表集中中位置
//display2PointerProperties 原始数据,需要通过notify方法传递至InputDispatcher
//display2PointerCoords 处理后数据
//action,事件类型
while (!idBits.isEmpty()) {
uint32_t id = idBits.clearFirstMarkedBit();
uint32_t index = idToIndex[id];
ALOGE("houyangInputTouch---dispatchMotion123---index:%d---id:%d---changedId:%d", index, id, changedId);
//ALOGE("houyangInputTouch---dispatchMotion123---mVirtualDisplayFlag:%d---postionXXX:%f", mVirtualDisplayFlag, coords[index].getAxisValue(AMOTION_EVENT_AXIS_X));
if (mVirtualDisplayFlag == 1 && coords[index].getAxisValue(AMOTION_EVENT_AXIS_X) > VIRTUALDISPLAY_TOUCH_COORDINATE_U6X) {
//ALOGE("houyangInputTouch---enter display2 touch judge");
display2PointerProperties[display2PointerCount].copyFrom(properties[index]);
display2PointerCoords[display2PointerCount].copyFrom(coords[index]);
display2PointerCoords[display2PointerCount].setAxisValue(AMOTION_EVENT_AXIS_X, coords[index].getX() - VIRTUALDISPLAY_TOUCH_COORDINATE_U6X);
display2Action = action;
if (changedId >= 0 && id == uint32_t(changedId)) {
touchId = 2;
display2Action |= display2PointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
}
display2PointerCount += 1;
} else {
//ALOGE("houyangInputTouch---enter display0 touch judge");
pointerProperties[pointerCount].copyFrom(properties[index]);
pointerCoords[pointerCount].copyFrom(coords[index]);
if (changedId >= 0 && id == uint32_t(changedId)) {
touchId = 0;
action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
}
pointerCount += 1;
}
}
ALOG_ASSERT(pointerCount != 0);
//ALOGE("houyangInputTouch---dispatchMotion123---before_touchId:%d", touchId);
//ALOGE("houyangInputTouch---dispatchMotion123---before_dispay0_action000:%d,pointerCount:%d", action, pointerCount);
//ALOGE("houyangInputTouch---dispatchMotion123---before_dispay2_display2Action222:%d,display2PointerCount:%d", display2Action, display2PointerCount);
if (mVirtualDisplayFlag == 1) {
if (changedId >= 0 && display2PointerCount == 1 && touchId == 2) { //分屏模式下处理副屏单指
if (display2Action == AMOTION_EVENT_ACTION_POINTER_DOWN) {
display2Action = AMOTION_EVENT_ACTION_DOWN;
} else if (display2Action == AMOTION_EVENT_ACTION_POINTER_UP) {
display2Action = AMOTION_EVENT_ACTION_UP;
} else {
// Can't happen.
ALOG_ASSERT(false);
}
} else if (changedId >= 0 && pointerCount == 1 && touchId == 0) {//分屏模式下处理主屏单指
if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) {
action = AMOTION_EVENT_ACTION_DOWN;
} else if (action == AMOTION_EVENT_ACTION_POINTER_UP) {
action = AMOTION_EVENT_ACTION_UP;
} else {
// Can't happen.
ALOG_ASSERT(false);
}
}
} else {
if (changedId >= 0 && pointerCount == 1) { //非分屏模式下正常处理
// Replace initial down and final up action.
// We can compare the action without masking off the changed pointer index
// because we know the index is 0.
if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) {
action = AMOTION_EVENT_ACTION_DOWN;
} else if (action == AMOTION_EVENT_ACTION_POINTER_UP) {
action = AMOTION_EVENT_ACTION_UP;
} else {
// Can't happen.
ALOG_ASSERT(false);
}
}
}
//ALOGE("houyangInputTouch---dispatchMotion123---after_dispay0_action000:%d,pointerCount:%d", action, pointerCount);
//ALOGE("houyangInputTouch---dispatchMotion123---after_dispay2_display2Action222:%d,display2PointerCount:%d", display2Action, display2PointerCount);
float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
if (mDeviceMode == DEVICE_MODE_POINTER) {
mPointerController -> getPosition( & xCursorPosition, &yCursorPosition);
}
const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
const int32_t deviceId = getDeviceId();
std::vector < TouchVideoFrame > frames = getDeviceContext().getVideoFrames();
std::for_each (frames.begin(), frames.end(),
[this](TouchVideoFrame & frame) {frame.rotate(this->mSurfaceOrientation); });
//ALOGE("houyangInputTouch---display0PointerCount:%d---display2PointerCount:%d", pointerCount, display2PointerCount);
//ALOGE("houyangInputTouch---finish_action---display0Action:%d---display2Action:%d----pointerCount:%d---display2PointerCount:%d",
// action, display2Action, pointerCount, display2PointerCount);
/*
//ka dun!!!! remove it after java screenTouchNum received ,now java cant receive screenTouchNum
if (pointerCount > 0 && display2PointerCount > 0 && mDoubleTouchFlag != 2) {
property_set("persist.sys.clickscreen", "2");
} else if ((pointerCount == 0 || display2PointerCount == 0) && mDoubleTouchFlag != 1){
property_set("persist.sys.clickscreen", "1");
}
*/
//处理屏幕数量
int32_t screenTouchNum = 1;
if (pointerCount > 0 && display2PointerCount > 0) {
screenTouchNum =2 ;
}
//ALOGE("houyangInputTouch---finish_action---moreScreenTouched:%d---screenTouchNum:%d",moreScreenTouched,screenTouchNum);
if (pointerCount > 0) {
NotifyMotionArgs args(getContext()->getNextId(), when, deviceId, source, displayId, policyFlags,
action, actionButton, flags, metaState, buttonState,
MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
downTime, std::move(frames), screenTouchNum);
//ALOGE("hhj---RotaryEncoderInputMapper---setValue---main1");
//主屏数据传递至InputDispatcher
getListener()->notifyMotion(&args);
}
if (display2PointerCount > 0) {
NotifyMotionArgs display2Args (getContext()->
getNextId(), when, deviceId, source, DISPLAYID_VIRTUALDISPLAY, policyFlags,
display2Action, actionButton, flags, metaState, buttonState,
MotionClassification::NONE, edgeFlags, display2PointerCount, display2PointerProperties,
display2PointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
downTime, std::move (frames), screenTouchNum);
//ALOGE("hhj---RotaryEncoderInputMapper---setValue---main2");
//副屏数据传递至InputDispatcher
getListener()->notifyMotion( & display2Args);
}
}
- 通过idBits 获取id. 通过id获取index,通过index获取集合中数据,后续通过notifymotion方法传递至InputDispatcher.
- 分屏模式下,主屏逻辑与副屏逻辑单独处理,创建两套完全独立的数据集合,并分别传递至InputDispatcher
4.3.InputReader简要概括总结
InputReader模块主要负责从节点文件中读取输入数据,并在TouchInputMapper中对数据进行处理后通过notifymotion方法数据交给InputDispatcher线程。
InputReader线程主要完成以下工作:
1.处理已有的输入设备
2.处理新增或者移除的输入设备
3.对输入设备产生的输入数据进行处理
5.InputDispatcher详解
5.1.InputDispatcher时序图
5.2.InputDispatcher简要流程图
5.3.InputDispatcher流程分析
dispatchOnce流程
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();
return true;
}
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
std::scoped_lock _l(mLock);
mDispatcherIsAlive.notify_all();
//通过dispatchOnceInnerLocked进行输入事件分发,传出参数nextWakeupTime决定下次派发循环的时间点
if (!haveCommandsLocked()) {
dispatchOnceInnerLocked(&nextWakeupTime);
}
//执行命令队列中的命令
if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN;//设置nextWakeupTime为立即开始执行下次线程循环
}
} // release lock
// 计算需要休眠的时间timeoutMillis,并通过pollOnce进入epoll_wait
nsecs_t currentTime = now();
int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
mLooper->pollOnce(timeoutMillis);
}
- 判断commandQueue中是否存在数据
- 通过dispatchOnceInnerLocked进行输入事件分发,传出参数nextWakeupTime决定下次派发循环的时间点
dispatchOnceInnerLocked流程
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
...
//当分发被冻结,则不再处理超时和分发事件的工作,
//setInputDispatchMode可以使InputDispatcher在禁用、冻结、正常状态切换
if (mDispatchFrozen) {
if (DEBUG_FOCUS) {
ALOGD("Dispatch frozen. Waiting some more.");
}
return;
}
//优化app切换延迟,当切换超时,则抢占分发,丢弃其他所有即将要处理的事件
//如果isAppSwitchDue为true,说明没有及时响应HOME键等操作
bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
if (mAppSwitchDueTime < *nextWakeupTime) {
*nextWakeupTime = mAppSwitchDueTime;
}
//如果还没有待分发的事件,去mInboundQueue中取出一个事件
if (! mPendingEvent) {
//如果mInboundQueue为空,并且没有待分发的事件,就return
if (mInboundQueue.isEmpty()) {
...
} else {
//如果mInboundQueue不为空,取队列头部的EventEntry赋值给mPendingEvent
mPendingEvent = mInboundQueue.dequeueAtHead();
}
//重置ANR信息.
resetANRTimeoutsLocked();
}
//检查事件是否需要丢弃,dropReason描述了丢弃原因
bool done = false;
DropReason dropReason = DROP_REASON_NOT_DROPPED;
if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
//在事件派发前调用过interceptInputEvent并设置POLICY_FLAG_PASS_TO_USER,则丢弃
dropReason = DROP_REASON_POLICY;
} else if (!mDispatchEnabled) {
//如果InputDispatcher被禁用,通过setInputDispatchMode设置,则此事件也会被丢弃
dropReason = DROP_REASON_DISABLED;
} ...
switch (mPendingEvent->type) {
...
case EventEntry::TYPE_KEY: {
KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
...
done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
case EventEntry::TYPE_MOTION: {
MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
//事件因为home键没有能被及时响应丢弃
if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
dropReason = DROP_REASON_APP_SWITCH;
}
//事件因为过期丢弃
if (dropReason == DROP_REASON_NOT_DROPPED
&& isStaleEvent(currentTime, typedEntry)) {
dropReason = DROP_REASON_STALE;
}
//事件因为阻碍了其他窗口获得事件丢弃
if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
dropReason = DROP_REASON_BLOCKED;
}
//执行dispatchMotionLocked进行Motion事件的派发,如果派发完成,无论成功派发还是事件被丢弃,都会返回true
//失败则返回false,在下次循环时再次尝试此事件派发
done = dispatchMotionLocked(currentTime, typedEntry,
&dropReason, nextWakeupTime);
break;
}
...
if (done) {
if (dropReason != DROP_REASON_NOT_DROPPED) {
dropInboundEventLocked(mPendingEvent, dropReason);
}
mLastDropReason = dropReason;
//设置mPendingEvent对象为null,使在下次循环时可以处理派发队列中的下一条事件
releasePendingEventLocked();
//使得InputDispatcher能够快速处理下一个分发事件
//因为当派发队列为空时,派发线程可能需要在下次循环中生成重复按键事件,因此不能直接进入休眠
*nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
}
}
//设置mDispatchEnabled 和mDispatchFrozen 的方法。
void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) {
if (DEBUG_FOCUS) {
ALOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
}
bool changed;
{ // acquire lock
std::scoped_lock _l(mLock);
if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
if (mDispatchFrozen && !frozen) {
resetNoFocusedWindowTimeoutLocked();
}
if (mDispatchEnabled && !enabled) {
resetAndDropEverythingLocked("dispatcher is being disabled");
}
mDispatchEnabled = enabled;
mDispatchFrozen = frozen;
changed = true;
} else {
changed = false;
}
if (DEBUG_FOCUS) {
logDispatchStateLocked();
}
} // release lock
if (changed) {
// Wake up poll loop since it may need to make new input dispatching choices.
mLooper->wake();
}
}
- 如果派发队列为空,则不会继续派发操作
- InputDispatcher的冻结处理
如果当前InputDispatcher被冻结,则不进行派发操作,InputDispatcher有三种状态,分别是正常状态、冻结状态(frozen)和禁用状态(disable),可以通过InputDispatcher的setInputDispatchMode函数来设置- 窗口切换操作处理
mAppSwitchDueTime ,代表了App最近发生窗口切换操作时(比如按下Home键、挂断电话),该操作事件最迟的分发时间,当事件分发的时间点距离该事件加入mInboundQueue的时间超过500ms,则认为app切换过期,即isAppSwitchDue=true。
如果mAppSwitchDueTime小于nextWakeupTime(下一次InputDispatcherThread醒来的时间),就将mAppSwitchDueTime赋值给nextWakeupTime,这样当InputDispatcher处理完分发事件后,会第一时间处理窗口切换操作- 取出事件
如果没有待分发的事件,就从mInboundQueue中取出一个事件,如果mInboundQueue为空,并且没有待分发的事件,就return,如果mInboundQueue不为空,取队列头部的EventEntry赋值给mPendingEvent,mPendingEvent的类型为EventEntry对象指针- 事件丢弃
dropReason代表了事件丢弃的原因,它的默认值为DROP_REASON_NOT_DROPPED,代表事件不被丢弃。
根据mPendingEvent的type做区分处理,这里主要截取了对Motion类型的处理。经过过滤,会调用dispatchMotionLocked函数为这个事件寻找合适的窗口,即继续分发操作。- 后续处理
执行dispatchMotionLocked进行Motion事件的派发,如果派发完成,无论成功派发还是事件被丢弃,都会返回true,否则返回false,在下次循环时再次尝试此事件派发。如果dispatchMotionLocked事件分发成功,则会调用releasePendingEventLocked函数,其内部会将mPendingEvent的值设置为Null,并将mPendingEvent指向的对象内存释放掉。将nextWakeupTime的值设置为LONG_LONG_MIN,这是为了让InputDispatcher能够快速处理下一个分发事件
dispatchMotionLocked流程
bool InputDispatcher::dispatchMotionLocked(
nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
//标记事件已经正式进入派发流程
if (! entry->dispatchInProgress) {
entry->dispatchInProgress = true;
}
//如果事件是需要丢弃的,则返回true,不会去为该事件寻找合适的窗口
if (*dropReason != DROP_REASON_NOT_DROPPED) {
setInjectionResult(entry, *dropReason == DROP_REASON_POLICY
? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
return true;
}
bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
//目标窗口信息列表会存储在inputTargets中
std::vector<InputTarget> inputTargets;
bool conflictingPointerActions = false;
int32_t injectionResult;
if (isPointerEvent) {
//处理点击形式的事件,比如触摸屏幕
injectionResult = findTouchedWindowTargetsLocked(currentTime,
entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
} else {
//处理非触摸形式的事件
injectionResult = findFocusedWindowTargetsLocked(currentTime,
entry, inputTargets, nextWakeupTime);
}
//输入事件被挂起,说明找到了窗口并且窗口无响应
if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
return false;
}
setInjectionResult(entry, injectionResult);
//返回值不为SUCCEEDED,表示无法找到合适窗口,例如窗口未获取焦点,或点击位置没落在任何一个窗口内,此事件将被直接丢弃
if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
...
return true;
}
//分发目标添加到inputTargets列表中
addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
...
//将事件派发给inputTargets列表中的目标
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}
- 对于被丢弃的事件直接返回true。
- 给事件寻找合适的目标窗口,目标窗口分为普通窗口和监听窗口(monitoring),普通窗口通过按点查找与按焦点查找两种方式获得,而监听窗口则无条件监听所有输入事件。普通窗口的查找结果决定了此次线程循环是否可以完成事件派发
- 如果成功找到可以接收事件的目标窗口,则调用dispatchEventLocked()函数进行派发
- 查找到的派发目标存储在InputTarget结构体中
findTouchedWindowTargetsLocked流程
int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
const MotionEntry* entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,bool* outConflictingPointerActions) {
// For security reasons, we defer updating the touch state until we are sure that
// event injection will be allowed.
int32_t displayId = entry.displayId;
int32_t action = entry.action;
//点击类型,down/up/point_down/point_up/move
int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
//ALOGE("findTouchWindowTrgetsLocked---displayId:%d action:%d maskedAction:%d",displayId,action,maskedAction);
// Update the touch state as needed based on the properties of the touch event.
int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING;
InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
sp<InputWindowHandle> newHoverWindowHandle;
// Copy current touch state into tempTouchState.
// This state will be used to update mTouchStatesByDisplay at the end of this function.
// If no state for the specified display exists, then our initial state will be empty.
const TouchState* oldState = nullptr;
//所有的属性都会保存到此变量中,后续会通过其中的属性做逻辑判断
TouchState tempTouchState;
std::unordered_map<int32_t, TouchState>::iterator oldStateIt =
mTouchStatesByDisplay.find(displayId);
if (oldStateIt != mTouchStatesByDisplay.end()) {
oldState = &(oldStateIt->second);
tempTouchState.copyFrom(*oldState);
//TouchState& state = oldStateIt->second;
//int a = static_cast<int>(state.windows.size());
// ALOGE("state.windows.size:%d",a);
// ALOGE("state.windows.displayId:%d---down:%d",state.displayId,state.down);
}
...
if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
// 从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));
bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
//此处为获取当前触摸window!
sp<InputWindowHandle> newTouchedWindowHandle = findTouchedWindowAtLocked(
displayId, x, y, isDown /*addOutsideTargets*/, true /*addPortalWindows*/);
...
//把选中的窗口保存到TempTouchState中,以便后续处理
mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
...
}
...
//遍历当前temptouchstate中的window.
bool haveForegroundWindow = false;
for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
//是否是前台
if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
// ALOGE("houyangInputDispatcher---InputDispatcher----targetFlags:%d",touchedWindow.targetFlags);
haveForegroundWindow = true;
//权限判断
if (!checkInjectionPermission(touchedWindow.windowHandle, entry.injectionState)) {
injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
injectionPermission = INJECTION_PERMISSION_DENIED;
// ALOGE("GOFail---1");
goto Failed;
}
}else {
// ALOGE("houyangInputDispatcher---InputDispatcher----targetFlags not null");
}
}
//int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
// int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
// ALOGE("houyangInputDispatcher---haveForegroundWindow:%d---hasGestureMonitor:%d",haveForegroundWindow,hasGestureMonitor);
// ALOGE("houyangInputDispatcher---x:%d---y:%d",x,y);
//若没有前台window
if (!haveForegroundWindow) {
ALOGI("Dropping event because there is no touched foreground window in display "
"%" PRId32 " or gesture monitor to receive it123321.",
displayId);
injectionResult = INPUT_EVENT_INJECTION_FAILED;
// ALOGE("GOFail---2");
goto Failed;
}
...
//如果执行到这里,说明窗口的查找过程一切顺利,设置injectionResult为SUCCEEDED,并将injectionResult放入参数inputTargets中
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
//更新inputTargets
for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
touchedWindow.pointerIds, inputTargets);
}
for (const TouchedMonitor& touchedMonitor : tempTouchState.gestureMonitors) {
addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset,
touchedMonitor.yOffset, inputTargets);
}
...
if (!wrongDevice) {
//phase v 多屏添加,获取touchinputmapper中设置的属性值
//ALOGE("InputDispatcher---wrongDevice---moreScreenTouched:%d",entry.moreScreenTouched);
int32_t mMoreScreenTouched = entry.moreScreenTouched;
char propClickStatus[100];
int mClickFlag = 0;
property_get("persist.sys.clickscreen", propClickStatus, 0);
mClickFlag = atoi(propClickStatus);
//ALOGE("tempTouchState---mClickFlag:%d",mClickFlag);
if (switchedDevice) {
if (DEBUG_FOCUS) {
ALOGD("Conflicting pointer actions: Switched to a different device.");
}
*outConflictingPointerActions = true;
}
//ALOGE("tempTouchState---!isHoverAction:%d---maskedAction:%d",!isHoverAction,maskedAction);
//类型
if (maskedAction == AMOTION_EVENT_ACTION_UP ||
maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
// All pointers up or canceled.
//初始化
tempTouchState.reset();
} else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
// First pointer went down.
if (oldState && oldState->down) {
if (DEBUG_FOCUS) {
ALOGD("Conflicting pointer actions: Down received while already down.");
}
*outConflictingPointerActions = true;
}
} else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP && mMoreScreenTouched != 2) {
// One pointer went up.
if (isSplit) {
int32_t pointerIndex = getMotionEventActionPointerIndex(action);
uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
//add if judge
//if(pointerId!=0 && pointerIndex!=0){
for (size_t i = 0; i < tempTouchState.windows.size();) {
TouchedWindow& touchedWindow = tempTouchState.windows[i];
//int position = static_cast<int>(i);
//ALOGE("tempTouchState---Touched---position:%d",position);
//ALOGE("tempTouchState---Touched window was removed: %s in pointerId %d pointerIndex %d displayId %" PRId32,
// touchedWindow.windowHandle->getName().c_str(), pointerId
// ,pointerIndex,displayId);
if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
//ALOGE("tempTouchState---for---1111---clearBit-pointerId:%d tempTouchState.windows.size:%d",pointerId,static_cast<int>(tempTouchState.windows.size()));
touchedWindow.pointerIds.clearBit(pointerId);
if (touchedWindow.pointerIds.isEmpty()) {
ALOGE("tempTouchState---for---eraseeraseerase!!!");
//清空此window.将不会再往该window上分发
tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
continue;
} else {
//ALOGE("tempTouchState---else---222");
}
}else {
//ALOGE("tempTouchState---else---111");
}
i += 1;
}
//}
}
}
}
return injectionResult;
}
该函数主要目的用于获取所有窗口信息,为查找输入窗口提供依据,因此每当窗口形态发生变化是,WMS会通过IMS将所有窗口提交到InputDispatcher,完成InputWindowHandle的更新。(更新窗口为WMS内容,此块不过分涉及)
- 根据窗口点击坐标和事件发生坐标选择合适的目标窗口
- 获取前台window,若无前台window则返回异常injectionResult
- 根据点击类型部分选择性清空tempTouchState
dispatchEventLocked流程
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry,
const std::vector<InputTarget>& inputTargets) {
// ALOGE("dispatchEventToCurrentInputTargets----start");
ATRACE_CALL();
#if DEBUG_DISPATCH_CYCLE
ALOGD("dispatchEventToCurrentInputTargets");
#endif
ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
for (const InputTarget& inputTarget : inputTargets) {
//遍历当前inputTargets
sp<Connection> connection =
getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
if (connection != nullptr) {
//分发
prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
} else {
if (DEBUG_FOCUS) {
ALOGD("Dropping event delivery to target with channel '%s' because it "
"is no longer registered with the input dispatcher.",
inputTarget.inputChannel->getName().c_str());
}
}
}
}
- 遍历InputTargets
- 根据inputTarget内部的inputChannel来获取Connection的索引,再根据这个索引作为Key值来获取mConnectionsByFd容器中的Connection。Connection可以理解为InputDispatcher和目标窗口的连接,其内部包含了连接的状态、InputChannel。 InputDispatcher与java层通信就是通过InputChannel。
- 调用prepareDispatchCycleLocked函数根据当前的inputTarget,开始事件发送循环。最终会通过inputTarget中的inputChannel来和窗口进行进程间通信,最终将Motion事件发送给目标窗口。
void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection,
EventEntry* eventEntry,
const InputTarget& inputTarget,
int32_t dispatchMode) {
...
// Use the eventEntry from dispatchEntry since the entry may have changed and can now be a
// different EventEntry than what was passed in.
EventEntry* newEntry = dispatchEntry->eventEntry;
// Apply target flags and update the connection's input state.
switch (newEntry->type) {
case EventEntry::Type::MOTION: {
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*newEntry);
// Assign a default value to dispatchEntry that will never be generated by InputReader,
// and assign a InputDispatcher value if it doesn't change in the if-else chain below.
constexpr int32_t DEFAULT_RESOLVED_EVENT_ID =
static_cast<int32_t>(IdGenerator::Source::OTHER);
dispatchEntry->resolvedEventId = DEFAULT_RESOLVED_EVENT_ID;
if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
} else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT;
} else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
} else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL;
} else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN;
} else {
dispatchEntry->resolvedAction = motionEntry.action;
dispatchEntry->resolvedEventId = motionEntry.id;
}
if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE &&
!connection->inputState.isHovering(motionEntry.deviceId, motionEntry.source,
motionEntry.displayId)) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter "
"event",
connection->getInputChannelName().c_str());
#endif
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
}
dispatchEntry->resolvedFlags = motionEntry.flags;
if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
}
if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED) {
dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
}
...
// Enqueue the dispatch entry.
connection->outboundQueue.push_back(dispatchEntry.release());
traceOutboundQueueLength(connection);
}
prepareDispatchCycleLocked没有太多内容,调用enqueueDispatchEntryLocked继续事件分发,将InputDispatcher中mInboundQueue中的事件取出后, 找到目标window后,封装dispatchEntry加入到connection的outbound队列。
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection) {
while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {
// 从outboundQueue 队列中取出 DispatchEntry
DispatchEntry* dispatchEntry = connection->outboundQueue.front();
dispatchEntry->deliveryTime = currentTime;
const nsecs_t timeout =
getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken());
dispatchEntry->timeoutTime = currentTime + timeout;
// Publish the event.
status_t status;
EventEntry* eventEntry = dispatchEntry->eventEntry;
switch (eventEntry->type) {
case EventEntry::Type::MOTION: {
MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
PointerCoords scaledCoords[MAX_POINTERS];
const PointerCoords* usingCoords = motionEntry->pointerCoords;
// Set the X and Y offset and X and Y scale depending on the input source.
float xOffset = 0.0f, yOffset = 0.0f;
float xScale = 1.0f, yScale = 1.0f;
if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) &&
!(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
float globalScaleFactor = dispatchEntry->globalScaleFactor;
xScale = dispatchEntry->windowXScale;
yScale = dispatchEntry->windowYScale;
xOffset = dispatchEntry->xOffset * xScale;
yOffset = dispatchEntry->yOffset * yScale;
std::array<uint8_t, 32> hmac = getSignature(*motionEntry, *dispatchEntry);
//ALOGE("houyangInputDispatcher---inputPublisher---moreScreenTouched:%d",motionEntry->moreScreenTouched);
// Publish the motion event.
//发布事件
status = connection->inputPublisher
.publishMotionEvent(dispatchEntry->seq,
dispatchEntry->resolvedEventId,
motionEntry->deviceId, motionEntry->source,
motionEntry->displayId, std::move(hmac),
dispatchEntry->resolvedAction,
motionEntry->actionButton,
dispatchEntry->resolvedFlags,
motionEntry->edgeFlags, motionEntry->metaState,
motionEntry->buttonState,
motionEntry->classification, xScale, yScale,
xOffset, yOffset, motionEntry->xPrecision,
motionEntry->yPrecision,
motionEntry->xCursorPosition,
motionEntry->yCursorPosition,
motionEntry->downTime, motionEntry->eventTime,
motionEntry->pointerCount,
motionEntry->pointerProperties, usingCoords,
motionEntry->moreScreenTouched);
reportTouchEventForStatistics(*motionEntry);
break;
}
}
// 将事件从outboundQueue中移除
// Re-enqueue the event on the wait queue.
connection->outboundQueue.erase(std::remove(connection->outboundQueue.begin(),
connection->outboundQueue.end(),
dispatchEntry));
traceOutboundQueueLength(connection);
// 在waitQueue 尾部重新插入
connection->waitQueue.push_back(dispatchEntry);
}
}
- 遍历在enqueueDispatchEntryLocked方法中设置的outboundQueue。
- 清除outboundQueue中已经处理的数据。
- 将数据加入到waitQueue.
该方法主要通过connection 发布最终的事件。
至此,InputDispatcher完成事件的发布,并且将发布的事件保存在connection的waitQueue中。
status_t InputPublisher::publishMotionEvent(
uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, int32_t displayId ..) {
....
InputMessage msg;
// 设置event的参数
msg.header.type = InputMessage::Type::MOTION;
msg.body.motion.action = action;
....
// 调用InputChannel的sendMessage方法
return mChannel->sendMessage(&msg);
}
status_t InputChannel::sendMessage(const InputMessage* msg) {
....
ssize_t nWrite;
do {
nWrite = ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
} while (nWrite == -1 && errno == EINTR);
....
return OK;
}
>frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel); // 将mInputChannel 注册到WMS中
} catch (RemoteException e) {
...
}
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
// 创建app端监听,即WindowInputEventReceiver 作为事件的接收端
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
}
5.4.InputDispatcher简要概括总结
总结:
InputReader把input event放入mInboundQueue(NotifiyMotionArgs转换为MotionEntry,添加到队尾)。
InputDispatcherThread被唤醒后,通过InputDispatcher主要任务就是找到对应的window,并建立进程间通信,把input event传递过去。其实简单来说,InputDispatcher就是负责消费mInboundQueue中的事件,并将数据转换处理后通过publishMotionEvent方法将事件发送到java层。
整个InputDispatcher中涉及到3中queue.
- InputDispatcher的mInboundQueue:存储的是从InputReader 送来的输入事件。
- Connection的outboundQueue:该队列是存储即将要发送给应用的输入事件。
- Connection的waitQueue:队列存储的是已经发给应用的事件,但是应用还未处理完成的。
- 通过dispatchOnceInnerLocked(),取出mInboundQueue 里面的 EventEntry事件
- 通过enqueueDispatchEntryLocked(),生成事件DispatchEntry并加入connection的outbound队列。
- 通过startDispatchCycleLocked(),从outboundQueue中取出事件DispatchEntry, 重新放入connection的waitQueue队列。
- 在startDispatchCycleLocked()里面,通过inputPublisher.publishMotionEvent() 方法将按键事件分发给java层。 publishMotionEvent的实现是在InputTransport.cpp 中
此WMS中会在setView会绑定注册InputEventReceiver ,用于接收InputDispatcher sendmsg发送的数据。具体bind过程涉及WMS,文档不过多介绍。·bind过程中会调用inputchannel去setFd,而sendMessage 中通过getFdfa方法来获取是发送到那一个window上。至此,所有native层流程结束。
6.Java层部分流程
6.1 Java层时序图
6.2 Java层input流程图
当我们触摸屏幕后,会有各种事件产生,我们可以做相应的处理,那么我们事件是怎么传递到我们的对应的控件的呢?
1 . 触摸屏幕后,我们的事件会传递到当前的 Activity 的 dispatchTouchEvent 方法中
2. Activity的dispatchTouchEvent把事件分发到 ViewGrop 的 dispatchTouchEvent 方法中
3. ViewGrop 的 dispatchTouchEvent方法开始分发给子view
4. 如果ViewGrop处理没有消费事件,事件将在最后传递回Activity的onTouchEvent中
Activity、ViewGroup、View的关系:
6.3 Activity、ViewGroup、View 流程分析
一个点击事件产生后,传递顺序是:Activity(Window) -> ViewGroup -> View
Activity的dispatchTouchEvent() —-> ViewGrop的dispatchTouchEvent()—->View的dispatchTouchEvent
—->View的onTouchEvent()—->ViewGrop的onTouchEvent()—->Activity的onTouchEvent()
//Activity
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
//是一个空方法
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}
return false;
}
final void attach(...) {
...
mWindow = new PhoneWindow(this);
. ...
}
onUserInteraction是一个空方法,可以重写这个方法,如果有特殊的业务需求,可以在事件分发给window之前做一些逻辑处理。
getWindow()方法获取的是一个PhoneWindow对象。 在attach中赋值
- 当触摸屏幕以后,事件会传递的 Activity 的 dispatchTouEvent方法中
- 判断当前的动作是不是 down,如果是down,那么我们开始调用 onUserInteraction()
- 调用 Window的superDispatchTouchEvent(),如果 Window 消费了事件,那么就直接返回true,代表事件已经消费,如果 Window 没有消费事件,那么返会false,如果ViewGrop处理没有消费事件,事件将传递到Activity的onTouchEvent中
- 上面一步如果放回了 false,那么就会调用 Activity的 onTouchEvent方法,如果onTouchEvent返会true表示事件被消费,返会false表示事件没被消费,那么将丢弃事件
> frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
> frameworks/base/core/java/com/android/internal/policy/DecorView.java
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
superDispatchTouchEvent方法只是调用DecorView的superDispatchTouchEvent,superDispatchTouchEvent 方法直接调用ViewGroup 的 dispatchTouchEvent
我们发现,最终这个方法调用的是 ViewGrop 的dispatchTouchEvent方法,接着就是用 ViewGrop的dispatchTouEvent方法分发事件了。
Activity的dispatchTouchEvent() —-> ViewGrop的dispatchTouchEvent()
至此为止,我们看了 Activity的dispatchTouchEvent() —-> ViewGrop的dispatchTouchEvent() 的过程。
如果ViewGrop没有消费事件,那么我们的事件回传到Activity的onTouchEvent中
>ViewGroup.java 第一段
public boolean dispatchTouchEvent(MotionEvent ev) {
//这个值,用于作为返回,目的是为了标志事件有没有被拦截
boolean handled = false;
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
//清除firsttarget,丢弃之前全部状态,相当于一个新的开始
cancelAndClearTouchTargets(ev);
resetTouchState();
}
// Check for interception.
//开始检查是否需要 拦截
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
//获取当前的事件是否 不允许拦截 (true不允许)
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
//事件被拦截了,那么把事件分发到拦截的方法中去
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
...
return handled;
}
ViewGroup的dispatchTouchEvent中做了以下几个操作
- 首先会判断事件是否是为 ACTION_DOWN 事件,如果是,则进行初始化。这里为什么要进行初始化呢?这是因为一个完整的事件序列是以 DOWN 开始,以 UP 结束的。所以,如果是 DOWN 事件,说明这是一个新的事件序列,故而需要初始化之前的状态。在注释 1 处,如果条件满足,则执行下面的代码,mFirstTouchTarget 的意义是,当前 ViewGroup 是否拦截了此事件,如果拦截了,mFirstTouchTarget == null,如果没有拦截则交给子 View 来处理,mFirstTouchTarget != null。假设当前的 ViewGroup 拦截了此事件,mFirstTouchTarget == null 为 false,如果这时触发 ACTION_DOWN 事件,则会执行 onInterceptTouchEvent(ev) 方法;如果触发的是 ACTION_MOVE、ACTION_UP 事件,则不再执行 onInterceptTouchEvent(ev) ,而是直接设置 intercepted = true ,此后的一个时间序列均由这个 ViewGroup 处理。
- FLAG_DISALLOW_INTERCEPT 标志位,它主要是禁止 ViewGroup 拦截除了 DOWN 之外的事件,一般通过子 View.requestDisallowInterceptTouchEvent(…) 来设置。
所以,总结一下就是,当 ViewGroup 要拦截事件的时候,那么后续的事件序列都交给它处理,而不用再调用 onInterceptTouchEvent(…) 方法了。所以 onInterceptTouchEvent(…) 方法并不是每次事件都会调用的。
>ViewGroup.java 第二段
>
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x =
isMouseEvent ? ev.getXCursorPosition() : ev.getX(actionIndex);
//相对于父布局的x,y
final float y =
isMouseEvent ? ev.getYCursorPosition() : ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
遍历所有的子view,寻找到最合适的view,把事件分发下去
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
//判断当前的循环到的子view是否能够接受事件,和当前的点是否所在此子view上
//只要此子view不能接受事件或者点没有落在它身上,查找下一个如果不允许则跳过本地循环,继续下一个view的判断
if (!child.canReceivePointerEvents()
|| !isTransformedTouchPointInView(x, y, child, null)) {
continue;
}
//将事件分发到子view上,如果子view拦截了事件事件,放回true,
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list, find original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
// The accessibility focus didn't handle the event, so clear
// the flag and do a normal dispatch to all children.
ev.setTargetAccessibilityFocus(false);
}
return handled;
}
上面代码做了以下几个操作
- for 循环,首先遍历 ViewGroup 的子元素,判断子元素是否能够接收到点击事件,如果子元素能够接收到点击事件,则交由子元素来处理。需要注意的是这个 for 循环是倒序便利的,即从最上层的子 View 开始往内层遍历。
- 判断触摸点的卫士是否在子 View 的范围或者子 View 是否在播放动画,如果均不符合则执行 continue 语句,表示这个子 View 不符合条件,开始便利下一个子 View。
- 调用 ViewGroup.dispatchTransformedTouchEvent(…)
// /frameworks/base/core/java/android/view/ViewGroup.java
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
...
}
如果有子 View,则调用子 View.dispatchTouchEvent(…) 方法,如果没有子 View,则调用 super.dispatchTouchEvent(…) 方法。
// frameworks/base/core/java/android/view/View.java
public boolean dispatchTouchEvent(MotionEvent event) {
...
boolean result = false;
...
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//noinspection SimplifiableIfStatement
//touchlistener回调监听
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) { // 1
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
...
return result;
}
这个方法很好理解,若有ontouch监听,则优先执行监听,若没有监听则继续执行view.ontouchevent
1.判断是否存在事件拦截,如果存在事件拦截,则分发到拦截的事件中,继而中断事件。
2.找到ViewGroup中所有的子View,把事件分发到view中。
3.遍历view后会存在几种判断,判断当前的循环到的子view是否能够接受事件,和当前的点是否所在此子view上,只要此子view不能接受事件或者点没有落在它身上,查找下一个
4. 若没有找到合适的view,则会继续分发调用父类View的dispatchtouchevent.
// /frameworks/base/core/java/android/view/View.java
public boolean onTouchEvent(MotionEvent event) {
. ...
final int action = event.getAction();
//这边判断view是否设置了clickable属性。
final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE; // 1
...
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
...
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
// take focus if we don't have it already and we should in
// touch mode.
boolean focusTaken = false;
...
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClickInternal();
}
}
}
...
}
mIgnoreNextUpEvent = false;
break;
...
}
return true;
}
return false;
}
只要 View 的 CLICKABLE 和 LONG_CLICKABLE 有一个为 true,那么 View.onTouch(…) 就会返回 true 消耗这个事件。CLICKABLE 和 LONG_CLICKABLE 代表 View 可以被点击和长按点击,可以通过 View.setClickable(…) 和 View.setLongClickable(…) 方法来设置,也可以通过 View.setOnClickListener(…) 和 View.setOnLongClickListener(…) 方法来设置,它们会自动将 View 设置为 CLICKABLDE 和 LONG_CLICKABLE。接着在 ACTION_UP 事件中会调用 View.performClickInternal() 方法
performClickInternal 方法就是执行应用的onclick事件。
6.4. 简要概括总结
总结:
- 触摸屏幕后,我们的事件会传递到当前的 Activity 的 dispatchTouchEvent 方法中
- Activity的dispatchTouchEvent把事件分发到 ViewGrop 的 dispatchTouchEvent 方法中
- ViewGrop 的 dispatchTouchEvent方法开始分发给子view
- 如果ViewGrop处理没有消费事件,事件将在最后传递回Activity的onTouchEvent中
- 如果事件不被中断,整个事件流向就是一个最初的那个环型图。 如果没有对控件里面的方法进行重写或更改返回值,而直接用super调用父类的默认实现,那么整个事件流向应该是从Activity -> ViewGroup -> View从上往下调用dispatchTouchEvent方法,一直到叶子节点(View)的时候,再由View -> ViewGroup -> Activity从下往上调用onTouchEvent方法;
- dispatchTouchEvent和onTouchEvent一旦return true,事件就停止传递了,没有谁能再收到这个事件;return false的时候,事件都回传给父控件的onTouchEvent和dispatchTouch处理;
6.5. java层从natice层接收数据部分代码逻辑
下方为java层接收native层数据的部分代码
> frameworks/base/core/java/android/view/InputEventReceiver.java
private void dispatchInputEvent(int seq, InputEvent event) {
mSeqMap.put(event.getSequenceNumber(), seq);
Slog.e("hyy","InputEventReceiver---dispatchInputEvent---event:"+event);
onInputEvent(event);
}
> frameworks/base/core/java/android/view/ViewRootImpl.java
final class WindowInputEventReceiver extends InputEventReceiver {
@Override
public void onInputEvent(InputEvent event, int displayId) {
enqueueInputEvent(event, this, 0, true);
}
...
}
> frameworks/base/core/java/android/view/ViewRootImpl.java
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
//将event放入QueuedInputEvent
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
// Always enqueue the input event in order, regardless of its time stamp.
// We do this because the application or the IME may inject key events
// in response to touch events and we want to ensure that the injected keys
// are processed in the order they were received and we cannot trust that
// the time stamp of injected events are monotonic.
QueuedInputEvent last = mPendingInputEventTail;
//成员变量 mPendingInputEventHead 赋值为QueuedInputEvent 在doProcessInputEvents方法中使用
if (last == null) {
mPendingInputEventHead = q;
mPendingInputEventTail = q;
} else {
last.mNext = q;
mPendingInputEventTail = q;
}
if (processImmediately) {
doProcessInputEvents(); // 分发输入事件
} else {
scheduleProcessInputEvents();
}
}
void doProcessInputEvents() {
// Deliver all pending input events in the queue.
while (mPendingInputEventHead != null) {
QueuedInputEvent q = mPendingInputEventHead;
mPendingInputEventHead = q.mNext;
if (mPendingInputEventHead == null) {
mPendingInputEventTail = null;
}
q.mNext = null;
mPendingInputEventCount -= 1;
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
long eventTime = q.mEvent.getEventTimeNano();
long oldestEventTime = eventTime;
if (q.mEvent instanceof MotionEvent) {
MotionEvent me = (MotionEvent)q.mEvent;
if (me.getHistorySize() > 0) {
oldestEventTime = me.getHistoricalEventTimeNano(0);
}
}
mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);
//继续分发
deliverInputEvent(q);
}
// We are done processing all input events that we can process right now
// so we can clear the pending flag immediately.
if (mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = false;
mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
}
}
>View.java
public final boolean dispatchPointerEvent(MotionEvent event) {
//如果是触摸事件
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
说明:
- Activity的dispatchTouchEvent只有return super.dispatchTouchEvent(ev)才往下走,返回true或者false事件就被消费了(终止传递);
7. 常见问题及处理方案
1.点击驱动无上报
通过获取设备节点数据无反馈
adb shell getevent -l 数据节点
2.驱动正常上报,界面点击无响应。log中反馈InputDispatcher drop
3.java无事件上报
adb shell dumpsys input
1.Event Hub 状态
2.Input Reader 状态
3.Input Dispatcher 状态
可以通过Event Hub查看当前设备点击屏幕所使用的设备节点。
![请添加图片描述](https://img-blog.csdnimg.cn/direct/1154a6f945814398a173a5648e2b2b94.jpeg
InputReader中可以查看点击的x,y轴坐标
InputDispatcher中可以查看window信息,历史事件信息,当前队列信息等