事件分发机制-InputEvent进入ViewGroup之前的流转

前言

系列总结回顾:
ViewGroup事件分发总结-TouchTarget
ViewGroup事件分发总结-多点触摸事件拆分

关于Android触摸事件分发机制,在日常应用层开发工作中最常接触的是ViewGroup中的事件派发。当应用窗口接收到系统传来的Event后,到将Event传入ViewGroup前还有一段路程,本文就来分析下这个过程。

源码探究

文中源码基于 Android 10.0

事件监听注册

APP侧想要从系统接收触摸事件,首先需要进行“接收器”的注册。我们知道Acitivty启动在onResume之后,会创建ViewRootImpl,然后调用它的setView方法向WindowManagerService添加窗口,而“接收器”的注册就是在这个阶段。

[ViewRootImpl#setView]

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            // ···
            // 创建InputChannel用于输入事件的传输
            mInputChannel = new InputChannel();
            // ···
            // 添加给WindowManagerService
            res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                    getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                    mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                    mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                    mTempInsets);
            // ···
            if (mInputChannel != null) {
                if (mInputQueueCallback != null) {
                    // 存储事件的队列
                    mInputQueue = new InputQueue();
                    mInputQueueCallback.onInputQueueCreated(mInputQueue);
                }
                // 初始化并注册输入事件接收器
                mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                        Looper.myLooper());
            }
            // ···
        }
    }
}

该方法中几个比较关键的步骤:

  1. 首先创建InputChannel,它用于系统进程传输输入事件给APP进程。
  2. 接着将InputChannel传给WindowManagerService,WMS会对InputChannel进行初始化。
  3. 创建用于存储系统传来的事件的队列。
  4. 创建InputEventReceiver,并传入InputChannel和主线程Looper,在构造函数中进行注册监听。
InputChannel的初始化

接下来看其中第二步骤,WindowSession#addToDisplay会调用WindowManagerService的addWindow方法:
[WindowManagerService#addWindow]

public int addWindow(Session session, IWindow client, int seq,
        LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
        Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
        InsetsState outInsetsState) {
    // ···
        final boolean openInputChannels = (outInputChannel != null
                && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
        if  (openInputChannels) {
            // 打开InputChannel(win是WindowState)
            win.openInputChannel(outInputChannel);
        }
    // ···
}

win的实例是WindowState,一个WindowState表示一个应用端窗口

[WindowState#openInputChannel]

void openInputChannel(InputChannel outInputChannel) {
    if (mInputChannel != null) {
        throw new IllegalStateException("Window already has an input channel.");
    }
    String name = getName();
    // 创建native层InputChannel数组(包含两个InputChannel)
    InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
    // 第一个用于输出事件
    mInputChannel = inputChannels[0];
    // 第二个用于接收事件
    mClientChannel = inputChannels[1];
    // InputWindowHandle作为具柄提供给InputManagerService,用来寻找目标窗口。
    // IWindow对应一个应用侧窗口,将其作为token。
    mInputWindowHandle.token = mClient.asBinder();
    // outInputChannel即在应用进程创建传过来的InputChannel
    if (outInputChannel != null) {
        // 将mClientChannel中的实例状态转移到outInputChannel中
        mClientChannel.transferTo(outInputChannel);
        // 释放mClientChannel中的引用
        mClientChannel.dispose();
        mClientChannel = null;
    } else {
        // If the window died visible, we setup a dummy input channel, so that taps
        // can still detected by input monitor channel, and we can relaunch the app.
        // Create dummy event receiver that simply reports all events as handled.
        mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
    }
    // 将mInputChannel和IWindow注册给InputManagerService保存
    mWmService.mInputManager.registerInputChannel(mInputChannel, mClient.asBinder());
}

该方法中首先调用native方法打开两个InputChannel,一个用于系统端写入事件,一个用于应用端读取事件(当事件处理完毕后,应用端InputChannel会用来写入反馈,系统端InputChannel用来读取反馈)。之后将用于发送事件的InputChannel注册给InputManagerService,将用于接收事件的InputChannel传回给应用窗口。

打开InputChannel

这里的打开InputChannel指的是什么呢?进入native层方法看看:

[android_view_InputChannel#android_view_InputChannel_nativeOpenInputChannelPair]

static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
        jclass clazz, jstring nameObj) {
    const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
    std::string name = nameChars;
    env->ReleaseStringUTFChars(nameObj, nameChars);

    // serverChannel对应前文中的mInputChannel,clientChannel对应前文中的mClientChannel
    sp<InputChannel> serverChannel;
    sp<InputChannel> clientChannel;
    // 分配serverChannel和clientChannel
    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);

    if (result) {
        String8 message;
        message.appendFormat("Could not open input channel pair.  status=%d", result);
        jniThrowRuntimeException(env, message.string());
        return NULL;
    }

    // 创建Java层InputChannel数组
    jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
    if (env->ExceptionCheck()) {
        return NULL;
    }

    // 创建Java层InputChannel,将serverChannel的指针保存在InputChannel.mPtr成员中
    jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
            std::make_unique<NativeInputChannel>(serverChannel));
    if (env->ExceptionCheck()) {
        return NULL;
    }

    // 同上
    jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
            std::make_unique<NativeInputChannel>(clientChannel));
    if (env->ExceptionCheck()) {
        return NULL;
    }

    env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
    env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
    // 返回InputChannel数组
    return channelPair;
}

