前言
上一篇文章和大家聊到了IMS在SystemServer進程native層中的原理,本文來聊聊App進程是怎么監(jiān)聽IMS分發(fā)出來的輸入信號的.
正文
還記得我寫過WMS系列文章WMS在Activity啟動中的職責 添加窗體(三)中,提到了App第一次渲染的時候會通過ViewRootImpl的addWindow方法,在WMS中為當前的Activity中的PhoneWindow添加一個對應的WindowState進行管理。
讓我們先看看ViewRootImpl中做了什么。
如果遇到什么問題,歡迎來到本文http://www.itdecent.cn/p/e26bb5fae995下討論
ViewRootImpl setView
文件:/frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
....
requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
//核心事件一
mInputChannel = new InputChannel();
}
mForceDecorViewVisibility = (mWindowAttributes.privateFlags
& PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
try {
//核心事件二
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
} catch (RemoteException e) {
...
} finally {
...
}
...
//核心事件三
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
...
// Set up the input pipeline.
CharSequence counterSuffix = attrs.getTitle();
mSyntheticInputStage = new SyntheticInputStage();
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
"aq:native-post-ime:" + counterSuffix);
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
InputStage imeStage = new ImeInputStage(earlyPostImeStage,
"aq:ime:" + counterSuffix);
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
"aq:native-pre-ime:" + counterSuffix);
mFirstInputStage = nativePreImeStage;
mFirstPostImeInputStage = earlyPostImeStage;
}
}
}
在這個過程中,我們可以把它視作三大部分的邏輯
1.沒有為當前的ViewRootImpl初始化InputChannel,則會先創(chuàng)建一個InputChannel。
2.接著把InputChannel對象通過Session的addToDisplay,也就是addWindow發(fā)送到WMS中進行處理。詳細的邏輯請看WMS在Activity啟動中的職責 添加窗體(三)。
3.最后為ViewRootImpl構建接受從InputChannel發(fā)送回來的輸入事件環(huán)境。
核心就是第二和第三點。先來看看第二點,Session的addToDisplay最后是調(diào)用到了WMS的addWindow中。
WMS addWindow
文件:/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
...
synchronized(mWindowMap) {
...
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);
...
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
...
mInputMonitor.setUpdateInputWindowsNeededLw();
boolean focusChanged = false;
if (win.canReceiveKeys()) {
focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
false /*updateInputWindows*/);
if (focusChanged) {
imMayMove = false;
}
}
if (focusChanged) {
mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
}
mInputMonitor.updateInputWindowsLw(false /*force*/);
}
...
return res;
}
我們把InputChannel相關的邏輯抽離出來:
1.首先如果當前的Window對應IWindow沒有對應在WMS的mWindowMap,則會創(chuàng)建一個全新的WindowState對應上。并且調(diào)用WindowState的openInputChannel初始化從ViewRootImpl傳過來的InputChannel
2.使用InputMonitor更新當前的焦點窗口。
我們來看看WindowState的openInputChannel方法。
WindowState
文件:/frameworks/base/services/core/java/com/android/server/wm/WindowState.java
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow,
PowerManagerWrapper powerManagerWrapper) {
super(service);
....
mInputWindowHandle = new InputWindowHandle(
mAppToken != null ? mAppToken.mInputApplicationHandle : null, this, c,
getDisplayId());
}
能看到實際上這個過程誕生了一個很重要的對象InputWindowHandle,輸入窗口的句柄。這個句柄最核心的對象就是通過WindowToken獲取AppToken的InputApplicationHandle。
WindowState openInputChannel
void openInputChannel(InputChannel outInputChannel) {
String name = getName();
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);
}
能看到這個過程,實際上和上一篇文章十分相似的monitorInput一節(jié)中的內(nèi)容十分相似。
依次執(zhí)行了如下的邏輯:
- 1.openInputChannelPair 為Java層的InputChannel在native創(chuàng)建一對InputChannel。
- 2.mInputWindowHandle 持有InputChannel對的0號對應的InputChannel
- 3.把1號位置中的NativeInputChannel賦值給ViewRootImpl傳遞過來的InputChannel。并關閉InputChannel對的1號位置對應的InputChannel。
- 4.把0號位置的InputChannel注冊到IMS底層中,監(jiān)聽輸入時間的到來。
這樣通過socketpair創(chuàng)建的一對socket對象,注冊了一個新的發(fā)送端到IMS的native層中,就能被App端的InputChannel監(jiān)聽到。
從這里就可以知道,0號位置的InputChannel對應的socket就是服務端(發(fā)送端)。關于如何創(chuàng)建InputChannel,以及如何注冊到IMS。這里就不多贅述,請閱讀IMS與事件分發(fā)(上)。
ViewRootImpl 構建輸入事件的監(jiān)聽環(huán)境
if (mInputChannel != null) {
...
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
...
// Set up the input pipeline.
CharSequence counterSuffix = attrs.getTitle();
mSyntheticInputStage = new SyntheticInputStage();
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
"aq:native-post-ime:" + counterSuffix);
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
InputStage imeStage = new ImeInputStage(earlyPostImeStage,
"aq:ime:" + counterSuffix);
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
"aq:native-pre-ime:" + counterSuffix);
mFirstInputStage = nativePreImeStage;
mFirstPostImeInputStage = earlyPostImeStage;
- 1.在ViewRootImpl中構建一個WindowInputEventReceiver對象,這個對象將會監(jiān)聽從IMS傳送過來的輸入事件。
- 2.構建InputStage對象,該系列對象實際上就是當IMS從native傳遞上來后,進行處理的輸入事件"舞臺".
WindowInputEventReceiver ViewRootImpl對輸入事件的監(jiān)聽原理
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
@Override
public void onInputEvent(InputEvent event, int displayId) {
enqueueInputEvent(event, this, 0, true);
}
@Override
public void onBatchedInputEventPending() {
if (mUnbufferedInputDispatch) {
super.onBatchedInputEventPending();
} else {
scheduleConsumeBatchedInput();
}
}
@Override
public void dispose() {
unscheduleConsumeBatchedInput();
super.dispose();
}
}
這個對象很簡單,他繼承于InputEventReceiver。InputEventReceiver對象就是專門監(jiān)聽IMS輸入事件的基類。每當IMS發(fā)送信號來了就會調(diào)用子類的onInputEvent方法,onBatchedInputEventPending。
我們先來看看InputEventReceiver的初始化。
InputEventReceiver
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
...
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);
mCloseGuard.open("dispose");
}
核心實際上就是調(diào)用native方法在native層初始化了IMS事件監(jiān)聽器。
InputEventReceiver native層初始化
文件:/frameworks/base/core/jni/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();
...
receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
return reinterpret_cast<jlong>(receiver.get());
}
這里只是簡單的生成一個NativeInputEventReceiver對象,并調(diào)用了NativeInputEventReceiver的initialize方法。為全局的clazz對象新增一個強引用計數(shù)。
NativeInputEventReceiver
class NativeInputEventReceiver : public LooperCallback {
public:
NativeInputEventReceiver(JNIEnv* env,
jobject receiverWeak, const sp<InputChannel>& inputChannel,
const sp<MessageQueue>& messageQueue);
status_t initialize();
void dispose();
status_t finishInputEvent(uint32_t seq, bool handled);
status_t consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime,
bool* outConsumedBatch);
protected:
virtual ~NativeInputEventReceiver();
private:
struct Finish {
uint32_t seq;
bool handled;
};
jobject mReceiverWeakGlobal;
InputConsumer mInputConsumer;
sp<MessageQueue> mMessageQueue;
PreallocatedInputEventFactory mInputEventFactory;
bool mBatchedInputEventPending;
int mFdEvents;
Vector<Finish> mFinishQueue;
void setFdEvents(int events);
const std::string getInputChannelName() {
return mInputConsumer.getChannel()->getName();
}
virtual int handleEvent(int receiveFd, int events, void* data);
};
從NativeInputEventReceiver的申明能看到實際上他是實現(xiàn)了LooperCallback。LooperCallback這個對象,可以閱讀Handler與相關系統(tǒng)調(diào)用的剖析(上),里面有講解到LooperCallback實際上就是native層Looper回調(diào)后的監(jiān)聽對象,回調(diào)的方法就是虛函數(shù)handleEvent。
在NativeInputEventReceiver有一個十分重要的對象InputConsumer。當IMS回調(diào)了輸入事件后,NativeInputEventReceiver使用InputConsumer在native層中進行處理。
構造函數(shù)沒什么好看的,直接看看initialize初始化的方法。
NativeInputEventReceiver initialize
status_t NativeInputEventReceiver::initialize() {
setFdEvents(ALOOPER_EVENT_INPUT);
return OK;
}
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中的InputChannel中的fd,這里fd就是上面初始化好的接收端的InputChannel。因此就是獲取主線程的Looper并使用Looper監(jiān)聽客戶端的InputChannel。
一旦IMS有信號發(fā)送過來則立即回調(diào)LooperCallback中的handleEvent。
當輸入信號從native層傳送過來了,則會開始回調(diào)handleEvent方法。關于IMS如果讀取輸入事件,處理后傳輸過來,可以閱讀我寫的IMS與事件分發(fā)(上)。
handleEvent App進程處理輸入事件
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
return 0; // remove the callback
}
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;
}
if (events & ALOOPER_EVENT_OUTPUT) {
for (size_t i = 0; i < mFinishQueue.size(); i++) {
const Finish& finish = mFinishQueue.itemAt(i);
status_t status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);
if (status) {
mFinishQueue.removeItemsAt(0, i);
if (status == WOULD_BLOCK) {
return 1; // keep the callback, try again later
}
...
return 0; // remove the callback
}
}
mFinishQueue.clear();
setFdEvents(ALOOPER_EVENT_INPUT);
return 1;
}
return 1;
}
大致上可以分為兩種情況,分別對象Looper注冊的事件類型ALOOPER_EVENT_INPUT和ALOOPER_EVENT_OUTPUT。
很多地方?jīng)]解析清楚:
- ALOOPER_EVENT_INPUT 是指那些可讀的文件描述符傳遞過來的事件
- ALOOPER_EVENT_OUTPUT 是指那些可寫的文件描述符,需要傳遞過去的事件。
在NativeInputEventReceiver中,ALOOPER_EVENT_INPUT代表從驅動讀取到的輸入事件傳遞過來;ALOOPER_EVENT_OUTPUT代表此時需要關閉輸入事件的監(jiān)聽,而傳遞過去的后返回的事件處理。
我們先來看看ALOOPER_EVENT_INPUT對應的事件處理。
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;
}
核心處理方法是consumeEvents。
consumeEvents
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
if (consumeBatches) {
mBatchedInputEventPending = false;
}
if (outConsumedBatch) {
*outConsumedBatch = false;
}
ScopedLocalRef<jobject> receiverObj(env, NULL);
bool skipCallbacks = false;
for (;;) {
uint32_t seq;
InputEvent* inputEvent;
int32_t displayId;
status_t status = mInputConsumer.consume(&mInputEventFactory,
consumeBatches, frameTime, &seq, &inputEvent, &displayId);
if (status) {
if (status == WOULD_BLOCK) {
if (!skipCallbacks && !mBatchedInputEventPending
&& mInputConsumer.hasPendingBatch()) {
mBatchedInputEventPending = true;
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);
}
return OK;
}
return status;
}
...
if (!skipCallbacks) {
....
jobject inputEventObj;
switch (inputEvent->getType()) {
case AINPUT_EVENT_TYPE_KEY:
inputEventObj = android_view_KeyEvent_fromNative(env,
static_cast<KeyEvent*>(inputEvent));
break;
case AINPUT_EVENT_TYPE_MOTION: {
MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent);
if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) {
*outConsumedBatch = true;
}
inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);
break;
}
default:
inputEventObj = NULL;
}
if (inputEventObj) {
//發(fā)送核心
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj,
displayId);
if (env->ExceptionCheck()) {
skipCallbacks = true;
}
env->DeleteLocalRef(inputEventObj);
} else {
skipCallbacks = true;
}
}
if (skipCallbacks) {
mInputConsumer.sendFinishedSignal(seq, false);
}
}
}
1.通過InputConsumer的consume方法消費它持有的InputChannel的輸入事件。
3.如果是Monition類型事件且是多點觸控需要批量處理的,則會通過CallVoidMethod反射調(diào)用InputEventReceiver的dispatchBatchedInputEventPending方法。
2.根據(jù)Key 還是Monition生成對應的Java對象,通過CallVoidMethod反射調(diào)用Java方法,InputEventReceiver的dispatchInputEvent方法。
InputConsumer consume
文件:/frameworks/native/libs/input/InputTransport.cpp
status_t InputConsumer::consume(InputEventFactoryInterface* factory,
bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent,
int32_t* displayId) {
*outSeq = 0;
*outEvent = NULL;
*displayId = -1; // Invalid display.
while (!*outEvent) {
if (mMsgDeferred) {
mMsgDeferred = false;
} else {
status_t result = mChannel->receiveMessage(&mMsg);
if (result) {
if (consumeBatches || result != WOULD_BLOCK) {
result = consumeBatch(factory, frameTime, outSeq, outEvent, displayId);
if (*outEvent) {
break;
}
}
return result;
}
}
switch (mMsg.header.type) {
case InputMessage::TYPE_KEY: {
KeyEvent* keyEvent = factory->createKeyEvent();
if (!keyEvent) return NO_MEMORY;
initializeKeyEvent(keyEvent, &mMsg);
*outSeq = mMsg.body.key.seq;
*outEvent = keyEvent;
break;
}
case InputMessage::TYPE_MOTION: {
ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source);
if (batchIndex >= 0) {
Batch& batch = mBatches.editItemAt(batchIndex);
if (canAddSample(batch, &mMsg)) {
batch.samples.push(mMsg);
break;
} else {
mMsgDeferred = true;
status_t result = consumeSamples(factory,
batch, batch.samples.size(), outSeq, outEvent, displayId);
mBatches.removeAt(batchIndex);
if (result) {
return result;
}
break;
}
}
// Start a new batch if needed.
if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE
|| mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
mBatches.push();
Batch& batch = mBatches.editTop();
batch.samples.push(mMsg);
break;
}
MotionEvent* motionEvent = factory->createMotionEvent();
if (! motionEvent) return NO_MEMORY;
updateTouchState(mMsg);
initializeMotionEvent(motionEvent, &mMsg);
*outSeq = mMsg.body.motion.seq;
*outEvent = motionEvent;
*displayId = mMsg.body.motion.displayId;
break;
}
default:
return UNKNOWN_ERROR;
}
}
return OK;
}
先從InputChannel的recv系統(tǒng)調(diào)用獲取socket里面的InputMessage數(shù)據(jù)。
雖然此時consumeBatches為false,但是result正常情況下不會是WOULD_BLOCK,會先執(zhí)行consumeBatch批量處理觸點事件。
在這個方法中分為兩個類型處理:
1.InputMessage::TYPE_KEY 是key按鍵類型,則通過上面?zhèn)飨聛淼膄actory構建一個KeyEvent對象,初始化后并且返回。
2.InputMessage::TYPE_MOTION 是觸點類型。由于觸點類型可以是多點觸碰,對于移動的觸點,需要進行觸點的跟蹤,因此這里引入了Batch概念,按照批次處理觸點事件。
struct Batch {
Vector<InputMessage> samples;
};
能看到實際上Batch就是一個InputMessage的集合。每當檢測到AMOTION_EVENT_ACTION_MOVE或者AMOTION_EVENT_ACTION_HOVER_MOVE的觸點類型,則會添加到mBatches集合中,等待下一次的更新。
當下一次觸點觸發(fā)了回調(diào),在這個outEvent鏈表不為空的循環(huán)前提下,canAddSample判斷到當前PointerCount和之前的一致,會把InputMessage不斷的添加到Batch的samples集合中。如果出現(xiàn)了不一致則需要consumeSamples進行更新Batch中記錄的InputMessage。
這樣就能跟蹤到了這一批次的觸點的軌跡,以及新增的觸點。
如果只有單個觸點則生成MotionEvent對象賦值給指針返回。
我們來看看InputEventReceiver是通過InputConsumer消費后是怎么觸發(fā)接下來的邏輯。我們只看單點觸發(fā)的邏輯。
InputReceiver 分發(fā)輸入事件
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj,
displayId);
實際上對應的是:
private void dispatchInputEvent(int seq, InputEvent event, int displayId) {
mSeqMap.put(event.getSequenceNumber(), seq);
onInputEvent(event, displayId);
}
而onInputEvent這個方法實際上就是對應WindowInputEventReceiver。
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
@Override
public void onInputEvent(InputEvent event, int displayId) {
enqueueInputEvent(event, this, 0, true);
}
可以看到最后回調(diào)到了enqueueInputEvent方法中。
enqueueInputEvent
void enqueueInputEvent(InputEvent event) {
enqueueInputEvent(event, null, 0, false);
}
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();
}
}
能看到整個很久愛都難,就是生成一個obtainQueuedInputEvent對象,添加到mPendingInputEventTail鏈表的末端,調(diào)用scheduleProcessInputEvents方法分發(fā)。如果是需要立即響應則調(diào)用doProcessInputEvents方法。
scheduleProcessInputEvents
private void scheduleProcessInputEvents() {
if (!mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = true;
Message msg = mHandler.obtainMessage(MSG_PROCESS_INPUT_EVENTS);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
}
能看到此時發(fā)送了一個MSG_PROCESS_INPUT_EVENTS一個Asynchronous異步消息。其實就是一個能在同步屏障內(nèi)優(yōu)先執(zhí)行的消息。
case MSG_PROCESS_INPUT_EVENTS:
mProcessInputEventsScheduled = false;
doProcessInputEvents();
break;
核心還是調(diào)用了doProcessInputEvents。
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);
}
}
Choreographer.mFrameInfo 更新了分發(fā)時間后,整個過程最核心的邏輯就是循環(huán)遍歷mPendingInputEventHead調(diào)用deliverInputEvent進行事件的分發(fā)QueuedInputEvent。
deliverInputEvent
private void deliverInputEvent(QueuedInputEvent q) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
}
InputStage stage;
if (q.shouldSendToSynthesizer()) {
stage = mSyntheticInputStage;
} else {
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}
if (q.mEvent instanceof KeyEvent) {
mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
}
if (stage != null) {
handleWindowFocusChanged();
stage.deliver(q);
} else {
finishInputEvent(q);
}
}
邏輯分為如下幾個步驟:
1.QueuedInputEvent的shouldSendToSynthesizer判斷默認是false,shouldSkipIme也是false。此時InputStage就是mFirstInputStage。這個對象就是NativePreImeInputStage。
2.如果獲取到的stage不為空,則調(diào)用NativePreImeInputStage的deliver方法分發(fā)事件。
ViewRootImpl 構建輸入事件的接收環(huán)境
mSyntheticInputStage = new SyntheticInputStage();
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
"aq:native-post-ime:" + counterSuffix);
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
InputStage imeStage = new ImeInputStage(earlyPostImeStage,
"aq:ime:" + counterSuffix);
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
"aq:native-pre-ime:" + counterSuffix);
mFirstInputStage = nativePreImeStage;
mFirstPostImeInputStage = earlyPostImeStage;
能看到這里面構建很多InputStage對象。這些對象都是通過責任鏈設計全部嵌套到一起。
我們簡單的看看它的UML圖,來區(qū)分他們的直接的關系:

