Android 从问题出发探索KeyEvent分发流程(2) Activity是如何收到事件的

# Android 从问题出发探索KeyEvent分发流程(1) PhoneWindowManager是如何收到事件的

现在回到这篇记录的Step 4开始往下走,探索下事件是怎么跑到Activity里面的

所与标出的类路径的根目录都是frameworks目录

Step 1 InputDispatcher调用 enqueueInboundEventLocked 将封装的KeyEntry 加入队列,同时唤醒Looper

/native/services/inputflinger/dispatcher/InputDispatcher.cpp

cpp 复制代码
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
        std::unique_ptr<KeyEntry> newEntry =
                std::make_unique<KeyEntry>(args->id, args->eventTime, args->deviceId, args->source,
                                           args->displayId, policyFlags, args->action, flags,
                                           keyCode, args->scanCode, metaState, repeatCount,
                                           args->downTime);

        needWake = enqueueInboundEventLocked(std::move(newEntry));
    } // release lock

    if (needWake) {
        mLooper->wake();
    }
}
cpp 复制代码
bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newEntry) {
    bool needWake = mInboundQueue.empty();
    mInboundQueue.push_back(std::move(newEntry));
    EventEntry& entry = *(mInboundQueue.back());
    traceInboundQueueLengthLocked();
}

Step 2 唤醒后回调dispatchOnce , 这个任务在Dispatcher的start()函数有定义, 调用路径dispatchOnce -> dispatchOnceInnerLocked 后续根据事件类型调用不同的dispatch函数,这里使用dispatchKeyLocked路径继续分析

/native/services/inputflinger/dispatcher/InputDispatcher.cpp

cpp 复制代码
void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        std::scoped_lock _l(mLock);
        mDispatcherIsAlive.notify_all();

        // Run a dispatch loop if there are no pending commands.
        // The dispatch loop might enqueue commands to run afterwards.
        if (!haveCommandsLocked()) {
            dispatchOnceInnerLocked(&nextWakeupTime);
        }

    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    mLooper->pollOnce(timeoutMillis);
}

/native/services/inputflinger/dispatcher/InputDispatcher.cpp

cpp 复制代码
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {

    switch (mPendingEvent->type) {
        case EventEntry::Type::KEY: {
            done = dispatchKeyLocked(currentTime, keyEntry, &dropReason, nextWakeupTime);
            break;
        }

        case EventEntry::Type::MOTION: {
            done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);
            break;
        }

        case EventEntry::Type::SENSOR: {
            dispatchSensorLocked(currentTime, sensorEntry, &dropReason, nextWakeupTime);
            done = true;
            break;
        }
    }
}

Step 3 判断KeyEvent是否是可重复按键(也就是长按重复上报),然后调用dispatchEventLocked进行真正的转发

/native/services/inputflinger/dispatcher/InputDispatcher.cpp

cpp 复制代码
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
                                        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    // Preprocessing.
    if (!entry->dispatchInProgress) {
        if (entry->repeatCount == 0 && entry->action == AKEY_EVENT_ACTION_DOWN &&
            (entry->policyFlags & POLICY_FLAG_TRUSTED) &&
            (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) {
            if (mKeyRepeatState.lastKeyEntry &&
                mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode &&
                mKeyRepeatState.lastKeyEntry->deviceId == entry->deviceId) {
    
                entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
                resetKeyRepeatLocked();
                mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
            } else {
                // Not a repeat.  Save key down state in case we do see a repeat later.
                resetKeyRepeatLocked();
                mKeyRepeatState.nextRepeatTime = entry->eventTime + mConfig.keyRepeatTimeout;
                }
       mKeyRepeatState.lastKeyEntry = entry;
     

        } else if (entry->action == AKEY_EVENT_ACTION_UP && mKeyRepeatState.lastKeyEntry &&
                   mKeyRepeatState.lastKeyEntry->deviceId != entry->deviceId) {
            // The key on device 'deviceId' is still down, do not stop key repeat
            if (DEBUG_INBOUND_EVENT_DETAILS) {
                ALOGD("deviceId=%d got KEY_UP as stale", entry->deviceId);
            }
        } else if (!entry->syntheticRepeat) {
            resetKeyRepeatLocked();
        }

    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

Step 4 dispatchEventLocked -> prepareDispatchCycleLocked -> enqueueDispatchEntriesLocked 事件经过这个流程后被加入到待分发队列outboundQueue ,调用startDispatchCycleLocked开始转发

/native/services/inputflinger/dispatcher/InputDispatcher.cpp

cpp 复制代码
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
                                          std::shared_ptr<EventEntry> eventEntry,
                                          const std::vector<InputTarget>& inputTargets) {

    for (const InputTarget& inputTarget : inputTargets) {
        sp<Connection> connection =
                getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
        if (connection != nullptr) {
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
        } 
        }
    }
}

