IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android Framework 客户端 ViewRootImpl 到 View 的事件分发过程 -> 正文阅读

[移动开发]Android Framework 客户端 ViewRootImpl 到 View 的事件分发过程

事件分发的整体流程

屏幕硬件 Linux 驱动 InputManagerService ViewRootImpl Activity Activity包含的子View 接收中断 1 获取触摸事件 2 分发事件 3 分发事件 4 处理事件 5 响应事件 6 响应事件 7 事件处理完成 8 重绘屏幕 9 重绘屏幕 10 屏幕硬件 Linux 驱动 InputManagerService ViewRootImpl Activity Activity包含的子View

ViewRootImpl 及其后面是客户端的时序,本文主要聚焦客户端中事件分发的流程

ViewRootImpl 分发事件

dispatchInputEvent(InputEvent event) 是 ViewRootImpl 分发事件的起点,event 是输入事件

服务端 ViewRootImpl InputStage dispatchInputEvent(InputEvent event) 1 dispatchInputEvent(event, null) 2 Handler 消息 3 enqueueInputEvent(event, receiver, 0, true) 4 obtainQueuedInputEvent(event, receiver, flags) 5 doProcessInputEvents() 6 deliverInputEvent(QueuedInputEvent q) 7 deliver(QueuedInputEvent q)) 8 服务端 ViewRootImpl InputStage
  1. ViewRootImpl 收到输入事件后,会使用 enqueueInputEvent() 将其加入到消息队列,obtainQueuedInputEvent()InputEvent 转为队列所需的 QueuedInputEvent 数据结构
  2. 消息队列的消息都会在 doProcessInputEvents() 中被交由 deliverInputEvent 方法处理
  3. deliverInputEvent() 方法借助 InputStage 链表结构,采用责任链模式对事件进行遍历、传递

android.view.ViewRootImpl#deliverInputEvent 中,相关的代码实现如下

InputStage stage;
if (q.shouldSendToSynthesizer()) {
    stage = mSyntheticInputStage;
} else {
    stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}

if (q.mEvent instanceof KeyEvent) {
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "preDispatchToUnhandledKeyManager");
    try {
        mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

if (stage != null) {
    handleWindowFocusChanged();
    stage.deliver(q);
} else {
    finishInputEvent(q);
}

其中InputStageViewRootImpl.java#setView(View,WindowManager.LayoutParams,View,int) 中被初始化


// 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;

SyntheticInputStageViewPostImeInputStage 等都继承自 InputStage,并重写了下面这个构造方法

public InputStage(InputStage next) {
    mNext = next;
}

可见,不同的InputStage 构建了一个链表结构,其头节点被 mFirstInputStage 所引用,每个节点包含 next 指针,指向下一个 InputStage

这里对各个InputStage进行简要说明:

InputStage说明
NativePreImeInputStage分发早于IME的InputEvent到NativeActivity中去处理, NativeActivity和普通acitivty的功能一致,不过是在native层实现,这样执行效率会更高,同时NativeActivity在游戏开发中很实用(不支持触摸事件)。
ViewPreIMEInputStage分发早于IME的InputEvent到View框架处理,会调用view(输入焦点)的onkeyPreIme方法,同时会给View在输入法处理key事件之前先得到消息并优先处理,View系列控件可以直接复写onKeyPreIme( 不支持触摸事件)。
ImeInputStage分发InputEvent到IME处理调用ImeInputStage的onProcess,InputMethodManager的dispatchInputEvent方法处理消息(不支持触摸事件)。
EarlyPostImeInputStage与touchmode相关,比如你的手机有方向键,按方向键会退出touchmode,这个事件被消费,有可能会有view的背景变化,但不确定(支持触摸事件)。
NativePostImeInputStage分发InputEvent事件到NativeActivity,IME处理完消息后能先于普通Activity处理消息(此时支持触摸事件)。
ViewPostImeInputStage分发InputEvent事件到View框架,view的事件分发(支持触摸事件)。最终会调用到输入焦点的3个方法:使用setKeyListener注册的监听器的onKey,之后是onKeyDown和onKeyUp,或者调用activity的onKeyDown和onKeyUp方法,也就是兜底处理无人处理的key事件。
SyntheticInputStage未处理 InputEvent最后处理。

android.view.ViewRootImpl#deliverInputEvent 中,如果 stage 不为 null 则会执行 android.view.ViewRootImpl.InputStage#deliver 方法

if (stage != null) {
    handleWindowFocusChanged();
    stage.deliver(q);
} else {
    finishInputEvent(q);
}

android.view.ViewRootImpl.InputStage#deliver 方法实现如下

public final void deliver(QueuedInputEvent q) {
    if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
        forward(q);
    } else if (shouldDropInputEvent(q)) {
        finish(q, false);
    } else {
        traceEvent(q, Trace.TRACE_TAG_VIEW);
        final int result;
        try {
            result = onProcess(q);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        apply(q, result);
    }
}

事件依次经过每个Stage,如果该事件没有被标识为 “Finished”, 该Stage就会处理它,然后返回处理结果ForwardFinishForward 运行下一个Stage继续处理,而Finished将会给该事件设置上“Finished”标识,并向下一个 Stage 前进,直到最后一级 SyntheticInputStage

Stage 调用 onProcess 处理它的事件,这里采用模板模式,onProcess 是一个钩子方法,不同子类的实现不同,对于触屏事件,和按键事件来说,主要是由 ViewPostImeInputStage 来处理。

ViewPostImeInputStage

android.view.ViewRootImpl.ViewPostImeInputStage#onProcess

@Override
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);
        }
    }
}

