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;
}
相关推荐
bropro22 分钟前
MySQL不使用子查询的原因
android·数据库·mysql
执笔论英雄1 小时前
【cuda】 pinpaged
android·java·数据库
新青年.1 小时前
Android(Compose)使用 LibVLC 播放 RTSP 视频流
android
一见2 小时前
WorkBuddy安装Skill的方法
android·java·javascript
毛骗导演2 小时前
万字解析 OpenClaw 源码架构-跨平台应用之Android 应用
android·前端·架构
happymaker06263 小时前
JDBC(MySQL)——DAY02
android·数据库·mysql
PenguinLetsGo3 小时前
代码段的消失:页表异常清零引发的 ILL_ILLOPC 溯源
android·linux
huangchen3 小时前
Compose 中 viewModel() 函数分析
android
Tobinary3 小时前
Android系统启动
android
我又来搬代码了4 小时前
【Android】基于GDAL库实现SHP文件读写
android