void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
                                                 const sp<Connection>& connection,
                                                 std::shared_ptr<EventEntry> eventEntry,
                                                 const InputTarget& inputTarget) {
            enqueueDispatchEntriesLocked(currentTime, connection, std::move(splitMotionEntry),
                                         inputTarget);
            return;
        }
    }

    // Not splitting.  Enqueue dispatch entries for the event as is.
    enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}

void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
                                                   const sp<Connection>& connection,
                                                   std::shared_ptr<EventEntry> eventEntry,
                                                   const InputTarget& inputTarget) {
    bool wasEmpty = connection->outboundQueue.empty();

    // Enqueue dispatch entries for the requested modes.
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
                               InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
                               InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
                               InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
                               InputTarget::FLAG_DISPATCH_AS_IS);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
                               InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
                               InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);

    // If the outbound queue was previously empty, start the dispatch cycle going.
    if (wasEmpty && !connection->outboundQueue.empty()) {
        startDispatchCycleLocked(currentTime, connection);
    }
}

待转发队列之前为空,新事件加入后不为空就开始调用startDispatchCycleLocked

Step 5 调用connection->inputPublisher.publishKeyEvent分发事件

cpp 复制代码
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
                                               const sp<Connection>& connection) {
    while (connection->status == Connection::Status::NORMAL && !connection->outboundQueue.empty()) {
        DispatchEntry* dispatchEntry = connection->outboundQueue.front();
        dispatchEntry->deliveryTime = currentTime;
        const std::chrono::nanoseconds timeout = getDispatchingTimeoutLocked(connection);
        dispatchEntry->timeoutTime = currentTime + timeout.count();

        // Publish the event.
        status_t status;
        const EventEntry& eventEntry = *(dispatchEntry->eventEntry);
        switch (eventEntry.type) {
            case EventEntry::Type::KEY: {
                const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
                std::array<uint8_t, 32> hmac = getSignature(keyEntry, *dispatchEntry);

                // Publish the key event.
                status = connection->inputPublisher
                                 .publishKeyEvent(dispatchEntry->seq,
                                                  dispatchEntry->resolvedEventId, keyEntry.deviceId,
                                                  keyEntry.source, keyEntry.displayId,
                                                  std::move(hmac), dispatchEntry->resolvedAction,
                                                  dispatchEntry->resolvedFlags, keyEntry.keyCode,
                                                  keyEntry.scanCode, keyEntry.metaState,
                                                  keyEntry.repeatCount, keyEntry.downTime,
                                                  keyEntry.eventTime);
                break;
            }
        }
    }
}

Connection 的构造函数可以看到inputPublisher 实际赋值传入的是InputChannelinputChannel 是一个android封装的socketpair ,我们只需要暂时记住这是一个进程间通信的方式,有点类似java中socket使用,分为客户端和服务端,InputDispatcher这一边的逻辑就相当于服务端。 但是我们找遍Connection也没找到InputChannel在哪里,直接搜索也没找到InputChannel.cpp位置,这时候我们猜测下这个类可能定义在别的类中,Connection是从引入的类访问他,所以可以看下Connection引入的类,Connection引入的类很少,很容易找到一个InputTransport,后续函数都在这个类中

Step 6 InputChannel调用sendMessage发送事件

/native/libs/input/InputTransport.cpp

