android R -- App收到事件回调处理流程

474 阅读4分钟

之前分析了inputChannel注册流程和 inputDispatcher分发事件的流程,这篇看下事件从inputDispatcher分发到app的流程。上一篇inputDispatcher分发juejin.cn/post/712600… 的最后会通过socket写入数据,那么socket的另一端会被回调并读取数据,关于inputChannel的注册建立,可以参考juejin.cn/post/712122… ,最后部分可以看到app端的inputChannel被注册,并在有数据的时候回调handleEvent()

先上流程图:

image.png

frameworks/base/core/jni/android_view_InputEventReceiver.cpp

int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    // Allowed return values of this function as documented in LooperCallback::handleEvent
    constexpr int REMOVE_CALLBACK = 0;
    constexpr int KEEP_CALLBACK = 1;
    ......
    if (events & ALOOPER_EVENT_INPUT) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        // 1.1 consumeEvents
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);
        mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
        return status == OK || status == NO_MEMORY ? KEEP_CALLBACK : REMOVE_CALLBACK;
    }
    ......
    return KEEP_CALLBACK;
}

1.1 consumeEvents

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
    if (kDebugDispatchCycle) {
        ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%" PRId64,
              getInputChannelName().c_str(), toString(consumeBatches), frameTime);
    }

    if (consumeBatches) {
        mBatchedInputEventPending = false;
    }
    if (outConsumedBatch) {
        *outConsumedBatch = false;
    }

    ScopedLocalRef<jobject> receiverObj(env, nullptr);
    bool skipCallbacks = false;
    for (;;) {
        uint32_t seq;
        InputEvent* inputEvent;
        // 1.2 mInputConsumer.consume 读取事件 赋值到inputEvent
        status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, frameTime, &seq, &inputEvent);
        ......

        if (!skipCallbacks) {
            if (!receiverObj.get()) {
                // mReceiverWeakGlobal指向的是java层传入的WindowInputEventReceiver,参考https://juejin.cn/post/7121222990636777509 里面的app端inputChannel注册过程
                receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
                
            }

            jobject inputEventObj;
            switch (inputEvent->getType()) {
            case AINPUT_EVENT_TYPE_KEY:
                // 2.1 创建java层的事件对象
                inputEventObj = android_view_KeyEvent_fromNative(env,
                        static_cast<KeyEvent*>(inputEvent));
                break;

            ......
            }

            if (inputEventObj) {
                // 2.2 调用java层的方法dispatchInputEvent
                env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
                
                env->DeleteLocalRef(inputEventObj);
            } else {
                skipCallbacks = true;
            }
        }

        if (skipCallbacks) {
            mInputConsumer.sendFinishedSignal(seq, false);
        }
    }
}

读取事件并保存到inputEvent,然后创建java层的android/view/KeyEvent对象,调用java层的android/view/InputEventReceiver的dispatchInputEvent方法

/native/libs/input/InputTransport.cpp

1.2 mInputConsumer.consume 读取事件

status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches,
                                nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {

    *outSeq = 0;
    *outEvent = nullptr;

    // 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.
            // 读取事件 保存到mMsg对象
            status_t result = mChannel->receiveMessage(&mMsg);
            ......
        }

        switch (mMsg.header.type) {
            case InputMessage::Type::KEY: {
                KeyEvent* keyEvent = factory->createKeyEvent();
                if (!keyEvent) return NO_MEMORY;
                // 初始化KeyEvent对象
                // 1.3 initializeKeyEvent
                initializeKeyEvent(keyEvent, &mMsg);
                *outSeq = mMsg.header.seq;
                *outEvent = keyEvent;
                
            break;
            }
            ......
        }
    }
    return OK;
}

/native/libs/input/InputTransport.cpp

1.3 initializeKeyEvent

