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 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台1 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone1 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc1 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo1 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077001 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼1 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone1 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen1 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone1 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui