Event分发源码分析(五)

4,分发Event

4.1 System分发

流程长而杂,仅分析关键点,

dispatchOnceInnerLocked方法首先从mInboundQueue中取出KeyEntry,然后根据不同类型的KeyEntry分别进行处理。

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
•••
mPendingEvent = mInboundQueue.dequeueAtHead();
•••
switch (mPendingEvent->type) {
    case EventEntry::TYPE_CONFIGURATION_CHANGED: {
        ConfigurationChangedEntry* typedEntry =
                static_cast<ConfigurationChangedEntry*>(mPendingEvent);
        done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
        dropReason = DROP_REASON_NOT_DROPPED; // configuration changes are never dropped
        break;
    }

    case EventEntry::TYPE_DEVICE_RESET: {
        DeviceResetEntry* typedEntry =
                static_cast<DeviceResetEntry*>(mPendingEvent);
        done = dispatchDeviceResetLocked(currentTime, typedEntry);
        dropReason = DROP_REASON_NOT_DROPPED; // device resets are never dropped
        break;
    }

    case EventEntry::TYPE_KEY: {
        KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
        if (isAppSwitchDue) {
            if (isAppSwitchKeyEventLocked(typedEntry)) {
                resetPendingAppSwitchLocked(true);
                isAppSwitchDue = false;
            } else if (dropReason == DROP_REASON_NOT_DROPPED) {
                dropReason = DROP_REASON_APP_SWITCH;
            }
        }
        if (dropReason == DROP_REASON_NOT_DROPPED
                && isStaleEventLocked(currentTime, typedEntry)) {
            dropReason = DROP_REASON_STALE;
        }
        if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
            dropReason = DROP_REASON_BLOCKED;
        }
        done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
        break;
    }

    case EventEntry::TYPE_MOTION: {
        MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
        if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
            dropReason = DROP_REASON_APP_SWITCH;
        }
        if (dropReason == DROP_REASON_NOT_DROPPED
                && isStaleEventLocked(currentTime, typedEntry)) {
            dropReason = DROP_REASON_STALE;
        }
        if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
            dropReason = DROP_REASON_BLOCKED;
        }
        done = dispatchMotionLocked(currentTime, typedEntry,
                &dropReason, nextWakeupTime);
        break;
}
•••
}
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection) {
•••
switch (eventEntry->type) {
        case EventEntry::TYPE_KEY: {
            KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);

            // Publish the key event.
            status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
                    keyEntry->deviceId, keyEntry->source,
                    dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
                    keyEntry->keyCode, keyEntry->scanCode,
                    keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
                    keyEntry->eventTime);
            break;
        }
case EventEntry::TYPE_MOTION: {
 •••
status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,
                    motionEntry->deviceId, motionEntry->source,
                    dispatchEntry->resolvedAction, motionEntry->actionButton,
                    dispatchEntry->resolvedFlags, motionEntry->edgeFlags,
                    motionEntry->metaState, motionEntry->buttonState,
                    xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision,
                    motionEntry->downTime, motionEntry->eventTime,
                    motionEntry->pointerCount, motionEntry->pointerProperties,
                    usingCoords);
            break;
•••
}

直接看最后的sendMessage方法,

status_t InputChannel::sendMessage(const InputMessage* msg) {
    size_t msgLength = msg->size();
    ssize_t nWrite;
    do {
        nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
} while (nWrite == -1 && errno == EINTR);
•••
}

sendMessage方法调用系统的send()方法来发送输入消息,这样输入消息就通过socket从SystemServer进程传输到应用进程中了,那么应用进程在哪儿接收呢?

4.2 System分发原理

startDispatchCycleLocked根据方法的不同分别调用publishKeyEvent和publishMotionEvent方法,那么是谁的方法呢?  首先得弄清楚connection是什么对象,慢慢往回查看。

Connection 对象是dispatchEventLocked方法中获取的,

sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);

mConnectionsByFd只是一个Connection数组,什么时候添加Connection呢?

KeyedVector<int, sp<Connection> > mConnectionsByFd;
sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);

        int fd = inputChannel->getFd();
        mConnectionsByFd.add(fd, connection);

现在确定了 connection 就是Connection对象。

InputPublisher inputPublisher;

inputPublisher 只是Connection 类中的一个变量。

Connection 是 InputDispatcher.h 中的内部类,而InputPublisher 是InputTransport.h 中的内部类,这扯着有点远。

还是顺藤摸瓜, 首先看InputDispatcher.cpp 中Connection 的构造函数,

InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) :
        status(STATUS_NORMAL), inputChannel(inputChannel), inputWindowHandle(inputWindowHandle),
        monitor(monitor),
        inputPublisher(inputChannel), inputPublisherBlocked(false) {
}

Connection 构造函数中又调用了inputPublisher的构造函数,

InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
        mChannel(channel) {
}
sp<InputChannel> mChannel;

mChannel只是InputPublisher的一个变量,至此,我们总算知道到底调用谁的publishKeyEvent和publishMotionEvent方法了, 非InputPublisher莫属。

   先暂停一下,那么registerInputChannel是如何调用的呢?完整的流程图如下:


