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

概述:

本文主要描述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的事件分发流程;

相关推荐
Kapaseker33 分钟前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
黄林晴1 小时前
Android17 为什么重写 MessageQueue
android
阿巴斯甜1 天前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker1 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95271 天前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab2 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android