可以看到这里创建了native层的InputChannel,再将指针保存在Java层InputChannel.mPtr成员中。

看其中的InputChannel::openInputChannelPair方法:
[InputTransport#InputChannel::openInputChannelPair]

status_t InputChannel::openInputChannelPair(const std::string& name,
        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
    int sockets[2];
    // 创建一对socketpair,保存在sockets中
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
        // 错误处理 ···
        return result;
    }

    // 设置sockopt读写缓存区
    int bufferSize = SOCKET_BUFFER_SIZE;
    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));

    std::string serverChannelName = name;
    serverChannelName += " (server)";
    // 创建Server端InputChannel,并持有其中一个socketpair
    outServerChannel = new InputChannel(serverChannelName, sockets[0]);

    std::string clientChannelName = name;
    clientChannelName += " (client)";
    // 创建Client端InputChannel,并持有其中一个socketpair
    outClientChannel = new InputChannel(clientChannelName, sockets[1]);
    return OK;
}

该方法中创建一对socketpair,这对socketpair中含有两个相通的文件描述符fd,通过向fd写数据和读数据实现数据通信。Server端InputChannel和Client端InputChannel各持有一个socketpair,达到互相之间的双向通信。

关于socketpair的详细解释可以参考《Android中socketpair双向通信详解》

到这里我们知道InputChannel其实是对socketpair的一层封装。

APP侧InputChannel

回到WindowState#openInputChannel方法中,在完成包含socketpair的InputChannel对的创建后,需要将其中一个交给APP侧,另一个交给InputManagerService。

通过InputChannel.transferTo方法将内部引用状态转移给之前在APP侧创建和传过来的InputChannel,该方法中调用了nativeTransferTo方法:
[android_view_InputChannel.cpp]

static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
        jobject otherObj) {
    if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {
        jniThrowException(env, "java/lang/IllegalStateException",
                "Other object already has a native input channel.");
        return;
    }

    // 通过Java层InputChannel.mPtr保存的地址获取native层InputChannel对象
    NativeInputChannel* nativeInputChannel =
            android_view_InputChannel_getNativeInputChannel(env, obj);
    // 将nativeInputChannel的地址设置给APP侧Java层InputChannel.mPtr
    android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel);
    // 清空自身的InputChannel.mPtr的值
    android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
}

转移对象就是通过更改InputChannel.mPtr的值来实现。

IMS侧InputChannel

当转移完对象后,就会进行InputManagerService侧的注册,在registerInputChannel方法中又会调用nativeRegisterInputChannel方法,传入InputChannel。

[com_android_server_input_InputManagerService.cpp]

