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;
}
相关推荐
geovindu12 小时前
sql:Search-Optimized SQL Queries using mysql 9.0
android·数据库·sql·mysql·数据库开发·数据库架构
知之则吱吱14 小时前
Android 开发者验证机制迎来新变化:核心要求、实施时间与开发者应对指南
android·google
墨天梦15 小时前
D09_Retrofit接口与数据转换
android·kotlin·retrofit
电商API_1800790524716 小时前
闲鱼商品列表API接口商品详情API接口请求示例(含一份真实响应数据示例)
android·数据挖掘·网络爬虫·跨境电商·反向海淘
我命由我1234517 小时前
Android 广播 - 组件通信
android·java·开发语言·android studio·android jetpack·android-studio·android runtime
阿俊-全栈开发17 小时前
LikeShop 单商户标准版商品管理二开:分类、SKU 规格与库存联动实现
android·likeshop·likeshop开源商城
用户21816970493020 小时前
Kotlin 语法二
android
妖星20 小时前
android 源码分析surfaceFinger是如何通过设置属性启动bootanim
android
JoyMonitor20 小时前
APK mmap 内存增长定位:pagemap 与 DEX 结构联合定位方法
android