Android系列之 屏幕触控机制(二)

目录

1 Android native层传递点击事件

2 .Window和WindowManagerService的关系

2.1 ViewRootImpl 如何串联Window和WindowManagerService

2.2 ViewRootImpl和Window的关系

2.3 ViewRootImpl和WindowManagerService的关系

以上参考前一个章节

[3 ViewRootImpl的事件接收及分发](#3 ViewRootImpl的事件接收及分发)

以下章节待分解:

  1. ViewGroup事件的分发机制

4.1 activity的视图创建流程

4.2 ViewGroup的事件分发流程

4.3 View的事件处理

  1. 事件分发具体案例及解决方案

图1 Android屏幕触控机制整体流程图

3 ViewRootImpl的事件接收及分发

图2

ViewRootImpl类中有一个类 WindowInputEventReceiver

WindowInputEventReceiver继承自InputEventReceiver

这边InputDispatcher分发到java侧的点击事件就会被InputEventReceiver.onInputEvent的接收到

当一个触摸事件传递后来后, WindowInputEventReceiver收到触摸事件,

会调用到onInputEvent方法,进而执行enqueueInputEvent方法

java 复制代码
final class WindowInputEventReceiver extends InputEventReceiver {
    public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
        super(inputChannel, looper);
    }
    @Override
    public void onInputEvent(InputEvent event, int displayId) {
        enqueueInputEvent(event, this, 0, true);
    }
    ...
}
java 复制代码
void enqueueInputEvent(InputEvent event,
        InputEventReceiver receiver, int flags, boolean processImmediately) {
    adjustInputEventForCompatibility(event);

    //将当前输入事件加入队列中排列等候执行
    QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);

    //输入事件添加进队列后,加入输入事件的默认尾部
    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);

    if (processImmediately) {
        doProcessInputEvents(); //立即处理消息
    } else {
        scheduleProcessInputEvents(); // 发送handler消息,最终还是会调用         doProcessInputEvents方法处理消息
    }
}

doProcessInputEvents 循环处理所有的可处理的输入事件

java 复制代码
void doProcessInputEvents() {
    //循环处理完所有可处理的输入事件
    while (mPendingInputEventHead != null) {
        ......
        //分发处理
        deliverInputEvent(q);
    }

    // 循环处理完所有可处理的输入事件,清除延迟处理标志位
    if (mProcessInputEventsScheduled) {
        mProcessInputEventsScheduled = false;
        mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
    }
}

可以看到,在处理事件的deliverInputEvent()方法里有一个InputStage类,这个类是在ViewRootImpl的setView()里创建的,这里会执行stage.deliver(q)方法,默认开始从链表最上端的NativePreImeInputStage开始处理,

java 复制代码
private void deliverInputEvent(QueuedInputEvent q) {
    Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
            q.mEvent.getSequenceNumber());
    ...
    InputStage stage;
    if (q.shouldSendToSynthesizer()) { // 默认为false 综合处理事件阶段,比如处理导航面板、操作杆等事件。
        stage = mSyntheticInputStage;
    } else {

    // 是否跳过输入法窗口进行分发 shouldSkipIme() 默认为false
        stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
    }
    ...
    if (stage != null) {
        handleWindowFocusChanged();
        stage.deliver(q);
    } else {
        finishInputEvent(q);
    }
}

InputStage类中根据链表形式将输入的事件的责任链拼接完成了,不同的InputStage子类,通过构造方法依次串联了起来,每个子类代表不同的事件

SyntheticInputStage。综合处理事件阶段,比如处理导航面板、操作杆等事件。

ViewPostImeInputStage。视图输入处理阶段,比如按键、手指触摸等运动事件,我们熟知的view事件分发就发生在这个阶段。

NativePostImeInputStage。 分发InputEvent事件到NativeActivity,为了让IME处理完消息后能先于普通的Activity处理消息

EarlyPostImeInputStage。输入法早期处理阶段。

ImeInputStage。输入法事件处理阶段,处理输入法字符。

ViewPreImeInputStage。视图预处理输入法事件阶段,调用视图view的dispatchKeyEventPreIme方法。

