應用程序註冊消息監聽過程分析
CPP層InputManagerService啓動後就需要監聽按鍵輸入了,當InputManagerService監聽到鍵盤輸入的事件後就需要分發鍵盤事件,但是分發給誰呢?這裏首先應該是分發給當前激活的Window窗口,但是當前激活的window窗口怎麼才能接受到消息呢,window窗口需要註冊一個鍵盤消息接收通道到InputManagerService中,那麼如何註冊的呢?我們知道一個Activity對應一個ViewRootImpl對象,在Activity啓動時會創建一個ViewRootImpl對象,並調用其setView函數把Activity的DecorView設置到ViewRootImpl中,而Activity正是在setView函數中註冊鍵盤消息的接收通道的。
Step 1. ViewRootImpl.setView
該函數定義在frameworks/base/core/java/android/view/ViewRootImpl.java中
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
//調用requestLayout來通知InputManagerService當前的窗口是激活的窗口
requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
//如果該窗口沒有指定INPUT_FEATURE_NO_INPUT_CHANNEL屬性,則創建消息接收通道InputChannel
mInputChannel = new InputChannel();
}
try {
//通過binder調用,調用server端的Session對象來跟WindowManagerService通信,該函數最後會調
//用到WindowManagerService的addWindow函數,函數中會創建一對InputChannel(server/client),
//這樣在函數調用結束後,mInputChannel就變成了client端的對象。在
//frameworks/base/core/java/android/view/IWindowSession.aidl的
//addToDisplay函數的聲明中,InputChannel指定的數據流的流向是out,因此
//WindowManagerService修改了mInputChannel,客戶端就能拿到這個對象的數據了。
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (Exception e) {
...
}
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
//初始化WindowInputEventReceiver,按鍵消息會從native層傳到該對象的onInputEvent函數
//中,onInputEvent函數是按鍵在應用端java層分發的起始端。
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
}
}
這個函數與註冊鍵盤消息通道的相關主要有三個功能:
一是調用requestLayout函數來通知InputManagerService,這個Activity窗口是當前被激活的窗口,同時將所有的窗口註冊到InputDispatcher中
二是調用mWindowSession的add成員函數來把鍵盤消息接收通道的server端註冊端註冊到CPP層的InputManagerService中,client端註冊到本應用程序的消息循環Looper中,這樣當InputManagerService監控到有鍵盤消息的時候,就會找到當前被激活的窗口,然後找到其在InputManagerService中對應的鍵盤消息接收通道(InputChannel),通過這個通道在InputManagerService的server端來通知應用程序消息循環的client端,這樣就把鍵盤消息分發給當前激活的Activity窗口了
三是應用程序這一側註冊消息接收通道
Step 2. ViewRootImpl.requestLayout
該函數定義在frameworks/base/core/java/android/view/ViewRootImpl.java中
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
這裏調用了scheduleTraversals函數來做進一步的操作,該函數調用mChoreographer來post一個Runnable到Looper中,之後會執行mTraversalRunnable中的run方法,即調用doTraversal函數
Step 3. ViewRootImpl.doTraversal
該函數定義在frameworks/base/core/java/android/view/ViewRootImpl.java中
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
performTraversals();
}
}
該函數主要是執行performTraversals()函數,進而調用relayoutWindow函數,在該函數中又會調用mWindowSession的relayout進入到java層的WindowManagerService的relayoutWindow函數,該函數會調用mInputMonitor.updateInputWindowsLw(true /force/);mInputMonitor是InputMonitor對象。
Step 4. InputMonitor.updateInputWindowsLw
該函數定義在frameworks/base/core/java/android/view/ViewRootImpl.java中
public void updateInputWindowsLw(boolean force) {
boolean addInputConsumerHandle = mService.mInputConsumer != null;
// Add all windows on the default display.
final int numDisplays = mService.mDisplayContents.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
WindowList windows = mService.mDisplayContents.valueAt(displayNdx).getWindowList();
for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
final WindowState child = windows.get(winNdx);
final InputChannel inputChannel = child.mInputChannel;
final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
if (inputChannel == null || inputWindowHandle == null || child.mRemoved) {
// Skip this window because it cannot possibly receive input.
continue;
}
if (addInputConsumerHandle
&& inputWindowHandle.layer <= mService.mInputConsumer.mWindowHandle.layer) {
addInputWindowHandleLw(mService.mInputConsumer.mWindowHandle);
addInputConsumerHandle = false;
}
final int flags = child.mAttrs.flags;
final int privateFlags = child.mAttrs.privateFlags;
final int type = child.mAttrs.type;
final boolean hasFocus = (child == mInputFocus);
final boolean isVisible = child.isVisibleLw();
if ((privateFlags
& WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS)
!= 0) {
disableWallpaperTouchEvents = true;
}
final boolean hasWallpaper = (child == mService.mWallpaperTarget)
&& (privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD) == 0
&& !disableWallpaperTouchEvents;
final boolean onDefaultDisplay = (child.getDisplayId() == Display.DEFAULT_DISPLAY);
// If there's a drag in progress and 'child' is a potential drop target,
// make sure it's been told about the drag
if (inDrag && isVisible && onDefaultDisplay) {
mService.mDragState.sendDragStartedIfNeededLw(child);
}
addInputWindowHandleLw(inputWindowHandle, child, flags, type, isVisible, hasFocus,
hasWallpaper);
}
}
// Send windows to native code.
mService.mInputManager.setInputWindows(mInputWindowHandles);
}
這個函數將當前系統中帶有InputChannel的Activity窗口都設置爲InputManagerService的輸入窗口,但是後面我們會看到,只有當前激活的窗口才會響應鍵盤消息。
Step 5. InputManagerService.setInputWindows
該函數定義在frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public void setInputWindows(InputWindowHandle[] windowHandles) {
nativeSetInputWindows(mPtr, windowHandles);
}
這個函數調用了本地方法nativeSetInputWindows來進一步執行操作,mPtr是native層NativeInputManager實例,在調用InputManagerService.nativeInit函數時會在native層構造NativeInputManager對象並將其保存在mPtr中。nativeSetInputWindows會調用NativeInputManager的setInputWindows函數
Step 6. NativeInputManager.setInputWindows
這個函數定義在frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray) {
Vector<sp<InputWindowHandle> > windowHandles;
if (windowHandleObjArray) {
jsize length = env->GetArrayLength(windowHandleObjArray);
for (jsize i = 0; i < length; i++) {
jobject windowHandleObj = env->GetObjectArrayElement(windowHandleObjArray, i);
if (! windowHandleObj) {
break; // found null element indicating end of used portion of the array
}
sp<InputWindowHandle> windowHandle =
android_server_InputWindowHandle_getHandle(env, windowHandleObj);
if (windowHandle != NULL) {
windowHandles.push(windowHandle);
}
env->DeleteLocalRef(windowHandleObj);
}
}
mInputManager->getDispatcher()->setInputWindows(windowHandles);
// Do this after the dispatcher has updated the window handle state.
bool newPointerGesturesEnabled = true;
size_t numWindows = windowHandles.size();
for (size_t i = 0; i < numWindows; i++) {
const sp<InputWindowHandle>& windowHandle = windowHandles.itemAt(i);
const InputWindowInfo* windowInfo = windowHandle->getInfo();
if (windowInfo && windowInfo->hasFocus && (windowInfo->inputFeatures
& InputWindowInfo::INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES)) {
newPointerGesturesEnabled = false;
}
}
}
這個函數首先將Java層的InputWindowHandle轉換成C++層的NativeInputWindowHandle,然後放在windowHandles向量中,最後將這些輸入窗口設置到InputDispatcher中去。
Step 7. InputDispatcher.setInputWindows
這個函數定義在frameworks/native/services/inputflinger/InputDispatcher.cpp 文件中:
void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) {
{ // acquire lock
AutoMutex _l(mLock);
Vector<sp<InputWindowHandle> > oldWindowHandles = mWindowHandles;
mWindowHandles = inputWindowHandles;
sp<InputWindowHandle> newFocusedWindowHandle;
bool foundHoveredWindow = false;
for (size_t i = 0; i < mWindowHandles.size(); i++) {
const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
if (!windowHandle->updateInfo() || windowHandle->getInputChannel() == NULL) {
mWindowHandles.removeAt(i--);
continue;
}
if (windowHandle->getInfo()->hasFocus) {
newFocusedWindowHandle = windowHandle;
}
}
if (mFocusedWindowHandle != newFocusedWindowHandle) {
mFocusedWindowHandle = newFocusedWindowHandle;
}
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
mLooper->wake();
}
這裏InputDispatcher的成員變量mFocusedWindowHandle 就代表當前激活的窗口的。這個函數遍歷inputWindowHandles,獲取獲得焦點的窗口,並賦值給mFocusedWindowHandle 這樣,InputManagerService就把當前激活的窗口保存在InputDispatcher中了,後面就可以把鍵盤消息分發給它來處理。
Step 8. 在前面的ViewRootImpl的setView中,下一步是server端註冊接收通道
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
try {
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (Exception e) {
...
}
這裏會調用到WindowManagerService的addWindow接口
Step 9. WindowManagerService.addWindow
該函數定義在frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
if (outInputChannel != null && (attrs.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
String name = win.makeInputChannelName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
win.setInputChannel(inputChannels[0]);
inputChannels[1].transferTo(outInputChannel);
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
}
}
這裏的outInputChannel即爲前面創建的InputChannel,它不爲NULL,因此,這裏會通過InputChannel.openInputChannelPair函數來創建一對輸入通道,其中一個位於WindowManagerService中,另外一個通過outInputChannel參數返回到應用程序中。WindowManagerService會爲每個窗口創建一個WindowState對象,然後將該InputChannel對的service端保存到WindowState中
Step 10. 下面看下InputChannel.openInputChannelPair的實現
函數定義在frameworks/base/core/java/android/view/InputChannel.java
public static InputChannel[] openInputChannelPair(String name) {
if (name == null) {
throw new IllegalArgumentException("name must not be null");
}
if (DEBUG) {
Slog.d(TAG, "Opening input channel pair '" + name + "'");
}
return nativeOpenInputChannelPair(name);
}
調用了nativeOpenInputChannelPair函數,在native創建一個InputChannel對
Step 11. InputChannel.nativeOpenInputChannelPair
函數定義在frameworks/base/core/jni/android_view_InputChannel.cpp
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
jclass clazz, jstring nameObj) {
const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
String8 name(nameChars);
env->ReleaseStringUTFChars(nameObj, nameChars);
sp<InputChannel> serverChannel;
sp<InputChannel> clientChannel;
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
...
return channelPair;
}
nativeOpenInputChannelPair函數調用InputChannel的openInputChannelPair函數創建一對InputChannel,該對象是Native層的InputChannel,跟java層是一一對應的。
Step 12. InputChannel.openInputChannelPair
該函數定義在frameworks/native/libs/input/InputTransport.cpp中
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;
ALOGE("channel '%s' ~ Could not create socket pair. errno=%d",
name.string(), errno);
outServerChannel.clear();
outClientChannel.clear();
return result;
}
int bufferSize = SOCKET_BUFFER_SIZE;
//設置server端和client端的接收緩衝區和發送緩衝區
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;
}
這裏調用了socketpair系統調用創建了一對已經連接的UNIX租socket,這裏可以把這一對socket當成pipe返回的文件描述符一樣使用,pipe返回的管道是單向管道,即只能從一端寫入,一端讀出,但是socketpair是創建的管道是全雙工的,可讀可寫。
創建好了server端和client端通道後,在WindowManagerService.addWindow函數中,一方面它把剛纔創建的Client端的輸入通道通過outInputChannel參數返回到應用程序中:
inputChannels[1].transferTo(outInputChannel);
另外還需要把server端的InputChannel註冊到InputManagerService中:
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
Step 13. InputManagerService.registerInputChannel
函數定義在frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public void registerInputChannel(InputChannel inputChannel,
InputWindowHandle inputWindowHandle) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
}
nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
}
通過調用nativeRegisterInputChannel來將InputChannel註冊到native層
Step 14. InputManagerService.nativeRegisterInputChannel
這個函數定義在frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
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);
sp<InputWindowHandle> inputWindowHandle =
android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);
status_t status = im->registerInputChannel(
env, inputChannel, inputWindowHandle, monitor);
}
根據java層的InputWindowHandle獲得native層的InputWindowHandle對象,根據java層的InputChannel獲得native層的InputChannel對象,然後調用NativeInputManager的resgiterInputChannel,該函數又調用了InputDispatcher的registerInputChannel
Step 15. InputDispatcher.registerInputChannel
該函數定義在frameworks/native/services/inputflinger/InputDispatcher.cpp
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
{ // acquire lock
AutoMutex _l(mLock);
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;
}
將InputWindowHandle, InputChanel封裝成Connection對象,然後fd作爲key,Connection作爲Value,保存在mConnectionsByFd中,如果傳入的monitor是true,則需要將InputChannel放到mMonitoringChannels中,從上面的InputManagerService的registerInputChannel函數裏傳入的monitor是false,所以這裏不加入到mMonitoringChannels。同時把fd加入到mLooper的監聽中,並指定當該fd有內容可讀時,Looper就會調用handleReceiveCallback函數。至此server端的InputChannel註冊完成,InputDispatcher睡眠在監聽的fds上,當有按鍵事件發生時,InputDispatcher就會往這些fd寫入InputMessage對象,進而回調handleReceiveCallback函數。//TODO這裏當客戶端的進程讀取完事件後會往InputChannel的client端寫入????,而server端可以收到,這時InputDispatcher就會被喚醒,然後調用handleReceiveCallback來表明按鍵接收結束。
至此,server端的InputChannel就註冊完成了,再回到前面的WindowManagerService.addWindow上的第二步inputChannels[1].transferTo(outInputChannel);,這個是將創建的一對InputChannel的client端複製到傳入的參數InputChannel上,當addWindow返回時,就回到ViewRootImpl.setView函數中,執行應用程序這一側的鍵盤消息接收通道。
if (mInputChannel != null) {
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
WindowInputEventReceiver繼承自InputEventReceiver類。
Step 16. InputEventReceiver.init
構造函數定義在frameworks/base/core/java/android/view/InputEventReceiver.java
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);
mCloseGuard.open("dispose");
}
調用了nativeInit執行native層的初始化
Step 17. InputEventReceiver.nativeInit
定義在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對象,並調用其initialize函數
Step 18. NativeInputEventReceiver.initialize
該函數定義在frameworks/base/core/jni/android_view_InputEventReceiver.cpp
status_t NativeInputEventReceiver::initialize() {
setFdEvents(ALOOPER_EVENT_INPUT);
return OK;
}
調用setFdEvents函數
Step 19. NativeInputEventReceiver.setFdEvents
該函數定義在frameworks/base/core/jni/android_view_InputEventReceiver.cpp
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);
}
}
}
這裏調用傳入的MessageQueue獲取Looper對象,如果events是0,則表示要移除監聽fd,如果events不爲0,表示要監聽fd,這個fd是前面WindowManagerService創建的一對InputChannel的client端,這樣當Server端寫入事件時,client端的looper就能被喚醒,並調用handleEvent函數(Looper::addFd函數可以指定LooperCallback對象,當fd可讀時,會調用LooperCallback的handleEvent,而NativeInputEventReceiver繼承自LooperCallback,所以這裏會調用NativeInputEventReceiver的handleEvent函數)
Step 20. NativeInputEventReceiver.handleEvent
該函數定義在frameworks/base/core/jni/android_view_InputEventReceiver.cpp
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;
}
}
該函數調用consumeEvents函數來處理接收一個按鍵事件
Step 21. NativeInputEventReceiver.consumeEvents
該函數定義在frameworks/base/core/jni/android_view_InputEventReceiver.cpp
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%lld.",
getInputChannelName(), consumeBatches ? "true" : "false", (long long)frameTime);
}
if (consumeBatches) {
mBatchedInputEventPending = false;
}
if (outConsumedBatch) {
*outConsumedBatch = false;
}
ScopedLocalRef<jobject> receiverObj(env, NULL);
bool skipCallbacks = false;
for (;;) {
uint32_t seq;
InputEvent* inputEvent;
status_t status = mInputConsumer.consume(&mInputEventFactory,
consumeBatches, frameTime, &seq, &inputEvent);
if (!skipCallbacks) {
jobject inputEventObj;
switch (inputEvent->getType()) {
case AINPUT_EVENT_TYPE_KEY:
inputEventObj = android_view_KeyEvent_fromNative(env,
static_cast<KeyEvent*>(inputEvent));
break;
}
if (inputEventObj) {
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
env->DeleteLocalRef(inputEventObj);
}
}
if (skipCallbacks) {
mInputConsumer.sendFinishedSignal(seq, false);
}
}
}
函數首先調用mInputConsumer.consume接收一個InputEvent對象,mInputConsumer在NativeInputEventReceiver構造函數中初始化
Step 22. InputConsumer.consume
該函數定義在frameworks/native/libs/input/InputTransport.cpp
status_t InputConsumer::consume(InputEventFactoryInterface* factory,
bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
*outSeq = 0;
*outEvent = NULL;
// Fetch the next input message.
// Loop until an event can be returned or no additional events are received.
while (!*outEvent) {
if (mMsgDeferred) {
// mMsg contains a valid input message from the previous call to consume
// that has not yet been processed.
mMsgDeferred = false;
} else {
// Receive a fresh message.
status_t result = mChannel->receiveMessage(&mMsg);
if (result) {
// Consume the next batched event unless batches are being held for later.
if (consumeBatches || result != WOULD_BLOCK) {
result = consumeBatch(factory, frameTime, outSeq, outEvent);
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;
}
}
return OK;
}
函數首先調用InputChannel的receiveMessage函數接收InputMessage對象,然後根據InputMessage對象調用initializeKeyEvent來構造KeyEvent對象。拿到可KeyEvent對象後,再對到consumeEvents中調用java層的InputEventReceiver.java的dispatchInputEvent函數
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
Step 23. InputEventReceiver.dispatchInputEvent
該函數定義在frameworks/base/core/java/android/view/InputEventReceiver.java
// Called from native code.
@SuppressWarnings("unused")
private void dispatchInputEvent(int seq, InputEvent event) {
mSeqMap.put(event.getSequenceNumber(), seq);
onInputEvent(event);
}
進而調用onInputEvent函數。至此按鍵就開始了java層的分發。