IMS中registerInputChannel方法如下,

public void registerInputChannel(InputChannel inputChannel,
            InputWindowHandle inputWindowHandle) {
        if (inputChannel == null) {
            throw new IllegalArgumentException("inputChannel must not be null.");
        }

        nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
    }

还有monitorInput方法,

public InputChannel monitorInput(String inputChannelName) { // 显示器
        if (inputChannelName == null) {
            throw new IllegalArgumentException("inputChannelName must not be null.");
        }

        InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
        nativeRegisterInputChannel(mPtr, inputChannels[0], null, true);
        inputChannels[0].dispose(); // don't need to retain the Java object reference
        return inputChannels[1];
    }

在nativeRegisterInputChannel方法中,

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);
    if (inputChannel == NULL) {
        throwInputChannelNotInitialized(env);
        return;
    }

    sp<InputWindowHandle> inputWindowHandle =
            android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);

    status_t status = im->registerInputChannel(
            env, inputChannel, inputWindowHandle, monitor);
    if (status) {
        String8 message;
        message.appendFormat("Failed to register input channel.  status=%d", status);
        jniThrowRuntimeException(env, message.string());
        return;
    }

    if (! monitor) {
        android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
                handleInputChannelDisposed, im);
    }
}

看到了吧,将Java层的InputChannel, InputWindowHandle分别和C/C++层的NativeInputChannel等相对应。

sp<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env, jobject inputChannelObj) {
    NativeInputChannel* nativeInputChannel =
            android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
    return nativeInputChannel != NULL ? nativeInputChannel->getInputChannel() : NULL;
}
static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env,
        jobject inputChannelObj) {
    jlong longPtr = env->GetLongField(inputChannelObj, gInputChannelClassInfo.mPtr);
    return reinterpret_cast<NativeInputChannel*>(longPtr);
}

首先利用本java层的InputChannel 创建NativeInputChannel,然后返回其内部变量InputChannel,这个有点绕。

C/C++层的 InputChannel类是InputTransport.cpp的一个内部类。

现在的问题有2个,

1, registerInputChannel/ monitorInput一般什么时候调用?

2, InputChannel 还未露出真身。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Cesium中的Event对象源码可以在以下路径中找到: ``` Cesium/Source/Core/Event.js ``` 以下是Event对象的基本结构和实现: ```javascript function Event() { this._listeners = []; this._scopes = []; } Object.defineProperties(Event.prototype, { numberOfListeners : { get : function() { return this._listeners.length; } } }); Event.prototype.add = function(listener, scope) { // 将新的监听器和作用域添加到内部数组中 this._listeners.push(listener); this._scopes.push(scope); }; Event.prototype.remove = function(listener, scope) { // 在内部数组中查找并删除指定的监听器和作用域 for (var i = 0; i < this._listeners.length; i++) { if (this._listeners[i] === listener && this._scopes[i] === scope) { this._listeners.splice(i, 1); this._scopes.splice(i, 1); break; } } }; Event.prototype.raiseEvent = function() { // 以当前作用域调用所有监听器 var length = this._listeners.length; for (var i = 0; i < length; i++) { this._listeners[i].apply(this._scopes[i], arguments); } }; ``` Event对象是Cesium中的一个基础工具,用于实现事件的订阅和发布。它包含了一个内部数组,用于存储所有订阅该事件的监听器和作用域。当事件被触发时,Event对象会以当前作用域调用所有订阅该事件的监听器。同时,Event对象还提供了add()和remove()方法,用于动态添加或删除事件监听器。 ### 回答2: Cesium 是一个用于构建地球上任何规模的 3D 地球应用程序的开源 JavaScript 库。Cesium Event 对象源码是 Cesium 库中用于处理事件的核心部分之一。 Cesium Event 对象在底层实现了事件的发布和订阅机制。它允许开发者在应用程序中定义自定义事件,并能够将事件发布给感兴趣的订阅者。这个机制使得不同部分之间可以进行松散的耦合,提高了代码的模块化和可重用性。 在 Cesium Event 对象的源码中,关键的部分是一个可观察者模式的实现。它包含了通过维护一个事件列表来存储事件的订阅者,并提供了主要的操作方法,如订阅,取消订阅和发布事件。这个机制允许开发者在代码中注册想要监听的事件,并在需要时触发这些事件来通知已订阅的对象。 具体来说,Cesium Event 对象的源码主要包括以下几个部分: 1. `Subscription` 类:表示一个订阅对象,它包含了订阅者的回调函数和其他相关信息。 2. `Event` 类:表示一个事件对象,它维护了一个订阅者列表,并提供了订阅、取消订阅和触发事件的方法。 3. `Eventful` 类:表示一个具有事件机制的对象,它继承了 `Event` 类,并可以通过调用 `subscribe` 方法来注册事件监听器,以便在事件被触发时执行相应的回调函数。 通过使用 Cesium Event 对象,开发者可以轻松地实现事件驱动的编程模式,并构建出更加灵活和可扩展的应用程序。这个源码的设计和实现考虑了高效性能和可维护性,为开发者提供了一种方便和强大的工具来处理事件相关的逻辑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值