了解更多印蔗,移步Android觸摸事件傳遞機(jī)制系列詳解
在Android觸摸事件的傳遞(二)--輸入系統(tǒng)InputManagerService崖蜜,介紹IMS服務(wù)的啟動(dòng)過程會(huì)創(chuàng)建兩個(gè)native
線程段只,分別是InputReader
,InputDispatcher
. 接下來從InputReader
線程的執(zhí)行過程從threadLoop
為起點(diǎn)開始分析
1 threadLoop
[-> InputReader.cpp]
bool InputReaderThread::threadLoop() {
mReader->loopOnce(); //【見小節(jié)1.2】
return true;
}
-
threadLoop
返回值true
代表的是會(huì)不斷地循環(huán)調(diào)用loopOnce()
识埋。另外,如果當(dāng)返回值為false
則會(huì) 退出循環(huán)。 - 整個(gè)過程是不斷循環(huán)的地調(diào)用
InputReader
的loopOnce()
方法年碘。
2 loopOnce();
- 查看InputReader配置是否修改,如界面大小展鸡、方向屿衅、鍵盤布局重新加載、指針?biāo)俣雀淖兊?/li>
- 從EventHub讀取事件娱颊,其中EVENT_BUFFER_SIZE = 256
- 處理事件
- 將事件傳到InputDispatcher
注:通過EventHub->getEvents()傲诵,獲取輸入事件和設(shè)備增刪事件,count為讀取的事件數(shù)量箱硕,mEventBuffer存儲(chǔ)著事件拴竹。由上一篇文章可知getEvents()是阻塞的,只有當(dāng)有事件或者被wake才會(huì)被喚醒向下執(zhí)行剧罩。
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
bool inputDevicesChanged = false;
Vector<InputDeviceInfo> inputDevices;
{ // acquire lock
AutoMutex _l(mLock);
oldGeneration = mGeneration;
timeoutMillis = -1;
//查看InputReader配置是否修改栓拜,如界面大小、方向惠昔、鍵盤布局重新加載幕与、指針?biāo)俣雀淖兊? uint32_t changes = mConfigurationChangesToRefresh;
if (changes) {
mConfigurationChangesToRefresh = 0;
timeoutMillis = 0;
refreshConfigurationLocked(changes); //刷新配置
} else if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
}
} // release lock
////從EventHub讀取事件,其中EVENT_BUFFER_SIZE = 256【見小節(jié)2.1】
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{ // acquire lock
AutoMutex _l(mLock);
mReaderIsAliveCondition.broadcast();
if (count) {//處理事件【見小節(jié)3.1】
processEventsLocked(mEventBuffer, count);
}
if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
if (now >= mNextTimeout) {
mNextTimeout = LLONG_MAX;
timeoutExpiredLocked(now);
}
}
if (oldGeneration != mGeneration) {
inputDevicesChanged = true;
getInputDevicesLocked(inputDevices);
}
} // release lock
// 發(fā)送一個(gè)消息镇防,該消息描述已更改的輸入設(shè)備啦鸣。
if (inputDevicesChanged) {
mPolicy->notifyInputDevicesChanged(inputDevices);
}
mQueuedListener->flush(); //將事件傳到InputDispatcher.
}
2.1 EventHub->getEvents(),獲取輸入事件和設(shè)備增刪事件
通過EventHub->getEvents()来氧,獲取輸入事件和設(shè)備增刪事件诫给,count為讀取的事件數(shù)量,mEventBuffer存儲(chǔ)著事件啦扬。由上一篇文章可知getEvents()是阻塞的中狂,只有當(dāng)有事件或者被wake才會(huì)被喚醒向下執(zhí)行。
Android觸摸事件的傳遞(三)--輸入系統(tǒng)EventHub
2.2 processEventsLocked 處理事件
- 接著判斷count的值扑毡,如果有讀到事件(count大于0)胃榕,則調(diào)用
processEventsLocked(mEventBuffer, count)
處理事件。 - processEventsLocked()函數(shù)中會(huì)遍歷所有的事件瞄摊,分別進(jìn)行處理勋又。其處理的事件類型分為四種:原始輸入事件、設(shè)備加載事件换帜、設(shè)備卸載事件及FINISHED_DEVICE_SCAN事件赐写。
- 這里我們只關(guān)心對于設(shè)備自身產(chǎn)生的事件。也就是觸摸屏相關(guān)的事件膜赃。也就是processEventsForDeviceLocked函數(shù)中所進(jìn)行的操作。
[-> 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) {
int32_t deviceId = rawEvent->deviceId;
while (batchSize < count) {
if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
|| rawEvent[batchSize].deviceId != deviceId) {
break;
}
batchSize += 1; //同一設(shè)備的事件打包處理
}
//數(shù)據(jù)事件的處理【見小節(jié)3.3】
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
} else {
switch (rawEvent->type) {
case EventHubInterface::DEVICE_ADDED:
//設(shè)備添加
addDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::DEVICE_REMOVED:
//設(shè)備移除
removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::FINISHED_DEVICE_SCAN:
//設(shè)備掃描完成
handleConfigurationChangedLocked(rawEvent->when);
break;
default:
ALOG_ASSERT(false);//不會(huì)發(fā)生
break;
}
}
count -= batchSize; //count減少已處理事件個(gè)數(shù)揉忘,表示剩余事件個(gè)數(shù)
rawEvent += batchSize; //rawEvent指針向后移動(dòng)batchSize個(gè)RawEvent對象跳座,也就是指到該處理的事件上端铛。
}
}
2.2.1事件派發(fā)到Device
根據(jù)事件獲得相應(yīng)的設(shè)備類型,然后將事件交給相應(yīng)的設(shè)備處理疲眷。判斷是否忽略該事件禾蚕,如果不是忽略該事件,則會(huì)調(diào)用相應(yīng)設(shè)備的process方法進(jìn)行處理狂丝。
void InputReader::processEventsForDeviceLocked(int32_t deviceId,
const RawEvent* rawEvents, size_t count) {
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
if (deviceIndex < 0) {
return;
}
InputDevice* device = mDevices.valueAt(deviceIndex);
if (device->isIgnored()) {
return;//可忽略則直接返回
}
device->process(rawEvents, count);
}
2.2.3 事件派發(fā)到InputMapper
這里的事件又交給了InputMapper
來處理
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
....
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
//調(diào)用具體mapper來處理
mapper->process(rawEvent);
}
....
}
-
InputMapper
對應(yīng)了很多的子類换淆,這里根據(jù)事件的類型進(jìn)行相應(yīng)的派發(fā),處理几颜。 - 事件到了這里之后倍试,如何傳遞到應(yīng)用層,這里mapper->process進(jìn)行了那些處理蛋哭。
- 這里來看一下對于觸摸屏事件的處理函數(shù)县习。
void TouchInputMapper::process(const RawEvent* rawEvent) {
mCursorButtonAccumulator.process(rawEvent);
mCursorScrollAccumulator.process(rawEvent);
mTouchButtonAccumulator.process(rawEvent);
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
sync(rawEvent->when);
}
}
追蹤數(shù)據(jù)流向
通過process函數(shù)處理,我們繼續(xù)追蹤函數(shù)的數(shù)據(jù)流向谆趾。對于相關(guān)的事件處理躁愿,這里最終執(zhí)行的是將
void TouchInputMapper::sync(nsecs_t when) {
.....
processRawTouches(false /*timeout*/);
}
void TouchInputMapper::processRawTouches(bool timeout) {
if (mDeviceMode == DEVICE_MODE_DISABLED) {
// Drop all input if the device is disabled.
mCurrentRawState.clear();
mRawStatesPending.clear();
return;
}
// Drain any pending touch states. The invariant here is that the mCurrentRawState is always
// valid and must go through the full cook and dispatch cycle. This ensures that anything
// touching the current state will only observe the events that have been dispatched to the
// rest of the pipeline.
const size_t N = mRawStatesPending.size();
size_t count;
for(count = 0; count < N; count++) {
const RawState& next = mRawStatesPending[count];
// A failure to assign the stylus id means that we're waiting on stylus data
// and so should defer the rest of the pipeline.
if (assignExternalStylusId(next, timeout)) {
break;
}
// All ready to go.
clearStylusDataPendingFlags();
mCurrentRawState.copyFrom(next);
if (mCurrentRawState.when < mLastRawState.when) {
mCurrentRawState.when = mLastRawState.when;
}
cookAndDispatch(mCurrentRawState.when);
}
if (count != 0) {
mRawStatesPending.removeItemsAt(0, count);
}
if (mExternalStylusDataPending) {
if (timeout) {
nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY;
clearStylusDataPendingFlags();
mCurrentRawState.copyFrom(mLastRawState);
cookAndDispatch(when);
} else if (mExternalStylusFusionTimeout == LLONG_MAX) {
mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT;
getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
}
}
}
void TouchInputMapper::cookAndDispatch(nsecs_t when) {
// Always start with a clean state.
mCurrentCookedState.clear();
// Apply stylus buttons to current raw state.
applyExternalStylusButtonState(when);
// Handle policy on initial down or hover events.
bool initialDown = mLastRawState.rawPointerData.pointerCount == 0
&& mCurrentRawState.rawPointerData.pointerCount != 0;
uint32_t policyFlags = 0;
bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState;
if (initialDown || buttonsPressed) {
// If this is a touch screen, hide the pointer on an initial down.
if (mDeviceMode == DEVICE_MODE_DIRECT) {
getContext()->fadePointer();
}
if (mParameters.wake) {
policyFlags |= POLICY_FLAG_WAKE;
}
}
// Consume raw off-screen touches before cooking pointer data.
// If touches are consumed, subsequent code will not receive any pointer data.
if (consumeRawTouches(when, policyFlags)) {
mCurrentRawState.rawPointerData.clear();
}
// Cook pointer data. This call populates the mCurrentCookedState.cookedPointerData structure
// with cooked pointer data that has the same ids and indices as the raw data.
// The following code can use either the raw or cooked data, as needed.
cookPointerData();
// Apply stylus pressure to current cooked state.
applyExternalStylusTouchState(when);
// Synthesize key down from raw buttons if needed.
synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
policyFlags, mLastCookedState.buttonState, mCurrentCookedState.buttonState);
// Dispatch the touches either directly or by translation through a pointer on screen.
if (mDeviceMode == DEVICE_MODE_POINTER) {
for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits);
!idBits.isEmpty(); ) {
uint32_t id = idBits.clearFirstMarkedBit();
const RawPointerData::Pointer& pointer =
mCurrentRawState.rawPointerData.pointerForId(id);
if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS
|| pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
mCurrentCookedState.stylusIdBits.markBit(id);
} else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER
|| pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
mCurrentCookedState.fingerIdBits.markBit(id);
} else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) {
mCurrentCookedState.mouseIdBits.markBit(id);
}
}
for (BitSet32 idBits(mCurrentRawState.rawPointerData.hoveringIdBits);
!idBits.isEmpty(); ) {
uint32_t id = idBits.clearFirstMarkedBit();
const RawPointerData::Pointer& pointer =
mCurrentRawState.rawPointerData.pointerForId(id);
if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS
|| pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
mCurrentCookedState.stylusIdBits.markBit(id);
}
}
// Stylus takes precedence over all tools, then mouse, then finger.
PointerUsage pointerUsage = mPointerUsage;
if (!mCurrentCookedState.stylusIdBits.isEmpty()) {
mCurrentCookedState.mouseIdBits.clear();
mCurrentCookedState.fingerIdBits.clear();
pointerUsage = POINTER_USAGE_STYLUS;
} else if (!mCurrentCookedState.mouseIdBits.isEmpty()) {
mCurrentCookedState.fingerIdBits.clear();
pointerUsage = POINTER_USAGE_MOUSE;
} else if (!mCurrentCookedState.fingerIdBits.isEmpty() ||
isPointerDown(mCurrentRawState.buttonState)) {
pointerUsage = POINTER_USAGE_GESTURES;
}
dispatchPointerUsage(when, policyFlags, pointerUsage);
} else {
if (mDeviceMode == DEVICE_MODE_DIRECT
&& mConfig.showTouches && mPointerController != NULL) {
mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
mPointerController->setButtonState(mCurrentRawState.buttonState);
mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
mCurrentCookedState.cookedPointerData.idToIndex,
mCurrentCookedState.cookedPointerData.touchingIdBits);
}
if (!mCurrentMotionAborted) {
dispatchButtonRelease(when, policyFlags);
dispatchHoverExit(when, policyFlags);
dispatchTouches(when, policyFlags);
dispatchHoverEnterAndMove(when, policyFlags);
dispatchButtonPress(when, policyFlags);
}
if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
mCurrentMotionAborted = false;
}
}
// Synthesize key up from raw buttons if needed.
synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
policyFlags, mLastCookedState.buttonState, mCurrentCookedState.buttonState);
// Clear some transient state.
mCurrentRawState.rawVScroll = 0;
mCurrentRawState.rawHScroll = 0;
// Copy current touch to last touch in preparation for the next cycle.
mLastRawState.copyFrom(mCurrentRawState);
mLastCookedState.copyFrom(mCurrentCookedState);
}
void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
....
dispatchMotion();
....
}
- 在相關(guān)的函數(shù)調(diào)用之后,最終調(diào)用了
dispatchTouches
- 對于
dispatchTouches
中沪蓬,會(huì)根據(jù)記錄的上一次的觸摸位置彤钟,對事件的類型進(jìn)行判斷,然后做相應(yīng)的分發(fā)跷叉,事件類型有抬起逸雹,下落,移動(dòng)等性芬,然后對相應(yīng)的事件進(jìn)行分發(fā)峡眶。無論是對于何種類型的事件派發(fā),最終被調(diào)用到的都是dispatchMotion()
方法植锉。
void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
....
dispatchMotion();
....
}
void TouchInputMapper::dispatchMotion() {
....
NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,
action, actionButton, flags, metaState, buttonState, edgeFlags,
mViewport.displayId, pointerCount, pointerProperties, pointerCoords,
xPrecision, yPrecision, downTime);
getListener()->notifyMotion(&args);
}
getListener函數(shù)
InputListenerInterface* InputReader::ContextImpl::getListener() {
return mReader->mQueuedListener.get();
}
notifyMotion函數(shù)實(shí)現(xiàn)
void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
mArgsQueue.push(new NotifyMotionArgs(*args));
}
我們將觸摸相關(guān)的事件進(jìn)行包裝之后辫樱,將其加入到一個(gè)ArgsQueue隊(duì)列,到此俊庇,我們已經(jīng)將數(shù)據(jù)加入到參數(shù)隊(duì)列中
InputReader的loopOnce過程, 可知當(dāng)執(zhí)行完processEventsLocked()過程, 然后便開始執(zhí)行mQueuedListener->flush()過程
2.3 QueuedInputListener的flush方法
void QueuedInputListener::flush() {
size_t count = mArgsQueue.size();
for (size_t i = 0; i < count; i++) {
NotifyArgs* args = mArgsQueue[i];
args->notify(mInnerListener);
delete args;
}
mArgsQueue.clear();
}
遍歷整個(gè)mArgsQueue數(shù)組, 在input架構(gòu)中NotifyArgs的實(shí)現(xiàn)子類主要有以下幾類:
- NotifyConfigurationChangedArgs
- NotifyKeyArgs
- NotifyMotionArgs
- NotifySwitchArgs
- NotifyDeviceResetArgs
NotifyArgs的notify函數(shù)實(shí)現(xiàn)
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifyMotion(this);
}
對于這個(gè)listener的創(chuàng)建來自于InputReader構(gòu)建的時(shí)候狮暑。
mQueuedListener = new QueuedInputListener(listener);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
InputDispatcher
而這里的Listener
則是InputDispatcher
,InputDispatcher
的notifyMotion
實(shí)現(xiàn)源碼辉饱。
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
.....
MotionEvent event;
event.initialize(args->deviceId, args->source, args->action, args->actionButton,
args->flags, args->edgeFlags, args->metaState, args->buttonState,
0, 0, args->xPrecision, args->yPrecision,
args->downTime, args->eventTime,
args->pointerCount, args->pointerProperties, args->pointerCoords);
....
//構(gòu)造MotionEntry搬男,然后將其加入到enqueueInboundEventLocked之中
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) {
mLooper->wake();//喚醒其中的looper
}
}
在該函數(shù)中,所做的事情是對于所傳遞的參數(shù)彭沼,構(gòu)造MotionEntry
缔逛,然后將其加入到enqueueInboundEventLocked
之中。然后喚醒其中的looper
。
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
bool needWake = mInboundQueue.isEmpty();
mInboundQueue.enqueueAtTail(entry);
...
//進(jìn)行一些事件和窗口相關(guān)的判斷處理
}
總結(jié)
補(bǔ)充--設(shè)備增加
1. addDeviceLocked
void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
if (deviceIndex >= 0) {
return; //已添加的相同設(shè)備則不再添加
}
InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
uint32_t classes = mEventHub->getDeviceClasses(deviceId);
int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId);
InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
device->configure(when, &mConfig, 0);
device->reset(when);
mDevices.add(deviceId, device); //添加設(shè)備到mDevices
...
}
2 createDeviceLocked
InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
const InputDeviceIdentifier& identifier, uint32_t classes) {
//創(chuàng)建InputDevice對象
InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
controllerNumber, identifier, classes);
...
//獲取鍵盤源類型
uint32_t keyboardSource = 0;
int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
keyboardSource |= AINPUT_SOURCE_KEYBOARD;
}
if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
}
if (classes & INPUT_DEVICE_CLASS_DPAD) {
keyboardSource |= AINPUT_SOURCE_DPAD;
}
if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
keyboardSource |= AINPUT_SOURCE_GAMEPAD;
}
//添加鍵盤類設(shè)備InputMapper
if (keyboardSource != 0) {
device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
}
//添加鼠標(biāo)類設(shè)備InputMapper
if (classes & INPUT_DEVICE_CLASS_CURSOR) {
device->addMapper(new CursorInputMapper(device));
}
//添加觸摸屏設(shè)備InputMapper
if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
device->addMapper(new MultiTouchInputMapper(device));
} else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
device->addMapper(new SingleTouchInputMapper(device));
}
...
return device;
}
該方法主要功能:
- 創(chuàng)建
InputDevice
對象褐奴,將InputReader
的mContext
賦給InputDevice
對象所對應(yīng)的變量 - 根據(jù)設(shè)備類型來創(chuàng)建并添加相對應(yīng)的
InputMapper
按脚,同時(shí)設(shè)置mContext.
input設(shè)備類型有很多種,以上代碼只列舉部分常見的設(shè)備以及相應(yīng)的InputMapper:
- 鍵盤類設(shè)備:KeyboardInputMapper
- 觸摸屏設(shè)備:MultiTouchInputMapper或SingleTouchInputMapper
- 鼠標(biāo)類設(shè)備:CursorInputMapper
參考
Android系統(tǒng)源碼剖析-事件分發(fā)
Android 輸入系統(tǒng)(三)InputReader