Android系統源碼剖析-事件分發
前言
對于事件的監控和獲取做了分析,在拿到事件之后,后續是如何處理分發的呢?本篇文章主要針對在通過getEvent獲取到事件之后,后續的相關分發處理流程。
InputReaderThread函數不斷地調用looperOnce函數,不斷的從中讀取事件,那么下一個問題來了,讀取到事件要放置到哪里,又在哪里被消耗掉了呢?也就是事件接下來的流向問題。讓我們回到looperOnce之前。
事件分發
processEventsLocked
在調用了getEvent之后,又調用了函數 processEventsLocked
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;
}
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
} else {
switch (rawEvent->type) {
case EventHubInterface::DEVICE_ADDED:
addDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::DEVICE_REMOVED:
removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::FINISHED_DEVICE_SCAN:
handleConfigurationChangedLocked(rawEvent->when);
break;
default:
ALOG_ASSERT(false); // can't happen
break;
}
}
count -= batchSize;
rawEvent += batchSize;
}
}
首先對于事件類型進行了判斷,如果事件不是合成事件,則會對其DeviceID進行判斷,通過對其判斷來確定batchSize等,然后對其進行處理,對于其他的類型事件,則會具體判斷,判斷是設備的添加,設備的移除,完成設備掃描等等,然后對事件分別進行處理,這里我們只關心對于設備自身產生的事件。也就是觸摸屏相關的事件。也就是 processEventsForDeviceLocked 函數中所進行的操作。
事件派發到Device
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);
}</code></pre>
根據事件獲得相應的設備類型,然后將事件交給相應的設備處理。判斷是否忽略該事件,如果不是忽略該事件,則會調用相應設備的process方法進行處理。
事件派發到InputMapper
InputDevice的process方法
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
....
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
mapper->process(rawEvent);
}
....
}
這里的事件又交給了InputMapper來處理