cpp 复制代码
status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t eventId, int32_t deviceId,
                                         int32_t source, int32_t displayId,
                                         std::array<uint8_t, 32> hmac, int32_t action,
                                         int32_t flags, int32_t keyCode, int32_t scanCode,
                                         int32_t metaState, int32_t repeatCount, nsecs_t downTime,
                                         nsecs_t eventTime) {
    InputMessage msg;
    msg.header.type = InputMessage::Type::KEY;
    msg.header.seq = seq;
    msg.body.key.eventId = eventId;
    msg.body.key.deviceId = deviceId;
    msg.body.key.source = source;
    msg.body.key.displayId = displayId;
    msg.body.key.hmac = std::move(hmac);
    msg.body.key.action = action;
    msg.body.key.flags = flags;
    msg.body.key.keyCode = keyCode;
    msg.body.key.scanCode = scanCode;
    msg.body.key.metaState = metaState;
    msg.body.key.repeatCount = repeatCount;
    msg.body.key.downTime = downTime;
    msg.body.key.eventTime = eventTime;
    return mChannel->sendMessage(&msg);
}

/native/libs/input/InputTransport.cpp

cpp 复制代码
status_t InputChannel::sendMessage(const InputMessage* msg) {
    const size_t msgLength = msg->size();
    InputMessage cleanMsg;
    msg->getSanitizedCopy(&cleanMsg);
    ssize_t nWrite;
    do {
        nWrite = ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);

    return OK;
}

通过文件描述符向该文件写入数据

Step 7 反向寻找事件监听者

现在事件发送出去了,问题就在于在哪里里接收的,我们就要回头找是谁,先确定这个Fd(文件描述符是多少),回头找到InputDispatcher.cpp /native/services/inputflinger/dispatcher/InputDispatcher.cpp

cpp 复制代码
Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
    { // acquire lock
   
        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);
    } // release lock

    // Wake the looper because some connections have changed.
    mLooper->wake();
    return clientChannel;
}

这里显示在创建InputChannel时候,他在关心ALOOPER_EVENT_INPUT 这个描述符,也就是输入事件,虽然InputDispatcher有LooperEventCallback但是内部并没有转发逻辑,我们通过命令行搜索 grep -rwn 'ALOOPER_EVENT_INPUT' framework目录 ,发现有不少类使用过,找到一个/base/core/jni/android_view_InputEventReceiver.cpp,这个实际是我们要找到接收者, 他对应的java类是InputEventReceiver.java

/base/core/jni/android_view_InputEventReceiver.cpp

cpp 复制代码
status_t NativeInputEventReceiver::initialize() {
    setFdEvents(ALOOPER_EVENT_INPUT);
    return OK;
}

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

java 复制代码
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
    if (inputChannel == null) {
        throw new IllegalArgumentException("inputChannel must not be null");
    }
    if (looper == null) {
        throw new IllegalArgumentException("looper must not be null");
    }

    mInputChannel = inputChannel;
    mMessageQueue = looper.getQueue();
    mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
            inputChannel, mMessageQueue);

    mCloseGuard.open("InputEventReceiver.dispose");
}

他的jni类在初始化对象时候实际做的就是在looper中设置ALOOPER_EVENT_INPUT 事件监听,这样InputTransport中写入事件,这边就收到事件到来被唤醒去处理, 他的实现类很多,其中的WindowInputEventReceiver是我们要找的,他是ViewRootImpl的内部类

Step 8 WindowInputEventReceiver接收并继续处理事件

/base/core/java/android/view/ViewRootImpl.java

java 复制代码
final class WindowInputEventReceiver extends InputEventReceiver {
    @Override
    public void onInputEvent(InputEvent 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);
        }
    }
}

void enqueueInputEvent(InputEvent event,
        InputEventReceiver receiver, int flags, boolean processImmediately) {
    QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
    QueuedInputEvent last = mPendingInputEventTail;
    if (last == null) {
        mPendingInputEventHead = q;
        mPendingInputEventTail = q;
    } else {
        last.mNext = q;
        mPendingInputEventTail = q;
    }

    if (processImmediately) {
        doProcessInputEvents();
    } else {
        scheduleProcessInputEvents();
    }
}

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

上面一套线性调用,获取事件->加入队列->取出事件处理,一直到deliverInputEvent进行分发调用

Step 9 找到目标InputStage,继续传递事件

