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的事件分发流程;

相关推荐
千里马学框架3 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台3 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone3 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc3 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
深圳老胡3 天前
STM32F407 控制 L6470 步进电机驱动 —— 控制过程简介
笔记·stm32·单片机·嵌入式硬件·代码规范
女神下凡3 天前
嵌入式设计的各种存储芯片的硬件/软件设计规范,非常全面。
arm开发·单片机·嵌入式硬件
ttyyttemo3 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077003 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼3 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
开发笔记-阿牛3 天前
蓝牙音乐灯拆解,又一次拆到蓝牙音乐灯芯片CK6865L
人工智能·单片机·嵌入式硬件·音视频·音频