概述
????????上一個章節,我們講解了App如何使用InputChannel通道與input系統服務建立通信的橋梁的過程,本章我們講述App如何從input系統服務中獲取上報的輸入事件,也就是我們本章講述的InputEventReceiver。
本文涉及的源碼路徑
frameworks/base/core/java/android/view/InputChannel.java
frameworks/base/core/java/android/view/ViewRootImpl.java
frameworks/base/core/jni/android_view_InputChannel.cpp
frameworks/base/core/jni/android_view_InputEventReceiver.cpp
frameworks/base/core/jni/com_android_server_input_InputManagerService.cpp
frameworks/base/services/core/java/com/android/server/wm/Session.java
frameworks/base/core/java/android/view/WindowState.java
WindowInputEventReceiver事件接收器
上一章節,我們講述了當App初始化完成InputChannel后,會創建一個事件接收器,如下所示:
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper());
其中 WindowInputEventReceiver是InputEventReceiver的子類,實現如下所示:
final class WindowInputEventReceiver extends InputEventReceiver {public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {super(inputChannel, looper);}@Overridepublic void onInputEvent(InputEvent event) {enqueueInputEvent(event, this, 0, true);}@Overridepublic void onBatchedInputEventPending() {if (mUnbufferedInputDispatch) {super.onBatchedInputEventPending();} else {scheduleConsumeBatchedInput();}}@Overridepublic void dispose() {unscheduleConsumeBatchedInput();super.dispose();}}
從中我們可以知道,核心實現是InputEventReceiver,因此接下來我們重點講述InputEventReceiver的實現。
InputEventReceiver 輸入事件接收器
InputEventReceiver的類,我們主要講述核心方法的實現,如下所示:
public abstract class InputEventReceiver {......private long mReceiverPtr;private InputChannel mInputChannel;private MessageQueue mMessageQueue;// Map from InputEvent sequence numbers to dispatcher sequence numbers.private final SparseIntArray mSeqMap = new SparseIntArray();private static native long nativeInit(WeakReference<InputEventReceiver> receiver,InputChannel inputChannel, MessageQueue messageQueue);private static native void nativeDispose(long receiverPtr);private static native void nativeFinishInputEvent(long receiverPtr, int seq, boolean handled);private static native boolean nativeConsumeBatchedInputEvents(long receiverPtr,long frameTimeNanos);public InputEventReceiver(InputChannel inputChannel, Looper looper) {......mInputChannel = inputChannel;mMessageQueue = looper.getQueue();mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),inputChannel, mMessageQueue);}public void onInputEvent(InputEvent event) {finishInputEvent(event, false);}public final void finishInputEvent(InputEvent event, boolean handled) {if (event == null) {throw new IllegalArgumentException("event must not be null");}if (mReceiverPtr == 0) {Log.w(TAG, "Attempted to finish an input event but the input event "+ "receiver has already been disposed.");} else {int index = mSeqMap.indexOfKey(event.getSequenceNumber());if (index < 0) {Log.w(TAG, "Attempted to finish an input event that is not in progress.");} else {int seq = mSeqMap.valueAt(index);mSeqMap.removeAt(index);nativeFinishInputEvent(mReceiverPtr, seq, handled);}}event.recycleIfNeededAfterDispatch();}// Called from native code.@SuppressWarnings("unused")private void dispatchInputEvent(int seq, InputEvent event) {mSeqMap.put(event.getSequenceNumber(), seq);onInputEvent(event);}// Called from native code.@SuppressWarnings("unused")private void dispatchBatchedInputEventPending() {onBatchedInputEventPending();}public static interface Factory {public InputEventReceiver createInputEventReceiver(InputChannel inputChannel, Looper looper);}
}
InputEventReceiver的初始化
InputEventReceiver的構造方法實現,如下:
public InputEventReceiver(InputChannel inputChannel, Looper looper) {......mInputChannel = inputChannel;mMessageQueue = looper.getQueue();mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),inputChannel, mMessageQueue);}
該方法的初始化使用了App自己的inputChannel通道和App的Looper消息隊列,然后調用JNI方法完成最終的初始化,如下所示:
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,jobject inputChannelObj, jobject messageQueueObj) {sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,inputChannelObj);if (inputChannel == NULL) {jniThrowRuntimeException(env, "InputChannel is not initialized.");return 0;}sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);if (messageQueue == NULL) {jniThrowRuntimeException(env, "MessageQueue is not initialized.");return 0;}sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,receiverWeak, inputChannel, messageQueue);status_t status = receiver->initialize();if (status) {String8 message;message.appendFormat("Failed to initialize input event receiver. status=%d", status);jniThrowRuntimeException(env, message.string());return 0;}receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the objectreturn reinterpret_cast<jlong>(receiver.get());
}
nativeInit方法主要完成如下的邏輯:
1、找到java層的InputChannel對象對應的Native層的C++對象inputChannel;
2、找到java層的messageQueue對象對應的Native層的C++對象messageQueue;
3、調用NativeInputEventReceiver的initialize完成初始化;
我們繼續講解NativeInputEventReceiver的實現,如下所示:
class NativeInputEventReceiver : public LooperCallback {
public:NativeInputEventReceiver(JNIEnv* env,jobject receiverWeak, const sp<InputChannel>& inputChannel,const sp<MessageQueue>& messageQueue);status_t initialize();......virtual int handleEvent(int receiveFd, int events, void* data);
};
NativeInputEventReceiver繼承LooperCallback, 并重載實現了handleEvent。我們這里重點提及到LooperCallback,是因為App獲取Input輸入事件是通過監控inputChannel通道,并通過LooperCallback實現輸入事件的獲取,監控代碼如下所示:
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);}}
}
該接口的實現,我加入了對ALOOPER_EVENT_INPUT事件類型的監控,InputDispatcher上報的輸入事件會觸發ALOOPER_EVENT_INPUT事件,并最終調用到NativeInputEventReceiver中的handleEvent方法。關于Looper的實現,我們這里不詳細展開,感興趣的同學可以,自行去閱讀AOSP源碼。
App Input輸入事件的獲取
當InputDispatcher上報事件時,將輸入事件寫入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;
}
我們只保留了InputDispatcher上報事件時的觸發回調代碼,然后繼續調用consumeEvents接口,如下所示:
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {......for (;;) {uint32_t seq;InputEvent* inputEvent;status_t status = mInputConsumer.consume(&mInputEventFactory,consumeBatches, frameTime, &seq, &inputEvent);......if (!skipCallbacks) {......switch (inputEvent->getType()) {case AINPUT_EVENT_TYPE_KEY:if (kDebugDispatchCycle) {ALOGD("channel '%s' ~ Received key event.", getInputChannelName());}inputEventObj = android_view_KeyEvent_fromNative(env,static_cast<KeyEvent*>(inputEvent));break;case AINPUT_EVENT_TYPE_MOTION: {if (kDebugDispatchCycle) {ALOGD("channel '%s' ~ Received motion event.", getInputChannelName());}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:assert(false); // InputConsumer should prevent this from ever happeninginputEventObj = NULL;}if (inputEventObj) {env->CallVoidMethod(receiverObj.get(),gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);} else {ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName());skipCallbacks = true;}}......}
}
consumeEvents實現比較復雜,涉及到的邏輯比較多,為了簡化描述,我們只保留首次上報輸入事件的處理,主要實現邏輯如下所示:
1、mInputConsumer.consume(&mInputEventFactory, consumeBatches, frameTime, &seq, &inputEvent);獲取事件;
2、根據不同的事件類型,封裝為一個inputEventObj對象;
3、調用InputEventReceiver的java方法env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);正式開始向App上報輸入事件;
調用InputEventReceiver的dispatchInputEvent如下所示:
// Called from native code.@SuppressWarnings("unused")private void dispatchInputEvent(int seq, InputEvent event) {mSeqMap.put(event.getSequenceNumber(), seq);onInputEvent(event);}
最終調用到了 WindowInputEventReceiver子類的onInputEvent方法,如下所示:
final class WindowInputEventReceiver extends InputEventReceiver {public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {super(inputChannel, looper);}@Overridepublic void onInputEvent(InputEvent event) {enqueueInputEvent(event, this, 0, true);}......}
繼續調用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;Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,mPendingInputEventCount);if (processImmediately) {doProcessInputEvents();} else {scheduleProcessInputEvents();}}
1、將上報的輸入事件加入到處理隊列中;
2、調用doProcessInputEvents立即開始App對輸入事件的處理,我們在后續文章進行講解。這里不再敘述。
總結
本文講述了App通過注冊輸入事件接收器的方式,當InputDispacther上報輸入事件時,通過Looper的回調機制App獲取到了事件,并通過doProcessInputEvents接口,正式開啟App對input事件的處理。