NativePreImeInputStage。本地方法预处理输入法事件阶段。

java 复制代码
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
            ......
            // Set up the input pipeline.
            CharSequence counterSuffix = attrs.getTitle();
            mSyntheticInputStage = new SyntheticInputStage();
            InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
            InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
                    "aq:native-post-ime:" + counterSuffix);
            InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
            InputStage imeStage = new ImeInputStage(earlyPostImeStage,
                    "aq:ime:" + counterSuffix);
            InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
            InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
                    "aq:native-pre-ime:" + counterSuffix);
            mFirstInputStage = nativePreImeStage;
            mFirstPostImeInputStage = earlyPostImeStage;
}

deliver的入口会判断当前QueuedInputEvent的状态。

如果判断QueuedInputEvent打开FLAG_FINISHED标志位,换句话说就是不是通过finish方法进来的,就会执行forward的方法。

如果判断到当前Window失去焦点,或者还没有进行刷新ui,QueuedInputEvent则执行finish

剩下的情况执行apply的默认方法,而执行的方法由每一个InputStage的子类复写onProcess标志位决定的。

因为咱们主要关心的是用户的触屏点击等操作,所以主要关注的类的ViewPostImeInputStage()

java 复制代码
/**
 * Delivers an event to be processed.
 */
public final void deliver(QueuedInputEvent q) {
    if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
        forward(q);
    } else if (shouldDropInputEvent(q)) {
        finish(q, false);
    } else {
        apply(q, onProcess(q));
    }
}

在ViewPostImeInputStage()方法中, processKeyEvent方法为处理keyEvent(按键)事件

如果是手指的触摸事件,比如按下滑动,多手势等,就会走到processPointerEvent()方法中

java 复制代码
@Override
protected int onProcess(QueuedInputEvent q) {
    if (q.mEvent instanceof KeyEvent) {
        return processKeyEvent(q); // keyEvent(按键)事件
    } 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); // 一般的motion事件
        }
    }
}

在processPointerEvent()方法中,最终会调用mView.dispatchPointerEvent(event)方法来做按键的处理,之后就涉及到android的事件分发流程

java 复制代码
private int processPointerEvent(QueuedInputEvent q) {
    final MotionEvent event = (MotionEvent)q.mEvent;
    ...
    mAttachInfo.mUnbufferedDispatchRequested = false;
    mAttachInfo.mHandlingPointerEvent = true;
    boolean handled = mView.dispatchPointerEvent(event); //传递给view 的dispatchPointerEvent方法处理 最终会做事件分发处理
    maybeUpdatePointerIcon(event);
    maybeUpdateTooltip(event);
    mAttachInfo.mHandlingPointerEvent = false;
    if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
        mUnbufferedInputDispatch = true;
        if (mConsumeBatchedInputScheduled) {
            scheduleConsumeBatchedInputImmediately();
        }
    }
    return handled ? FINISH_HANDLED : FORWARD;
}
相关推荐
测试工坊1 小时前
Android CPU 使用率采集入门:从原理到公式
android
恋猫de小郭1 小时前
iOS + AI ,国外一个叫 Rork Max 的项目打算替换掉 Xcode
android·前端·flutter
systeminof3 小时前
从静态到实时对抗:首例安卓Runtime AI病毒解析
android·人工智能
福大大架构师每日一题5 小时前
ComfyUI v0.14.2 发布:修复 Gemini/Nano banana 节点空白图像问题,全新 MIME 匹配机制登场
android·comfyui
fengci.5 小时前
ctfshow大牛杯
android
Android系统攻城狮6 小时前
Android tinyalsa深度解析之pcm_format_to_bits调用流程与实战(一百二十三)
android·pcm·tinyalsa·音频进阶·音频性能实战
城东米粉儿7 小时前
Android Okhttp ConnectionPool 笔记
android
城东米粉儿8 小时前
Android Retrofit 笔记
android
城东米粉儿8 小时前
Android Retrofit 线程切换 笔记
android
城东米粉儿10 小时前
Kotlin @JvmOverLoads 笔记
android