Android7 Input(十)View 处理Input事件pipeline

发布于:2025-06-06 ⋅ 阅读:(16) ⋅ 点赞:(0)

概述:

        本文主要描述View对InputEvent事件pipeline处理过程。

本文涉及的源码路径

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

InputEvent事件处理

View处理input事件是调用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;

            ......
            deliverInputEvent(q);
        }
        ......
    }

该方法的核心实现逻辑如下:

1、从事件队列中取出一个待处理的Input事件;

2、调用deliverInputEvent()进行处理;

3、处理完所有事件后,退出循环体;

我们继续讲解deliverInputEvent方法,实现如下:

 private void deliverInputEvent(QueuedInputEvent q) {
        ......

        InputStage stage;
        if (q.shouldSendToSynthesizer()) {
            stage = mSyntheticInputStage;
        } else {
            stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
        }
        /* 开启事件分发 */
        if (stage != null) {
            stage.deliver(q);
        } else {
            finishInputEvent(q);
        }
    }

该方法,根据事件q设置的flag,从InputStage Pipeline的不同起点,开始处理input事件,为了简化叙述,我们从点击触摸屏输入事件的处理讲起。我跳过输入法相关,流水线的处理起点为mFirstPostImeInputStage,也就是earlyPostImeStage。

InputEvent事件处理Pipeline

承接上文,我们开始将入inputEvent事件的处理Pipeline,前面的文章我们已经讲述了InputStage的处理模型,因此不再详解讲述每一个pipeline被调用到的过程,我们只讲述每一级中核心方法onProcess。 为了简化讲述的过程,我们跳过与输入法相关的,流水线起点为earlyPostImeStage

1、EarlyPostImeStage

调用EarlyPostImeInputStage类中的process方法,如下所示:

 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);
                }
            }
            return FORWARD;
        }

我们只关注触摸事件,因此直接调用processPointerEvent()进行处理,这部分代码不再展开详细描述,当EarlyPostImeInputStage处理完inputEvent后,返回FORWARD,表示将该inputEvent事件传递给下一级的流水线进行处理,也就是NativePostImeInputStage处理。

2、NativePostImeInputStage

调用NativePostImeInputStage中的onProcess方法,如下所示:

protected int onProcess(QueuedInputEvent q) {
            if (mInputQueue != null && q.mEvent instanceof KeyEvent) {
                mInputQueue.sendInputEvent(q.mEvent, q, true, this);
                return DEFER;
            }
            return FORWARD;
        }

该方法只处理按键事件,其他事件直接转发,事件传递给下一级的流水线进行处理,也就是ViewPostImeInputStage

3、ViewPostImeInputStage

调用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进行处理,如下所示:

private int processPointerEvent(QueuedInputEvent q) {
            final MotionEvent event = (MotionEvent)q.mEvent;

            mAttachInfo.mUnbufferedDispatchRequested = false;
            final View eventTarget =
                    (event.isFromSource(InputDevice.SOURCE_MOUSE) && mCapturingView != null) ?
                            mCapturingView : mView;
            mAttachInfo.mHandlingPointerEvent = true;
            boolean handled = eventTarget.dispatchPointerEvent(event);
            maybeUpdatePointerIcon(event);
            mAttachInfo.mHandlingPointerEvent = false;
            if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
                mUnbufferedInputDispatch = true;
                if (mConsumeBatchedInputScheduled) {
                    scheduleConsumeBatchedInputImmediately();
                }
            }
            return handled ? FINISH_HANDLED : FORWARD;
        }

该方法的核心逻辑如下:

1、找到传递事件的Viw视图入口。这里事件在Android UI窗口处理的起点是Activity。我们下一章进行讲解,这里先暂时跳过;

2、根据处理结果,如果事件被处理了,返回FINISH_HANDLED,不再向下一级的流水线传递,如果没有处理,则直接转发到下一级的流水线处理,也就是SyntheticInputStage,为了简化叙述,我们这里假设input事件被处理了,则apply的对返回结果的处理如下所示:

 protected void apply(QueuedInputEvent q, int result) {
            if (result == FORWARD) {
                forward(q);
            } else if (result == FINISH_HANDLED) {
                finish(q, true);
            } else if (result == FINISH_NOT_HANDLED) {
                finish(q, false);
            } else {
                throw new IllegalArgumentException("Invalid result: " + result);
            }
        }

由于事件已经被处理,所以直接调用finish,如下所示:

 protected void finish(QueuedInputEvent q, boolean handled) {
            q.mFlags |= QueuedInputEvent.FLAG_FINISHED;
            if (handled) {
                q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED;
            }
            // 当前InputStage不再处理,直接转发
            forward(q);
        }

finish中标记inputEvent事件被处理标记,然后再次调用forward,最终调用到了onDeliverToNext接口,如下所示:

 protected void onDeliverToNext(QueuedInputEvent q) {
            if (DEBUG_INPUT_STAGES) {
                Log.v(mTag, "Done with " + getClass().getSimpleName() + ". " + q);
            }
            if (mNext != null) {
                mNext.deliver(q);
            } else {
                finishInputEvent(q);
            }
        }

最终调用的是SyntheticInputStage中onDeliverToNext方法,如下所示:

protected void onDeliverToNext(QueuedInputEvent q) {
            ......
            super.onDeliverToNext(q);
        }

然后有调用到了父类的onDeliverToNext,也就是InputStage中的onDeliverToNext方法,此时流水线已经到达了末端,然后进入到finishInputEvent方法如下所示:

private void finishInputEvent(QueuedInputEvent q) {
        Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
                q.mEvent.getSequenceNumber());

        if (q.mReceiver != null) {
            boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
            q.mReceiver.finishInputEvent(q.mEvent, handled);
        } else {
            q.mEvent.recycleIfNeededAfterDispatch();
        }

        recycleQueuedInputEvent(q);
    }

核心逻辑就是调用finishInputEvent进行收尾工作,方法如下所示:

 public final void finishInputEvent(InputEvent event, boolean handled) {
        ......
        } 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();
    }

核心就是调用JNI层中的nativeFinishInputEvent方法,如下所示:

static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jlong receiverPtr,
        jint seq, jboolean handled) {
    sp<NativeInputEventReceiver> receiver =
            reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
    status_t status = receiver->finishInputEvent(seq, handled);
    if (status && status != DEAD_OBJECT) {
        String8 message;
        message.appendFormat("Failed to finish input event.  status=%d", status);
        jniThrowRuntimeException(env, message.string());
    }
}

继续调用finishInputEvent方法,我们进一步追踪,最终调用到了sendUnchainedFinishedSignal这个方法,如下所示:

status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
    InputMessage msg;
    msg.header.type = InputMessage::TYPE_FINISHED;
    msg.body.finished.seq = seq;
    msg.body.finished.handled = handled;
    return mChannel->sendMessage(&msg);
}

        这里又看到了熟悉的InputChannel的东西,最后将结束处理的信息通过夸进程通知到了InputDispatcher;然后触发epoll事件,调用到handleReceiveCallback方法进行处理完事件的清理工作,这里不再展开描述;

总结

        本文描述了Android系统中View对输入事件的处理流程,也就是pipeline事件的处理过程。然后最终将处理结果通过跨进程传递给了InputDispatcher。下一章,我们讲述View UI的事件分发流程;


网站公告

今日签到

点亮在社区的每一天
去签到