InputMapper對應了很多的子類,這里根據事件的類型進行相應的派發,處理。
事件到了這里之后,如何傳遞到應用層,這里mapper->process進行了那些處理。這里來看一下對于觸摸屏事件的處理函數。
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);
}
}</code></pre>
通過這里的函數處理,我們繼續追蹤函數的數據流向。對于相關的事件處理,這里最終執行的是將
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);
}
}
}</code></pre>
在相關的函數調用之后,最終調用了 dispatchTouches
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);
}</code></pre>
void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
....
dispatchMotion();
....
}
對于dispatchTouches中,會根據記錄的上一次的觸摸位置,對事件的類型進行判斷,然后做相應的分發,事件類型有抬起,下落,移動等,然后對相應的事件進行分發。無論是對于何種類型的事件派發,最終被調用到的都是 dispatchMotion() 方法。
對于相關事件的分發最終調用到了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函數
InputListenerInterface* InputReader::ContextImpl::getListener() {
return mReader->mQueuedListener.get();
}
notifyMotion函數實現
void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
mArgsQueue.push(new NotifyMotionArgs(*args));
}
這里可以看到,我們將觸摸相關的事件進行包裝之后,將其加入到一個ArgsQueue隊列,到此,我們已經將數據加入到參數隊列中,到此事件從設備文件獲取到寫入流程已經完成,這里讓我們再回到loopOnce方法中,最后調用了 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();
}
NotifyArgs的notify函數實現
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifyMotion(this);
}
對于這個listener的創建來自于InputReader構建的時候。
mQueuedListener = new QueuedInputListener(listener);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
而這里的Listener則是 InputDispatcher ,InputDispatcher 的notifyMotion實現源碼。
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);
....
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();
}
}
在該函數中,所做的事情是對于所傳遞的參數,構造MotionEntry,然后將其加入到enqueueInboundEventLocked之中。然后喚醒其中的looper。
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
bool needWake = mInboundQueue.isEmpty();
mInboundQueue.enqueueAtTail(entry);
...
//進行一些事件和窗口相關的判斷處理
}
Dispatcher開啟的線程中,每次循環的操作如何?
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();
return true;
}
Dispatcher下dispatchOnce的實現
void InputDispatcher::dispatchOnce() {
...
dispatchOnceInnerLocked(&nextWakeupTime);
...
}
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
....
mPendingEvent = mInboundQueue.dequeueAtHead();
....
switch (mPendingEvent->type) {
case EventEntry::TYPE_MOTION: {
MotionEntry typedEntry = static_cast<MotionEntry>(mPendingEvent);
if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
dropReason = DROP_REASON_APP_SWITCH;
}
if (dropReason == DROP_REASON_NOT_DROPPED
&& isStaleEventLocked(currentTime, typedEntry)) {
dropReason = DROP_REASON_STALE;
}
if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
dropReason = DROP_REASON_BLOCKED;
}
done = dispatchMotionLocked(currentTime, typedEntry,
&dropReason, nextWakeupTime);
break;
}
....
}
}</code></pre>
從mInboudQueue中,獲取到事件,然后對事件類型進行判斷,判斷之后調用了dispatchMotionLocked函數,來繼續進行事件的傳遞。
bool InputDispatcher::dispatchMotionLocked(
nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
....
Vector<InputTarget> inputTargets;
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);
}
....
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}
- dispatchEventLocked
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) {
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
}
}
}</code></pre>
獲得目標輸入,根據InputChannel獲取相應的連接,然后調用prepareDispatchCycleLocked(),進行事件的派發。
enqueueDispatchEntriesLocked,在該方法中又調用了startDispatchCycleLocked方法。其實現為
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection) {
EventEntry* eventEntry = dispatchEntry->eventEntry;
....
switch (eventEntry->type) {
....
case EventEntry::TYPE_MOTION: {
status = connection->inputPublisher.publishMotionEvent( ....);
break;
}
....
}
...
}
至此調用了connection 的inputPublisher的publishMotionEvent方法將事件分發消耗。
InputPublisher定義在InputTransport.cpp中
status_t InputPublisher::publishMotionEvent(...) {
....
InputMessage msg;
msg.header.type = InputMessage::TYPE_MOTION;
msg.body.motion.seq = seq;
msg.body.motion.deviceId = deviceId;
msg.body.motion.source = source;
msg.body.motion.action = action;
msg.body.motion.actionButton = actionButton;
msg.body.motion.flags = flags;
msg.body.motion.edgeFlags = edgeFlags;
msg.body.motion.metaState = metaState;
msg.body.motion.buttonState = buttonState;
msg.body.motion.xOffset = xOffset;
msg.body.motion.yOffset = yOffset;
msg.body.motion.xPrecision = xPrecision;
msg.body.motion.yPrecision = yPrecision;
msg.body.motion.downTime = downTime;
msg.body.motion.eventTime = eventTime;
msg.body.motion.pointerCount = pointerCount;
for (uint32_t i = 0; i < pointerCount; i++) {
msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
}
return mChannel->sendMessage(&msg);
}
該方法所執行的操作是利用傳入的觸摸信息,構建點擊消息,然后通過InputChannel將消息發送出去。這里引出了InputChannel,在此,我們通InputPublisher的創建反推出InputChannel是何時被引入的,何時被創建的。從而進一步分析其作用。在分析之前先讓我們來對上述的分析過程做一個總結。

ReaderThread開啟后會從EventHub中輪詢獲取時間,獲取到事件之后,對手將進行一系列的處理,最終將經過一系列處理封裝的事件信息通過InputChannel發送出去。