void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) {
    event->initialize(msg->body.key.eventId, msg->body.key.deviceId, msg->body.key.source,
                      msg->body.key.displayId, msg->body.key.hmac, msg->body.key.action,
                      msg->body.key.flags, msg->body.key.keyCode, msg->body.key.scanCode,
                      msg->body.key.metaState, msg->body.key.repeatCount, msg->body.key.downTime,
                      msg->body.key.eventTime);
}

2.1 创建java层的事件对象

base/core/jni/android_view_KeyEvent.cpp

jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent* event) {
    ScopedLocalRef<jbyteArray> hmac = toJbyteArray(env, event->getHmac());
    jobject eventObj =
            env->CallStaticObjectMethod(gKeyEventClassInfo.clazz, gKeyEventClassInfo.obtain,
                                        event->getId(),
                                        nanoseconds_to_milliseconds(event->getDownTime()),
                                        nanoseconds_to_milliseconds(event->getEventTime()),
                                        event->getAction(), event->getKeyCode(),
                                        event->getRepeatCount(), event->getMetaState(),
                                        event->getDeviceId(), event->getScanCode(),
                                        event->getFlags(), event->getSource(),
                                        event->getDisplayId(), hmac.get(), nullptr);
    
    return eventObj;
}

gKeyEventClassInfo是jni注册的结构体,里面保存了java层的类和方法。 注册的信息可以参考

int register_android_view_KeyEvent(JNIEnv* env) {
    jclass clazz = FindClassOrDie(env, "android/view/KeyEvent");
    gKeyEventClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);

    gKeyEventClassInfo.obtain =
            GetStaticMethodIDOrDie(env, gKeyEventClassInfo.clazz, "obtain",
                                   "(IJJIIIIIIIII[BLjava/lang/String;)Landroid/view/KeyEvent;");
    gKeyEventClassInfo.recycle = GetMethodIDOrDie(env, gKeyEventClassInfo.clazz,
            "recycle", "()V");

    gKeyEventClassInfo.mId = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mId", "I");
    gKeyEventClassInfo.mDeviceId = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mDeviceId", "I");
    gKeyEventClassInfo.mSource = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mSource", "I");
    gKeyEventClassInfo.mDisplayId = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mDisplayId",
                                                    "I");
    gKeyEventClassInfo.mHmac = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mHmac", "[B");
    gKeyEventClassInfo.mMetaState = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mMetaState",
                                                    "I");
    gKeyEventClassInfo.mAction = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mAction", "I");
    gKeyEventClassInfo.mKeyCode = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mKeyCode", "I");
    gKeyEventClassInfo.mScanCode = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mScanCode", "I");
    gKeyEventClassInfo.mRepeatCount = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mRepeatCount",
                                                      "I");
    gKeyEventClassInfo.mFlags = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mFlags", "I");
    gKeyEventClassInfo.mDownTime = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mDownTime", "J");
    gKeyEventClassInfo.mEventTime = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mEventTime",
                                                    "J");
    gKeyEventClassInfo.mCharacters = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mCharacters",
                                                     "Ljava/lang/String;");

    return RegisterMethodsOrDie(env, "android/view/KeyEvent", g_methods, NELEM(g_methods));
}

2.2 调用java层的方法dispatchInputEvent

调用gInputEventReceiverClassInfo.dispatchInputEvent java层的android/view/InputEventReceiver类的dispatchInputEvent方法。

gInputEventReceiverClassInfo是jni中注册的与java层对象的映射关系 base/core/jni/android_view_InputEventReceiver.cpp

int register_android_view_InputEventReceiver(JNIEnv* env) {
    int res = RegisterMethodsOrDie(env, "android/view/InputEventReceiver",
            gMethods, NELEM(gMethods));

    jclass clazz = FindClassOrDie(env, "android/view/InputEventReceiver");
    gInputEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);

    gInputEventReceiverClassInfo.dispatchInputEvent = GetMethodIDOrDie(env,
            gInputEventReceiverClassInfo.clazz,
            "dispatchInputEvent", "(ILandroid/view/InputEvent;)V");
    gInputEventReceiverClassInfo.onFocusEvent =
            GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onFocusEvent", "(ZZ)V");
    gInputEventReceiverClassInfo.onPointerCaptureEvent =
            GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onPointerCaptureEvent",
                             "(Z)V");
    gInputEventReceiverClassInfo.onDragEvent =
            GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onDragEvent", "(ZFF)V");
    gInputEventReceiverClassInfo.onBatchedInputEventPending =
            GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onBatchedInputEventPending",
                             "(I)V");

    return res;
}