InputStage 的分發(fā)入口
先來看看InputStage的deliver
public final void deliver(QueuedInputEvent q) {
if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
forward(q);
} else if (shouldDropInputEvent(q)) {
finish(q, false);
} else {
apply(q, onProcess(q));
}
}
protected void finish(QueuedInputEvent q, boolean handled) {
q.mFlags |= QueuedInputEvent.FLAG_FINISHED;
if (handled) {
q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED;
}
forward(q);
}
protected void forward(QueuedInputEvent q) {
onDeliverToNext(q);
}
protected int onProcess(QueuedInputEvent q) {
return FORWARD;
}
protected void onDeliverToNext(QueuedInputEvent q) {
if (mNext != null) {
mNext.deliver(q);
} else {
finishInputEvent(q);
}
}
protected void apply(QueuedInputEvent q, int result) {
if (result == FORWARD) {
forward(q);
} else if (result == FINISH_HANDLED) {
finish(q, true);
} else if (result == FINISH_NOT_HANDLED) {
finish(q, false);
} else {
throw new IllegalArgumentException("Invalid result: " + result);
}
}
deliver的入口會判斷當前QueuedInputEvent的狀態(tài)。
如果判斷QueuedInputEvent打開FLAG_FINISHED標志位,換句話說就是不是通過finish方法進來的,就會執(zhí)行forward的方法。
如果判斷到當前Window失去焦點,或者還沒有進行刷新ui,QueuedInputEvent則執(zhí)行finish
剩下的情況執(zhí)行apply的默認方法,而執(zhí)行的方法由每一個InputStage的子類復寫onProcess標志位決定的。
我們來看看對整個鏈路從NativePreImeInputStage開始逆推回去,關鍵還是看apply中的方法。
在所有的InputStage中分為兩類,一類是直接繼承InputStage,一類是繼承AsyncInputStage,我們優(yōu)先看看AsyncInputStage。
AsyncInputStage
abstract class AsyncInputStage extends InputStage {
private final String mTraceCounter;
private QueuedInputEvent mQueueHead;
private QueuedInputEvent mQueueTail;
private int mQueueLength;
protected static final int DEFER = 3;
....
protected void defer(QueuedInputEvent q) {
q.mFlags |= QueuedInputEvent.FLAG_DEFERRED;
enqueue(q);
}
@Override
protected void forward(QueuedInputEvent q) {
q.mFlags &= ~QueuedInputEvent.FLAG_DEFERRED;
QueuedInputEvent curr = mQueueHead;
if (curr == null) {
super.forward(q);
return;
}
final int deviceId = q.mEvent.getDeviceId();
QueuedInputEvent prev = null;
boolean blocked = false;
while (curr != null && curr != q) {
if (!blocked && deviceId == curr.mEvent.getDeviceId()) {
blocked = true;
}
prev = curr;
curr = curr.mNext;
}
if (blocked) {
if (curr == null) {
enqueue(q);
}
return;
}
if (curr != null) {
curr = curr.mNext;
dequeue(q, prev);
}
super.forward(q);
while (curr != null) {
if (deviceId == curr.mEvent.getDeviceId()) {
if ((curr.mFlags & QueuedInputEvent.FLAG_DEFERRED) != 0) {
break;
}
QueuedInputEvent next = curr.mNext;
dequeue(curr, prev);
super.forward(curr);
curr = next;
} else {
prev = curr;
curr = curr.mNext;
}
}
}
private void enqueue(QueuedInputEvent q) {
if (mQueueTail == null) {
mQueueHead = q;
mQueueTail = q;
} else {
mQueueTail.mNext = q;
mQueueTail = q;
}
mQueueLength += 1;
}
private void dequeue(QueuedInputEvent q, QueuedInputEvent prev) {
if (prev == null) {
mQueueHead = q.mNext;
} else {
prev.mNext = q.mNext;
}
if (mQueueTail == q) {
mQueueTail = prev;
}
q.mNext = null;
mQueueLength -= 1;
}
}
在AsyncInputStage存儲了一個QueuedInputEvent鏈表。當判斷到事件打開了FLAG_FINISHED,其在核心方法forward做了如下的事情:
當鏈表中沒有任何待分發(fā)的事件,直接調(diào)用父類的forward方法,也就調(diào)用onDeliverNext方法,在onDeliverNext如果當前InputStage不存在下一個InputStage則會調(diào)用finishInputEvent。
-
當存在待分發(fā)的事件鏈表,則會嘗試判斷是否已經(jīng)存在相同的輸入設備(也就是相同的輸入類型)相同事件對象。
- 如果找到了相同的輸入設備id則block為true,找到相同事件對象或者末尾則跳出循環(huán)。
- 如果遍歷剛好在末尾,說明沒有相同的事件則通過enqueue添加到事件鏈表末尾。
- 如果curr不為空,說明此時有相同的事件則dequeue 出隊當前的輸入事件,調(diào)用父類forward。
- 如果經(jīng)過forward的處理,事件隊列還存在輸入事件關閉FLAG_DEFERRED標志位的QueuedInputEvent,則繼續(xù)遍歷鏈表進行消費。
- 如果找到了相同的輸入設備id則block為true,找到相同事件對象或者末尾則跳出循環(huán)。
finishInputEvent
private void finishInputEvent(QueuedInputEvent q) {
if (q.mReceiver != null) {
boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
q.mReceiver.finishInputEvent(q.mEvent, handled);
} else {
...
}
recycleQueuedInputEvent(q);
}
能看到這個過程中很簡單,如果QueuedInputEvent持有了InputEventReceiver對象則會InputEventReceiver.finishInputEvent進行native方法的調(diào)用,告訴native層銷毀了當前的事件。
public final void finishInputEvent(InputEvent event, boolean handled) {
if (event == null) {
throw new IllegalArgumentException("event must not be null");
}
if (mReceiverPtr == 0) {
....
} else {
int index = mSeqMap.indexOfKey(event.getSequenceNumber());
if (index < 0) {
...
} else {
int seq = mSeqMap.valueAt(index);
mSeqMap.removeAt(index);
nativeFinishInputEvent(mReceiverPtr, seq, handled);
}
}
event.recycleIfNeededAfterDispatch();
}
NativeInputEventReceiver finishInputEvent
status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {
status_t status = mInputConsumer.sendFinishedSignal(seq, handled);
if (status) {
if (status == WOULD_BLOCK) {
Finish finish;
finish.seq = seq;
finish.handled = handled;
mFinishQueue.add(finish);
if (mFinishQueue.size() == 1) {
setFdEvents(ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT);
}
return OK;
}
}
return status;
}
能看到很簡單就是調(diào)用InputConsumer的sendFinishedSignal方法發(fā)送該輸入事件的序列號處理對應在InputDispatcher中事件。
InputStage分類
當InputStage需要開始分發(fā)事件,就會調(diào)用apply方法,而apply中就會調(diào)用onProcess方法。每一個子類InputStage的onProcess其實就是意味著這個InputStage做了什么事情。
接下來我們就按照責任鏈的嵌套順序來看看InputStage,每一個輸入階段都做了什么。
NativePreImeInputStage
final class NativePreImeInputStage extends AsyncInputStage
implements InputQueue.FinishedInputEventCallback {
public NativePreImeInputStage(InputStage next, String traceCounter) {
super(next, traceCounter);
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (mInputQueue != null && q.mEvent instanceof KeyEvent) {
mInputQueue.sendInputEvent(q.mEvent, q, true, this);
return DEFER;
}
return FORWARD;
}
...
}
NativePreImeInputStage實際上就是就是處理InputQueue。
ViewPreImeInputStage
final class ViewPreImeInputStage extends InputStage {
public ViewPreImeInputStage(InputStage next) {
super(next);
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);
}
return FORWARD;
}
private int processKeyEvent(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
if (mView.dispatchKeyEventPreIme(event)) {
return FINISH_HANDLED;
}
return FORWARD;
}
}
ViewPreImeInputStage 這個InputStage是預處理KeyEvent,把鍵盤等事件通過DecorView的dispatchKeyEventPreIme進行預處理分發(fā)。
ImeInputStage
final class ImeInputStage extends AsyncInputStage
implements InputMethodManager.FinishedInputEventCallback {
public ImeInputStage(InputStage next, String traceCounter) {
super(next, traceCounter);
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (mLastWasImTarget && !isInLocalFocusMode()) {
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) {
final InputEvent event = q.mEvent;
int result = imm.dispatchInputEvent(event, q, this, mHandler);
if (result == InputMethodManager.DISPATCH_HANDLED) {
return FINISH_HANDLED;
} else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
return FORWARD;
} else {
return DEFER; // callback will be invoked later
}
}
}
return FORWARD;
}
...
}
ImeInputStage專門處理軟鍵盤的事件分發(fā)。
EarlyPostImeInputStage
final class EarlyPostImeInputStage extends InputStage {
public EarlyPostImeInputStage(InputStage next) {
super(next);
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);
} else {
final int source = q.mEvent.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
return processPointerEvent(q);
}
}
return FORWARD;
}
private int processKeyEvent(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
if (mAttachInfo.mTooltipHost != null) {
mAttachInfo.mTooltipHost.handleTooltipKey(event);
}
if (checkForLeavingTouchModeAndConsume(event)) {
return FINISH_HANDLED;
}
mFallbackEventHandler.preDispatchKeyEvent(event);
return FORWARD;
}
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
if (mTranslator != null) {
mTranslator.translateEventInScreenToAppWindow(event);
}
final int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) {
ensureTouchMode(event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN));
}
if (action == MotionEvent.ACTION_DOWN) {
// Upon motion event within app window, close autofill ui.
AutofillManager afm = getAutofillManager();
if (afm != null) {
afm.requestHideFillUi();
}
}
if (action == MotionEvent.ACTION_DOWN && mAttachInfo.mTooltipHost != null) {
mAttachInfo.mTooltipHost.hideTooltip();
}
if (mCurScrollY != 0) {
event.offsetLocation(0, mCurScrollY);
}
if (event.isTouchEvent()) {
mLastTouchPoint.x = event.getRawX();
mLastTouchPoint.y = event.getRawY();
mLastTouchSource = event.getSource();
}
return FORWARD;
}
}
該方法實際上是處理mFallbackEventHandler的Key事件。這個對象是PhoneFallbackEventHandler,里面處理了手機屏幕外按鍵的事件處理,如多媒體音量,通話音量等等。還處理了Touch模式以及AutofillManager。
NativePostImeInputStage
final class NativePostImeInputStage extends AsyncInputStage
implements InputQueue.FinishedInputEventCallback {
public NativePostImeInputStage(InputStage next, String traceCounter) {
super(next, traceCounter);
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (mInputQueue != null) {
mInputQueue.sendInputEvent(q.mEvent, q, false, this);
return DEFER;
}
return FORWARD;
}
...
}
NativePostImeInputStage繼續(xù)處理了之前還需要繼續(xù)處理InputQueue中的事件。
ViewPostImeInputStage
final class ViewPostImeInputStage extends InputStage {
public ViewPostImeInputStage(InputStage next) {
super(next);
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);
} else {
final int source = q.mEvent.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
return processPointerEvent(q);
} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
return processTrackballEvent(q);
} else {
return processGenericMotionEvent(q);
}
}
}
....
}
- 判斷是KeyEvent類型則processKeyEvent開始分發(fā)KeyEntry
- 如果不是KeyEvent,但是是手指輸入設備,則調(diào)用processPointerEvent。最終會調(diào)用View 的dispatchPointerEvent
- 如果來自SOURCE_CLASS_TRACKBALL輸入設備,則調(diào)用processTrackballEvent。最終會調(diào)用View 的dispatchTrackballEvent
- 剩下的則會通過processGenericMotionEvent分發(fā)Monition。會調(diào)用View的dispatchGenericMotionEvent方法。
SyntheticInputStage
final class SyntheticInputStage extends InputStage {
private final SyntheticTrackballHandler mTrackball = new SyntheticTrackballHandler();
private final SyntheticJoystickHandler mJoystick = new SyntheticJoystickHandler();
private final SyntheticTouchNavigationHandler mTouchNavigation =
new SyntheticTouchNavigationHandler();
private final SyntheticKeyboardHandler mKeyboard = new SyntheticKeyboardHandler();
public SyntheticInputStage() {
super(null);
}
@Override
protected int onProcess(QueuedInputEvent q) {
q.mFlags |= QueuedInputEvent.FLAG_RESYNTHESIZED;
if (q.mEvent instanceof MotionEvent) {
final MotionEvent event = (MotionEvent)q.mEvent;
final int source = event.getSource();
if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
mTrackball.process(event);
return FINISH_HANDLED;
} else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
mJoystick.process(event);
return FINISH_HANDLED;
} else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
== InputDevice.SOURCE_TOUCH_NAVIGATION) {
mTouchNavigation.process(event);
return FINISH_HANDLED;
}
} else if ((q.mFlags & QueuedInputEvent.FLAG_UNHANDLED) != 0) {
mKeyboard.process((KeyEvent)q.mEvent);
return FINISH_HANDLED;
}
return FORWARD;
}
@Override
protected void onDeliverToNext(QueuedInputEvent q) {
if ((q.mFlags & QueuedInputEvent.FLAG_RESYNTHESIZED) == 0) {
if (q.mEvent instanceof MotionEvent) {
final MotionEvent event = (MotionEvent)q.mEvent;
final int source = event.getSource();
if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
mTrackball.cancel();
} else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
mJoystick.cancel();
} else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
== InputDevice.SOURCE_TOUCH_NAVIGATION) {
mTouchNavigation.cancel(event);
}
}
}
super.onDeliverToNext(q);
}
...
}
在對剩下不同的設備輸入事件進行通過對應的處理對象進行enqueue處理。
View觸點事件的分發(fā)
在這么多的InputStage 輸入處理階段對象中,需要我們進行重點關注的是ViewPostImeInputStage。在這個階段中對Key和Motion對象進行處理。
Key事件分發(fā)
private int processKeyEvent(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
....
// Deliver the key to the view hierarchy.
if (mView.dispatchKeyEvent(event)) {
return FINISH_HANDLED;
}
...
return FORWARD;
}
實際上此時的mView是DecorView。通過根布局的dispatchKeyEvent向整個View視圖層級分發(fā)。
DecorView dispatchKeyEvent
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
final int keyCode = event.getKeyCode();
final int action = event.getAction();
final boolean isDown = action == KeyEvent.ACTION_DOWN;
...
if (!mWindow.isDestroyed()) {
final Window.Callback cb = mWindow.getCallback();
final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
: super.dispatchKeyEvent(event);
if (handled) {
return true;
}
}
return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
: mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
}
DecorView會校驗它持有的PhoneWindow是否被銷毀。沒有銷毀則獲取PhoneWindow的Window.Callback監(jiān)聽對象,調(diào)用它的dispatchKeyEvent方法。
如果判斷dispatchKeyEvent處理的事件返回false,說明需要繼續(xù)處理Key事件。因此此時發(fā)現(xiàn)當前的KeyEvent是ACTION_DOWN,則會調(diào)用PhoneWindow的onKeyDown方法,否則則調(diào)用onKeyUp。
我們主要來考察Key的事件分發(fā).注意此時正在監(jiān)聽Window.Callback的回調(diào)是Activity。
Activity dispatchKeyEvent
public boolean dispatchKeyEvent(KeyEvent event) {
onUserInteraction();
final int keyCode = event.getKeyCode();
if (keyCode == KeyEvent.KEYCODE_MENU &&
mActionBar != null && mActionBar.onMenuKeyEvent(event)) {
return true;
}
Window win = getWindow();
if (win.superDispatchKeyEvent(event)) {
return true;
}
View decor = mDecor;
if (decor == null) decor = win.getDecorView();
return event.dispatch(this, decor != null
? decor.getKeyDispatcherState() : null, this);
}
Activity獲取PhoneWindow對象,調(diào)用PhoneWindow的superDispatchKeyEvent。
PhoneWindow superDispatchKeyEvent
public boolean superDispatchKeyEvent(KeyEvent event) {
return mDecor.superDispatchKeyEvent(event);
}
DecorView superDispatchKeyEvent
public boolean superDispatchKeyEvent(KeyEvent event) {
...
if (super.dispatchKeyEvent(event)) {
return true;
}
return (getViewRootImpl() != null) && getViewRootImpl().dispatchUnhandledKeyEvent(event);
}
調(diào)用了核心的了ViewGroup的dispatchKeyEvent方法。
ViewGroup dispatchKeyEvent
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onKeyEvent(event, 1);
}
if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
== (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
if (super.dispatchKeyEvent(event)) {
return true;
}
} else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
== PFLAG_HAS_BOUNDS) {
if (mFocused.dispatchKeyEvent(event)) {
return true;
}
}
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
}
return false;
}
在ViewGroup中會記錄當前的焦點View。如果是當前的ViewGroup帶上了焦點,則會調(diào)用父類的dispatchKeyEvent方法。否則則嘗試的查找當前的ViewGroup中焦點View的dispatchKeyEvent繼續(xù)分發(fā)Key事件。
View dispatchKeyEvent
public boolean dispatchKeyEvent(KeyEvent event) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onKeyEvent(event, 0);
}
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
return true;
}
if (event.dispatch(this, mAttachInfo != null
? mAttachInfo.mKeyDispatchState : null, this)) {
return true;
}
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
return false;
}
能夠在這里看到了此時會回調(diào)我們給當前View設置的mOnKeyListener回調(diào)onKey方法。
這樣就完成了對Key事件的監(jiān)聽。
ViewRootImpl Motion觸點事件分發(fā)
我們回到ViewPostImeInputStage中對Motion的觸點事件處理processPointerEvent的考察。
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
mAttachInfo.mUnbufferedDispatchRequested = false;
mAttachInfo.mHandlingPointerEvent = true;
boolean handled = mView.dispatchPointerEvent(event);
...
return handled ? FINISH_HANDLED : FORWARD;
}
很簡單就是調(diào)用了DecorView的dispatchPointerEvent方法。而DecorView的dispatchPointerEvent就是調(diào)用了View的dispatchPointerEvent
View dispatchPointerEvent
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
這里就會判斷是否是觸點事件,如果是則調(diào)用dispatchTouchEvent方法,否則則dispatchGenericMotionEvent處理。我們考察dispatchTouchEvent觸點事件的分發(fā)。
ViewGroup dispatchTouchEvent
從這個方法開始,就是我們熟悉的事件分發(fā)處理:
public boolean dispatchTouchEvent(MotionEvent ev) {
...
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
if (actionMasked == MotionEvent.ACTION_DOWN) {
cancelAndClearTouchTargets(ev);
resetTouchState();
}
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
//核心事件1
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
intercepted = true;
}
...
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() : null;
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex(); // always 0 for down
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
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;
}
...
}
if (preorderedList != null) preorderedList.clear();
}
if (newTouchTarget == null && mFirstTouchTarget != null) {
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
// Dispatch to touch targets.
if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
if (canceled
|| actionMasked == MotionEvent.ACTION_UP
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
resetTouchState();
} else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
final int actionIndex = ev.getActionIndex();
final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
removePointersFromTouchTargets(idBitsToRemove);
}
}
...
return handled;
}
在這個過程中如下執(zhí)行了幾個核心邏輯:
- 1.onInterceptTouchEvent 校驗當前的ViewGroup是否需要攔截當前事件分發(fā)到子View。
- 2.如果不進行攔截事件分發(fā),則代表可以繼續(xù)分發(fā)觸點事件。首先會對當前ViewGroup中所有的子View先按照z軸的順序排序。然后按照這個順序遍歷每一個子View.
- 1.為了進行優(yōu)化,ViewGroup會記錄TouchTarget對象鏈表。TouchTarget這個鏈表實際上就是記錄每一次可以進行焦點處理的子View。通過isTransformedTouchPointInView方法校驗當前的觸點是否在子View范圍中,如果當前能夠獲取到TouchTarget對象,則跳出當前遍歷z軸順序的循環(huán)。并在下面一個新循環(huán)中處理dispatchTransformedTouchEvent。
- 2.如果TouchTarget中獲取不到有效的觸點對象,說明該View已經(jīng)清空了一次TouchTarget鏈表或者第一次。則會dispatchTransformedTouchEvent處理每一個子View成功后,為對應的子View添加一個對應的TouchTarget。
來看看isTransformedTouchPointInView是怎么判斷觸點事件在View的范圍:
protected boolean isTransformedTouchPointInView(float x, float y, View child,
PointF outLocalPoint) {
final float[] point = getTempPoint();
point[0] = x;
point[1] = y;
transformPointToViewLocal(point, child);
final boolean isInView = child.pointInView(point[0], point[1]);
if (isInView && outLocalPoint != null) {
outLocalPoint.set(point[0], point[1]);
}
return isInView;
}
public boolean pointInView(float localX, float localY, float slop) {
return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
localY < ((mBottom - mTop) + slop);
}
很簡單知道子View的四個邊緣和滑動的距離,只要在這四個區(qū)域內(nèi)即可。
核心分發(fā)給子View的核心是dispatchTransformedTouchEvent。
ViewGroup dispatchTransformedTouchEvent
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
final int oldAction = event.getAction();
...
final int oldPointerIdBits = event.getPointerIdBits();
final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
if (newPointerIdBits == 0) {
return false;
}
final MotionEvent transformedEvent;
...
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
handled = child.dispatchTouchEvent(transformedEvent);
}
// Done.
transformedEvent.recycle();
return handled;
}
如果child為null,說明可能在這個ViewGroup中沒找到需要觸點處理的子View。則調(diào)用了父類View的dispatchTouchEvent。
如果child不為null,則調(diào)用該子View的dispatchTouchEvent方法。
View dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent event) {
...
boolean result = false;
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
stopNestedScroll();
}
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
if (!result && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
if (actionMasked == MotionEvent.ACTION_UP ||
actionMasked == MotionEvent.ACTION_CANCEL ||
(actionMasked == MotionEvent.ACTION_DOWN && !result)) {
stopNestedScroll();
}
return result;
}
在這個過程中按照順序執(zhí)行如下的步驟:
- 1.判斷當前是ACTION_DOWN 則暫?;瑒?/li>
- 2.判斷mOnTouchListener不為空,先執(zhí)行mOnTouchListener的onTouch方法。
- 3.回調(diào)onTouchEvent方法
- 4.判斷到是ACTION_UP或者ACTION_CANCEL或者ACTION_DOWN,且不是拽動則暫?;瑒印?/li>
View onTouchEvent
public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
final int action = event.getAction();
final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
if ((viewFlags & ENABLED_MASK) == DISABLED) {
if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
return clickable;
}
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
...
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (prepressed) {
setPressed(true, x, y);
}
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
removeLongPressCallback();
if (!focusTaken) {
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClickInternal();
}
}
}
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
}
if (prepressed) {
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
mUnsetPressedState.run();
}
removeTapCallback();
}
mIgnoreNextUpEvent = false;
break;
case MotionEvent.ACTION_DOWN:
...
break;
case MotionEvent.ACTION_CANCEL:
...
break;
case MotionEvent.ACTION_MOVE:
....
break;
}
return true;
}
return false;
}
我們只需要關注Up手勢中做了比較重要的邏輯:
- 如果可以進行聚焦,但是沒有焦點則先requestFocus進行焦點的請求
- 如果prepressed為true,則調(diào)用setPressed把下按狀態(tài)設置為true
- 調(diào)用post發(fā)送PerformClick的runnable,如果發(fā)送失敗則調(diào)用performClickInternal直接發(fā)送onClick方法。
public boolean performClick() {
notifyAutofillManagerOnClick();
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
notifyEnterOrExitForAutoFillIfNeeded(true);
return result;
}
總結
到這里就結束了對IMS相關的邏輯分析。
根據(jù)上一次的設計圖,來展示更加完整的結構圖

App進程初始化IMS的監(jiān)聽:
- 當Activity初始化后,在resume生命周期,會調(diào)用ViewRootImpl的setView方法。
- 在這個方法中,會調(diào)用addWindow,把初始化好的InputChannel傳送到WMS的WindowState中。WindowState會為InputChannel初始化一對socket文件描述符,一端在監(jiān)聽IMS的事件發(fā)送,另一段是監(jiān)聽發(fā)送的到來。
- App主線程的WindowInputEventReceiver 對象會通過Looper會監(jiān)聽InputChannel的接收端。一旦接收端有事件發(fā)送到來,就會喚醒Looper在InputConsumer中進行消費。
- InputConsumer消費觸點對象后,會回調(diào)到WindowInputEventReceiver中,調(diào)用Looper發(fā)送一個IMS發(fā)送對象,準備在InputStage中進行處理。
InputStage是輸入事件的處理階段,是一種很典型的責任鏈設計模式,每一個處理階段都會知道下一個處理階段是什么,這種設計在App開發(fā)中十分常見,對于冗長的業(yè)務,我們可以通過這種設計靈活的進行解藕。
- NativePreImeInputStage 預處理InputQueue
- ViewPreImeInputStage 這個InputStage是預處理KeyEvent,把鍵盤等事件通過DecorView的dispatchKeyEventPreIme進行預處理分發(fā)。
- ImeInputStage專門處理軟鍵盤的事件分發(fā)
- EarlyPostImeInputStage 處理mFallbackEventHandler的Key事件。這個對象是PhoneFallbackEventHandler,里面處理了手機屏幕外按鍵的事件處理,如多媒體音量,通話音量等等。還處理了Touch模式以及AutofillManager
- NativePostImeInputStage繼續(xù)處理了之前還需要繼續(xù)處理InputQueue中的事件
- ViewPostImeInputStage 對Key和Motion進行View層級的事件分發(fā)
- SyntheticInputStage 根據(jù)設備進行不同的輸入事件入隊處理(如觸屏球等)。
關于InputQueue和SyntheticInputStage我們不需要過多的關注。我們App開發(fā)還是主要關注ViewPostImeInputStage是如何分發(fā)的。
事件分發(fā)的流程順序:
- ViewGroup的dispatchTouchEvent 分發(fā)事件
- ViewGroup的onInterceptTouchEvent 攔截事件
- View的dispatchTouchEvent
- onTouchListener.onTouch 的監(jiān)聽回調(diào)
- onTouchEvent 方法回調(diào)
- onClickListener.onClick 當是手勢抬起時,點擊事件回調(diào)
這個流程是面試最常見的問題之一,記住即可。
后記
原計劃是準備聊聊PMS的安裝Apk原理。不過,我個人覺得還是先把另外三大組件都聊一邊,我們再回來聊聊PMS的安裝原理。
然后以PMS安裝的dex文件的介紹,來開始Android art虛擬機的原理介紹。