onProcess 方法根据不同的事件类型进行划分,触屏事件走的是 processPointerEvent() , 按键事件走的是 processKeyEvent()

以触屏事件为例,

android.view.ViewRootImpl.ViewPostImeInputStage#processPointerEvent

private int processPointerEvent(QueuedInputEvent q) {
    final MotionEvent event = (MotionEvent)q.mEvent;

    mAttachInfo.mUnbufferedDispatchRequested = false;
    mAttachInfo.mHandlingPointerEvent = true;
    boolean handled = mView.dispatchPointerEvent(event);
    maybeUpdatePointerIcon(event);
    maybeUpdateTooltip(event);
    mAttachInfo.mHandlingPointerEvent = false;
    if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
        mUnbufferedInputDispatch = true;
        if (mConsumeBatchedInputScheduled) {
            scheduleConsumeBatchedInputImmediately();
        }
    }
    return handled ? FINISH_HANDLED : FORWARD;
}

其中 boolean handled = mView.dispatchPointerEvent(event);处将事件转交给 ViewRootImpl 下的 View 去处理

android.view.View#dispatchPointerEvent

@UnsupportedAppUsage
public final boolean dispatchPointerEvent(MotionEvent event) {
    if (event.isTouchEvent()) {
        return dispatchTouchEvent(event);
    } else {
        return dispatchGenericMotionEvent(event);
    }
}

dispatchPointerEvent() 中判断,如果是触摸事件,则走 dispatchTouchEvent(), 否则执行 dispatchGenericMotionEvent()

上述过程的时序图为

InputStage ViewPostImeInputStage View onProcess(QueuedInputEvent q) 1 processPointerEvent(QueuedInputEvent q) 2 dispatchPointerEvent(MotionEvent event) 3 dispatchTouchEvent(MotionEvent event) 4 InputStage ViewPostImeInputStage View

参考博文

https://www.cnblogs.com/huan89/p/14286463.html

http://gityuan.com/2016/12/31/input-ipc/

https://blog.csdn.net/vviccc/article/details/93377708

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-18 12:49:12  更:2021-08-18 12:49:22 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 9:46:51-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码