下面分析一下java层的代码

/base/core/java/android/view/InputEventReceiver.java

    // Called from native code.
    private void dispatchInputEvent(int seq, InputEvent event) {
        mSeqMap.put(event.getSequenceNumber(), seq);
        // 3.1 回调java层
        onInputEvent(event);
    }

onInputEvent方法是子类实现的,这边的子类是ViewRootImpl中的WindowInputEventReceiver对象,看下WindowInputEventReceiver的onInputEvents()

3.1 回调java层

android.view.ViewRootImpl.WindowInputEventReceiver

public void onInputEvent(InputEvent event) {
          
            List<InputEvent> processedEvents;
            try {
                processedEvents =
                    mInputCompatProcessor.processInputEventForCompatibility(event);
            } 
            if (processedEvents != null) {
                if (processedEvents.isEmpty()) {
                    // InputEvent consumed by mInputCompatProcessor
                    finishInputEvent(event, true);
                } else {
                    for (int i = 0; i < processedEvents.size(); i++) {
                        enqueueInputEvent(
                                processedEvents.get(i), this,
                                QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);
                    }
                }
            } else {
                enqueueInputEvent(event, this, 0, true);
            }
        }

都要调用到enqueueInputEvent()方法

3.2 enqueueInputEvent()

void enqueueInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags, boolean processImmediately) {
        QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);

       
        // Always enqueue the input event in order, regardless of its time stamp.
        // We do this because the application or the IME may inject key events
        // in response to touch events and we want to ensure that the injected keys
        // are processed in the order they were received and we cannot trust that
        // the time stamp of injected events are monotonic.
        QueuedInputEvent last = mPendingInputEventTail;
        if (last == null) {
            mPendingInputEventHead = q;
            mPendingInputEventTail = q;
        } else {
            last.mNext = q;
            mPendingInputEventTail = q;
        }
        mPendingInputEventCount += 1;
        // processImmediately == true
        if (processImmediately) {
            // 3.3  doProcessInputEvents将事件插入pending队列
            doProcessInputEvents();
        } 
    }

3.3 doProcessInputEvents

    void doProcessInputEvents() {
        // Deliver all pending input events in the queue.
        while (mPendingInputEventHead != null) {
            QueuedInputEvent q = mPendingInputEventHead;
            mPendingInputEventHead = q.mNext;
            if (mPendingInputEventHead == null) {
                mPendingInputEventTail = null;
            }
            q.mNext = null;

            mPendingInputEventCount -= 1;
            mViewFrameInfo.setInputEvent(mInputEventAssigner.processEvent(q.mEvent));
            // 3.4 处理事件
            deliverInputEvent(q);
        }

        // We are done processing all input events that we can process right now
        // so we can clear the pending flag immediately.
        if (mProcessInputEventsScheduled) {
            mProcessInputEventsScheduled = false;
            mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
        }
    }

3.4 处理事件

    private void deliverInputEvent(QueuedInputEvent q) {
        
        try {
            // 责任链stage,处理事件
            InputStage stage;
            if (q.shouldSendToSynthesizer()) {
                stage = mSyntheticInputStage;
            } else {
                stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
            }
            .....

            if (stage != null) {
                handleWindowFocusChanged();
                stage.deliver(q);
            } else {
                finishInputEvent(q);
            }
        }
    }

InputStage链是ViewRootImpl的setView方法中初始化的,参考juejin.cn/post/712122… 中setView的方法。到这里事件会经过stage一个一个的处理。最后还会调用finishInputEvent(q); 通知inputDispatcher处理后面的事件。下次再分析