/base/core/java/android/view/ViewRootImpl.java

java 复制代码
private void deliverInputEvent(QueuedInputEvent q) {
        InputStage stage;
        if (q.shouldSendToSynthesizer()) {
            stage = mSyntheticInputStage;
        } else {
            stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
        }
        if (stage != null) {
            handleWindowFocusChanged();
            stage.deliver(q);
        } else {
            finishInputEvent(q);
        }
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

这里KeyEvent 最后是被ViewPostImeInputStage 处理,可以在setView 函数看到InputStage的创建过程和多个不同InputStage的包含关系

Step 10 ViewPostImeInputStage调用processKeyEvent将事件传递给DecorView

/base/core/java/android/view/ViewRootImpl.java

java 复制代码
private int processKeyEvent(QueuedInputEvent q) {
    final KeyEvent event = (KeyEvent)q.mEvent;

    if (mUnhandledKeyManager.preViewDispatch(event)) {
        if (ViewDebugManager.DEBUG_ENG) {
            Log.v(mTag, "App handle dispatchUnique event = " + event + ", mView = " + mView
                    + ", this = " + this);
        }

        return FINISH_HANDLED;
    }

    // Deliver the key to the view hierarchy.
    if (mView.dispatchKeyEvent(event)) {
        if (ViewDebugManager.DEBUG_ENG) {
            Log.v(mTag, "App handle key event: event = " + event + ", mView = " + mView
                    + ", this = " + this);
        }
        return FINISH_HANDLED;
    }
}

mView 是在setView 赋值,Activity 通过WindowManager 的addView间接调用这个函数,传入的就是View就是DecorView

Step 11 DecorView回调Window.Callback将事件传递到Activity

/base/core/java/com/android/internal/policy/DecorView.java

java 复制代码
public boolean dispatchKeyEvent(KeyEvent event) {
    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);
}

mWindow就是PhoneWindow,callback,Activity实现了这个接口,这个calback就是Activity在attch函数中创建PhoneWindow时候设置的

/base/core/java/android/app/Activity.java

java 复制代码
final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor voiceInteractor,
        Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
        IBinder shareableActivityToken) {
    attachBaseContext(context);

    mFragments.attachHost(null /*parent*/);

    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    mWindow.setWindowControllerCallback(mWindowControllerCallback);
    mWindow.setCallback(this);
    }

至此,Activty如何收到KeyEvent的过程就结束了

相关推荐
2501_915909065 分钟前
苹果上架App软件全流程指南:iOS 应用发布步骤、App Store 上架流程、uni-app 打包上传与审核技巧详解
android·ios·小程序·https·uni-app·iphone·webview
2501_915921437 分钟前
iOS 文件管理与能耗调试结合实战 如何查看缓存文件、优化电池消耗、分析App使用记录(uni-app开发与性能优化必备指南)
android·ios·缓存·小程序·uni-app·iphone·webview
2501_915918411 小时前
App 苹果 上架全流程解析 iOS 应用发布步骤、App Store 上架流程
android·ios·小程序·https·uni-app·iphone·webview
2501_916007471 小时前
苹果上架全流程详解,iOS 应用发布步骤、App Store 上架流程、uni-app 打包上传与审核要点完整指南
android·ios·小程序·https·uni-app·iphone·webview
PuddingSama2 小时前
Android 高级绘制技巧: BlendMode
android·前端·面试
2501_915921433 小时前
iOS App 性能监控与优化实战 如何监控CPU、GPU、内存、帧率、耗电情况并提升用户体验(uni-app iOS开发调试必备指南)
android·ios·小程序·uni-app·iphone·webview·ux
Digitally3 小时前
如何将视频从安卓手机传输到电脑?
android·智能手机·电脑
CV资深专家3 小时前
Android 相机框架的跨进程通信架构
android
前行的小黑炭3 小时前
Android :如何提升代码的扩展性,方便复制到其他项目不会粘合太多逻辑,增强你的实战经验。
android·java·kotlin
2501_915921434 小时前
前端开发工具有哪些?常用前端开发工具、前端调试工具、前端构建工具与效率提升工具对比与最佳实践
android·前端·ios·小程序·uni-app·iphone·webview