到此,對于輸入事件,我們已經分析到了InputChannel,對于其上的具體分析轉化,將是接下來分析的核心。
InputChannel
從上面分析可以看到事件傳遞部分最后是通過InputChannel所發送出去的,那么InputChannel是在何時被創建的呢?何時被InputManager所使用的呢?同時,InputReaderThread和InputDispatcherThread是運行在SystemServer進程中的,而我們的應用進程是和其不在同一個進程中的。這之間一定也是有進程間的通信機制在里面。具體又是如何實現的呢?對于InputChannel這邊是事件傳遞過程中一個比較核心的點。
InputChannel的創建是在 ViewRootImpl 中 setView 方法中。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
....
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
....
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
....
}
這里對于ViewRootImpl和WindowSession相關暫且不介紹,對于這方面的知識,需要很大的篇幅來介紹,這里先只是講到是在這里創建的,對于其相關的內容將在后續的文章中介紹。這里首先是創建了一個InputChannel,然后將其調用了 WindowSession 的 addToDisplay 方法將其作為參數傳遞。
public InputChannel() {
}
在InputChannel中的方法都為調用了相應的native方法。這里調用的addToDisplay將會把InputChannel添加到WindowManagerService中。會調用WMS的 addWindow 方法。
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
....
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
....
}</code></pre>
對于InputChannel的相關處理調用了WindowState的openInputChannel方法。
void openInputChannel(InputChannel outInputChannel) {
if (mInputChannel != null) {
throw new IllegalStateException("Window already has an input channel.");
}
String name = makeInputChannelName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
mInputChannel = inputChannels[0];
mClientChannel = inputChannels[1];
mInputWindowHandle.inputChannel = inputChannels[0];
if (outInputChannel != null) {
mClientChannel.transferTo(outInputChannel);
mClientChannel.dispose();
mClientChannel = null;
} else {
mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
}
mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
}
首先調用了InputChannel的 openInputChannelPair 方法,該方法調用了InputChannel的native方法 nativeOpenInputChannelPair ,創建了兩個 InputChannel ,對其中一個通過 InputManager 進行了InputChannel的注冊。對于 InputChannel 的相關Native的實現是在InputTransport中, nativeOpenInputChannelPair 的源碼如下。
status_t InputChannel::openInputChannelPair(const String8& name,
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
status_t result = -errno;
outServerChannel.clear();
outClientChannel.clear();
return result;
}
int bufferSize = SOCKET_BUFFER_SIZE;
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
String8 serverChannelName = name;
serverChannelName.append(" (server)");
outServerChannel = new InputChannel(serverChannelName, sockets[0]);
String8 clientChannelName = name;
clientChannelName.append(" (client)");
outClientChannel = new InputChannel(clientChannelName, sockets[1]);
return OK;
}</code></pre>
這里的Socket創建用到了 Linux中的Socketpaire,在之前的版本中是通過管道來實現的,但是 創建的管道只能是單向的 -- mode 只能是 "r" 或 "w" 而不能是某種組合--用戶只能選擇要么往里寫,要么從中讀,而不能同時在一個管道中進行讀寫。實際應用中,經常會有同時進行讀寫的要求。
Linux實現了一個源自BSD的socketpair調用可以實現上述在同一個文件描述符中進行讀寫的功能(該調用目前也是POSIX規范的一部分 。該系統調用能創建一對已連接的(UNIX族)無名socket。在Linux中,完全可以把這一對socket當成pipe返回的文件描述符一樣使用,唯一的區別就是這一對文件描述符中的任何一個都可讀和可寫。
socketpair產生的文件描述符是一對socket,socket上的標準操作都可以使用,其中也包括shutdown。——利用shutdown,可以實現一個半關閉操作,通知對端本進程不再發送數據,同時仍可以利用該文件描述符接收來自對端的數據。
status_t InputChannel::sendMessage(const InputMessage* msg) {
size_t msgLength = msg->size();
ssize_t nWrite;
do {
nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
} while (nWrite == -1 && errno == EINTR);
.....
return OK;
}
接收消息,通過讀socket的方式來讀取消息。
status_t InputChannel::receiveMessage(InputMessage* msg) {
ssize_t nRead;
do {
nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
} while (nRead == -1 && errno == EINTR);
......
return OK;
}
通過之前的分析,我們已經追蹤到了通過InputChannel發送消息的代碼,接收端的消息處理是如何呢?是如何觸發開始接收消息,消息如何在傳到InputChannel之后,進行的進一步的數據傳遞呢?分析之前,這里先對上面InputChannel進行一個總結。

之前的 setView 中,我們創建了InputChannel之后,開啟了對于InputChannel中輸入事件的監聽。
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
WindowInputEventReceiver的構造函數如下,其繼承自InputEventReceiver。
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
....
}
InputEventReceiver的構造函數源碼如下
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
....
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);
}
這里調用了native方法來做初始化,相關的native方法的實現在 android_view_InputEventReceiver.cpp
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject inputChannelObj, jobject messageQueueObj) {
....
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
receiverWeak, inputChannel, messageQueue);
status_t status = receiver->initialize();
.....
}
根據傳入的 InputChannel 和 MessageQueue ,創建一個NativeInputEventReceiver,然后調用其 initialize 方法。
status_t NativeInputEventReceiver::initialize() {
setFdEvents(ALOOPER_EVENT_INPUT);
return OK;
}
在 initialize() 方法中,只調用了一個函數 setFdEvents ,
void NativeInputEventReceiver::setFdEvents(int events) {
if (mFdEvents != events) {
mFdEvents = events;
int fd = mInputConsumer.getChannel()->getFd();
if (events) {
mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
} else {
mMessageQueue->getLooper()->removeFd(fd);
}
}
}
從InputConsumer中獲取到channel的fd,然后調用Looper的 addFd 方法。
int ALooper_addFd(ALooper* looper, int fd, int ident, int events,
ALooper_callbackFunc callback, void* data) {
return ALooper_to_Looper(looper)->addFd(fd, ident, events, callback, data);
}
Looper的addFd的實現如下
int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
Request request;
request.fd = fd;
request.ident = ident;
request.events = events;
request.seq = mNextRequestSeq++;
request.callback = callback;
request.data = data;
if (mNextRequestSeq == -1) mNextRequestSeq = 0;
struct epoll_event eventItem;
request.initEventItem(&eventItem);
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex < 0) {
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
if (epollResult < 0) {
return -1;
}
mRequests.add(fd, request);
}
}
該方法所執行的操作就是對傳遞的fd添加epoll監控,Looper會循環調用 pollOnce 方法,而 pollOnce 方法的核心實現就是 pollInner 。其代碼大致實現內容為等待消息的到來,當有消息到來后,根據消息類型做一些判斷處理,然后調用其相關的callback。我們當前是對于開啟的socket的一個監聽,當有數據到來,我們便會執行相應的回調。這里對于InputChannel的回調是在調用了NativeInputEventReceiver的 handleEvent 方法。
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
.....
if (events & ALOOPER_EVENT_INPUT) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
return status == OK || status == NO_MEMORY ? 1 : 0;
}
....
return 1;
}
對于Event的處理,這里調用consumeEvents來對事件進行處理。
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
...
for(;;) {
...
InputEvent* inputEvent;
status_t status = mInputConsumer.consume(&mInputEventFactory,
consumeBatches, frameTime, &seq, &inputEvent);
...
}
...
}
InputConsumer是在InputTransport中做的聲明。
status_t InputConsumer::consume(InputEventFactoryInterface* factory,
bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
while (!*outEvent) {
....
status_t result = mChannel->receiveMessage(&mMsg);
....
}
}
調用consume方法會持續的調用InputChannel的receiveMessage方法來從socket中讀取數據。到這里,我們已經將寫入socket的事件讀出來了。InputChannel在創建之后,通過為其InputEventReceiver對其fd進行epoll監控,當有變動的時候,調用InputChannel來接收消息。InputChannel又是如何被設置到InputDispatcher之中的呢?在調用 openInputChannel 方法,創建Socket的完成之后,調用該方法。
public void registerInputChannel(InputChannel inputChannel,
InputWindowHandle inputWindowHandle) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
}
nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
}
nativeRegisterInputManger
static void nativeRegisterInputChannel(JNIEnv env, jclass / clazz /,
jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
NativeInputManager im = reinterpret_cast<NativeInputManager*>(ptr);
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
if (inputChannel == NULL) {
throwInputChannelNotInitialized(env);
return;
}
sp<InputWindowHandle> inputWindowHandle =
android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);
status_t status = im->registerInputChannel(
env, inputChannel, inputWindowHandle, monitor);
if (status) {
String8 message;
message.appendFormat("Failed to register input channel. status=%d", status);
jniThrowRuntimeException(env, message.string());
return;
}
if (! monitor) {
android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
handleInputChannelDisposed, im);
}
}</code></pre>
NativeInputManager的 registerInputChannel 還會調用到InputDispatcher的registerInputChannel,會通過InputChannel創建相應的Connection,同時將InputChannel加入到相應的監控之中。在上面對代碼的分析之中,獲取InputChannel,就是通過這個Connection來獲取的。
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
{ // acquire lock
AutoMutex _l(mLock);
if (getConnectionIndexLocked(inputChannel) >= 0) {
return BAD_VALUE;
}
sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
int fd = inputChannel->getFd();
mConnectionsByFd.add(fd, connection);
if (monitor) {
mMonitoringChannels.push(inputChannel);
}
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
} // release lock
// Wake the looper because some connections have changed.
mLooper->wake();
return OK;
}</code></pre>
經過InputManager的層層傳遞,最終會到達InputDispatcher之中,然后對其進行封裝,并在其內部進行保存,同時也傳遞了相應的窗口的句柄,方便了后期在事件傳遞的時候,對于窗口的判斷。
ViewRootImpl
事件在從socket讀出之后,經過傳遞,最終會調用到ViewRootImpl的 enqueueInputEvent 方法。
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
adjustInputEventForCompatibility(event);
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
QueuedInputEvent last = mPendingInputEventTail;
if (last == null) {
mPendingInputEventHead = q;
mPendingInputEventTail = q;
} else {
last.mNext = q;
mPendingInputEventTail = q;
}
mPendingInputEventCount += 1;
if (processImmediately) {
doProcessInputEvents();
} else {
scheduleProcessInputEvents();
}
}</code></pre>
enqueueInputEvent 方法從InputEventReceiver中獲取到InputEvent,然后將其加入到當前的事件隊列之中,最后調用 doProcessInputEvents 來進行處理。
void doProcessInputEvents() {
while (mPendingInputEventHead != null) {
QueuedInputEvent q = mPendingInputEventHead;
mPendingInputEventHead = q.mNext;
if (mPendingInputEventHead == null) {
mPendingInputEventTail = null;
}
q.mNext = null;
mPendingInputEventCount -= 1;
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);
}
if (mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = false;
mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
}
}</code></pre>
遍歷所有的消息,如果事件類型為觸摸屏事件,對其進行相應的時間修改,最后對于每一個處理完成的事件調用 deliverInputEvent ,
private void deliverInputEvent(QueuedInputEvent q) {
q.mEvent.getSequenceNumber());
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
}
InputStage stage;
if (q.shouldSendToSynthesizer()) {
stage = mSyntheticInputStage;
} else {
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}
if (stage != null) {
stage.deliver(q);
} else {
finishInputEvent(q);
}
}
在事件分發環節,首先進行事件的一個判斷,通過shouldSkipIme來判斷是否傳遞給輸入法,然后決定使用何種InputStage進行消息的繼續傳遞,這里實現了多種InputStage,對于每一個類型的InputStage都實現了一個方法 process 方法來針對不同類型的事件做處理,如果是觸摸屏類的消息,最終會將事件的處理轉交到View的身上。
InputStage中的事件如何傳遞處理,傳遞處理之后,如何進行
對于InputStage涉及的篇幅較多,這里也不再展開,當消息到達ViewRootImpl中后,接下來就是在View間的派發。
來自:https://segmentfault.com/a/1190000012860933