static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
        jlong ptr, jobject inputChannelObj, jint displayId) {
    // 获取native层InputManagerService
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    // 获取native层InputChannel
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    // ···
    // 注册进native层InputManagerService
    status_t status = im->registerInputChannel(env, inputChannel, displayId);
    // ···
}

该方法中取出native层对应的对象,native层InputManagerService的registerInputChannel方法中会把InputChannel注册给它的InputDispatcher。

接着看InputDispatcher#registerInputChannel方法:
[InputDispatcher.cpp]

status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        int32_t displayId) {
    // ···
    { // acquire lock
        std::scoped_lock _l(mLock);
        // ···
        // 创建Connection封装InputChannel
        sp<Connection> connection = new Connection(inputChannel, false /*monitor*/);

        // 获取InputChannel中socketpair的文件描述符
        int fd = inputChannel->getFd();
        mConnectionsByFd.add(fd, connection);
        mInputChannelsByToken[inputChannel->getToken()] = inputChannel;

        // Looper监听fd可读事件,当InputChannel对端写入数据时会触发handleReceiveCallback回调
        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    } // release lock

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

该方法中创建Connection用来封装InputChannel,并保存在mConnectionsByFd和mInputChannelsByToken集合中。并让InputDispatcherThread中创建的Looper监听InputChannel中fd的可读事件。

Connection中有几个重要成员:

  • InputPublisher inputPublisher:持有InputChannel,用于操作InputChannel进行写入事件和读取反馈。
  • Queue outboundQueue:缓存等待写入InputChannel进行发送的事件。
  • Queue waitQueue:缓存已发送给应用窗口的事件,用于等待事件完成反馈。

到这里便完成了InputChannel的初始化,创建了一对InputChannel分别保存在InputManagerService侧和应用窗口侧,从而实现传输链路的连接,之后双方可以通过InputChannel中的socketpair进行双向通信。

InputEventReceiver的初始化

InputEventReceiver用于APP侧接收到数据后响应事件回调。回到ViewRootImpl#setView中,这里创建其子类WindowInputEventReceiver,并传入初始化完成的InputChannel和主线程Looper。

在InputEventReceiver的构造函数中,会利用InputChannel和主线程Looper中的MessageQueue进行初始化操作,其中会调用nativeInit方法:

[android_view_InputEventReceiver.cpp]

static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
    // 通过InputChannel.mPtr成员获得native层InputChannel对象
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    // ···

    // 通过MessageQueue.mPtr成员获得native层MessageQueue对象
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    // ···

    // 创建native层InputEventReceiver,封装Java层InputEventReceiver、InputChannel、MessageQueue
    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);
    // 初始化,使Looper监听fd中可读事件
    status_t status = receiver->initialize();
    // ···

    receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
    // 返回native层InputEventReceiver指针
    return reinterpret_cast<jlong>(receiver.get());
}

该方法中主要是创建了native层的InputEventReceiver,并返回其地址给Java层保存。在它的初始化操作中,使用MessageQueue对应的Looper监听InputChannel中fd的ALOOPER_EVENT_INPUT可读事件。

小结

输入事件“接收器”的注册过程,其实就是创建了一对InputChannel,分别保存在InputManagerService侧和应用窗口侧,利用其进行双端数据传输。然后又创建WindowInputEventReceiver,用于应用窗口侧在接收到事件后,进一步分发处理。

InputChannel

输入事件接收

这里不讨论InputManagerService中的事件发送流程,仅从触发事件接收开始分析。

当InputManagerService通过InputChannel的fd写入事件数据后,应用窗口侧的InputChannel就会产生可读事件,将唤醒应用侧Looper(NativeInputEventReceiver初始化时注册了fd监听和回调),然后触发LooperCallback的handleEvent回调(NativeInputEventReceiver继承LooperCallback)。

[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;
    }
    // ···
}

判断类型如果是ALOOPER_EVENT_INPUT,则调用consumeEvents方法进一步处理。

[android_view_InputEventReceiver.cpp]

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
    // ···
    // 循环不断的处理(如果有事件)
    for (;;) {
        uint32_t seq;
        InputEvent* inputEvent;
        // 从InputChannel中读取一条数据,封装在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;

            case AINPUT_EVENT_TYPE_MOTION: {
                // 触摸类型事件
                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 happening
                inputEventObj = NULL;
            }

            if (inputEventObj) {
                // 调用Java层方法分发事件对象
                env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
                if (env->ExceptionCheck()) {
                    ALOGE("Exception dispatching input event.");
                    skipCallbacks = true;
                }
                env->DeleteLocalRef(inputEventObj);
            } else {
                // ···
            }
        }

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

可以看到,这里面循环不断的从InputChannel中读取数据封装成InputEvent对象,并根据事件类型(按键、触摸)创建对应的事件对象,之后调用Java层WindowInputEventReceiver的dispatchInputEvent方法进行事件派发。

小结

事件接收是通过先前注册的主线程Looper监听fd来实现,收到事件后封装成对应类型的触摸事件,调用Java层InputEventReceiver#dispatchInputEvent方法传递事件对象进行派发。

应用窗口事件派发

队列分发

[InputEventReceiver#dispatchInputEvent]

private void dispatchInputEvent(int seq, InputEvent event) {
    // 保存event序列号,用于事件处理完毕后再通知InputManagerService
    mSeqMap.put(event.getSequenceNumber(), seq);
    // 处理事件
    onInputEvent(event);
}

WindowInputEventReceiver重写了onInputEvent方法:
[WindowInputEventReceiver#onInputEvent]

public void onInputEvent(InputEvent event) {
    // ···
    // 事件入队
    enqueueInputEvent(event, this, 0, true);
    // ···
}

这里调用ViewRootImpl的enqueueInputEvent方法先将事件入队。

[ViewRootImpl#enqueueInputEvent]

void enqueueInputEvent(InputEvent event,
        InputEventReceiver receiver, int flags, boolean processImmediately) {
    // 从对象缓存池获取一个QueuedInputEvent,封装InputEvent
    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;
    Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
            mPendingInputEventCount);

    // 此时processImmediately为true,表示立即处理
    if (processImmediately) {
        // 直接开始处理
        doProcessInputEvents();
    } else {
        // 通过handler发送一个MSG_PROCESS_INPUT_EVENTS消息处理
        scheduleProcessInputEvents();
    }
}

加入队列后就立即开始处理。

接着看doProcessInputEvents方法:
[ViewRootImpl#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;
        
        // ···

        // 依次分发队列头事件
        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;
        // 事件全部分发完毕,取消MSG_PROCESS_INPUT_EVENTS消息
        mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
    }
}

该方法中会依次将队列中的事件全部进行分发。

[ViewRootImpl#deliverInputEvent]

private void deliverInputEvent(QueuedInputEvent q) {
    Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
            q.mEvent.getSequenceNumber());
    // ···

    InputStage stage;
    // ···
    // 获取InputStage
    stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;

    // ···

    if (stage != null) {
        handleWindowFocusChanged();
        // 由InputStage进行具体的事件分发处理
        stage.deliver(q);
    } else {
        finishInputEvent(q);
    }
}

该方法中获取InputStage后,由InputStage处理事件分发。

InputStage

InputStage是事件分发责任链中的一个基本单元。输入事件可能会经过多个InputStage,直到有InputStage拦截处理,或者都没有处理。

InputStage责任链的设置也是在ViewRootImpl的setView分发中:
[ViewRootImpl#setView]

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    // ···

    // Set up the input pipeline.
    CharSequence counterSuffix = attrs.getTitle();
    // 综合处理未分发的事件
    mSyntheticInputStage = new SyntheticInputStage();
    // 分发给view hierarchy,优先级在输入法之后
    InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
    // 分发给native activity,优先级在输入法之后
    InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
            "aq:native-post-ime:" + counterSuffix);
    // 优先级在输入法之后
    InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
    // 分发给输入法(不支持触摸事件)
    InputStage imeStage = new ImeInputStage(earlyPostImeStage,
            "aq:ime:" + counterSuffix);
    // 分发给view hierarchy,优先级在输入法之前(不支持触摸事件)
    InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
    // 分发给native activity,优先级在输入法之前(不支持触摸事件)
    InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
            "aq:native-pre-ime:" + counterSuffix);

    // mFirstInputStage是优先级最高的InputStage
    mFirstInputStage = nativePreImeStage;
    // mFirstPostImeInputStage是输入法之后优先级最高的InputStage
    mFirstPostImeInputStage = earlyPostImeStage;
}

可以看到通过组合包装的方式创建了InputStage责任链,越早创建的InputStage越后执行。

这里关心ViewPostImeInputStage,它负责分发事件到视图树,看它的deliver分发:
[ViewPostImeInputStage#deliver]

public final void deliver(QueuedInputEvent q) {
    if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
        // 如果标记FLAG_FINISHED则转到下一个InputStage
        forward(q);
    } else if (shouldDropInputEvent(q)) {
        // 如果需要丢弃则标记FLAG_FINISHED,然后转给下一个InputStage
        finish(q, false);
    } else {
        // onProcess分发处理事件并返回是否处理的结果,apply会根据结果继续向下转发
        apply(q, onProcess(q));
    }
}

直接看onProcess方法:
[ViewPostImeInputStage#onProcess]

protected int onProcess(QueuedInputEvent q) {
    if (q.mEvent instanceof KeyEvent) {
        // 按键事件
        return processKeyEvent(q);
    } else {
        final int source = q.mEvent.getSource();
        if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
            // 触摸事件
            return processPointerEvent(q);
        } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
            // 轨迹球事件
            return processTrackballEvent(q);
        } else {
            // 其他通用事件(例如外设)
            return processGenericMotionEvent(q);
        }
    }
}

该方法中根据不同事件类型,调用不同方法处理事件。

这里看触摸事件的处理,进入processPointerEvent方法:
[ViewPostImeInputStage#processPointerEvent]

private int processPointerEvent(QueuedInputEvent q) {
    // 事件对象转换成MotionEvent
    final MotionEvent event = (MotionEvent)q.mEvent;
    // ···
    // 调用了View的dispatchPointerEvent方法
    boolean handled = mView.dispatchPointerEvent(event);
    // ···
    return handled ? FINISH_HANDLED : FORWARD;
}

mView就是在ViewRootImpl#setView时传入的DecorView,这里调用了它的dispatchPointerEvent方法进行MotionEvent的分发。

DecorView派发过程

当MotionEvent传给DecorView后,还没有直接派发给我们设置的视图树。

dispatchPointerEvent方法中又调用了dispatchTouchEvent方法:
[DecorView#dispatchTouchEvent]

public boolean dispatchTouchEvent(MotionEvent ev) {
    // 获取PhoneWindow中的Callback
    final Window.Callback cb = mWindow.getCallback();
    // 正常情况下会执行cb.dispatchTouchEvent(ev) 
    return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
            ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}

可以看到DecorView并不会直接把MotionEvent派发给Content View,而是转发给Window.Callback,这个Callback就是Activity实例(Activity实现了Window.Callback接口,Activity在attach方法中创建PhoneWindow时设置Callback)。

进入Activity:
[Activity#dispatchTouchEvent]

public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    // 又转发给PhoneWindow处理
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}

该方法中又优先将MotionEvent交由PhoneWindow处理,若没有处理则再通过onTouchEvent由自身处理。

进入PhoneWindow:
[PhoneWindow#superDispatchTouchEvent]

public boolean superDispatchTouchEvent(MotionEvent event) {
    // 由调用了DecorView
    return mDecor.superDispatchTouchEvent(event);
}

这里又调用了DecorView,不同的是调用的是superDispatchTouchEvent方法,该方法中会调用super.dispatchTouchEvent,即ViewGroup的dispatchTouchEvent方法。从这里开始就正式将MotionEvent传入ViewGroup,开始ViewGroup的事件派发流程。

小结

decorview-inputevent

尾声

一个触摸事件在派发给ViewGroup前,还经过了InputManagerService-APP窗口-窗口根视图的漫长流程,其中事件传输的关键就是InputChannel和InputEventReceiver。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值