Android InputEventReceiver事件接收流程分析

2023-11-11

本文基于Android 12。

InputEvent经过inputflinger读取后,通过Inputchannel发送到Java层的InputEventReceiver对象,输入事件和View的状态强相关,事件发送需要确定当前的焦点App,焦点Window(View),事件接收者是谁,所以InputEventReceiver对象也在View的创建流程中被初始化,ViewRootImpl中通过InputChannel参数实例化WindowInputEventReceiver,其覆盖了父类InputEventReceiver的onInputEvent()方法。

一、WindowInputEventReceiver

    // ViewRootImpl.java    
    	final class WindowInputEventReceiver extends InputEventReceiver {
            public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
                super(inputChannel, looper);
            }
          
            @Override
            public void onInputEvent(InputEvent event) {
                    enqueueInputEvent(event, this, 0, true);
            }
        }

调用enqueueInputEvent方法。

    // ViewRootImpl.java 
        QueuedInputEvent mPendingInputEventHead;
    
        void enqueueInputEvent(InputEvent event,
                InputEventReceiver receiver, int flags, boolean processImmediately) {
            QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
            QueuedInputEvent last = mPendingInputEventTail;
            if (last == null) {
                mPendingInputEventHead = q;
                mPendingInputEventTail = q;
            } else {
                last.mNext = q;
                mPendingInputEventTail = q;
            }
            mPendingInputEventCount += 1;
            doProcessInputEvents();
        }

enqueueInputEvent对事件通过mPendingInputEventHead变量维护一个事件队列,doProcessInputEvents()进行下一步处理。

    // ViewRootImpl.java 
    	void doProcessInputEvents() {
            while (mPendingInputEventHead != null) {
                QueuedInputEvent q = mPendingInputEventHead;
                mPendingInputEventHead = q.mNext;
                if (mPendingInputEventHead == null) {
                    mPendingInputEventTail = null;
                }
                q.mNext = null;
              	deliverInputEvent(q);
            }
        }

doProcessInputEvents对mPendingInputEventHead队列中所有事件进行分发。

    // ViewRootImpl.java     
    	private void deliverInputEvent(QueuedInputEvent q) {
            try {
              	InputStage stage;
                if (q.shouldSendToSynthesizer()) {
                    stage = mSyntheticInputStage;
                } else {
                    stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
                }
                if (stage != null) {
                    handleWindowFocusChanged();
                    stage.deliver(q);
                } else {
                    finishInputEvent(q);
                }
            }
        }

将事件交给InputStage处理,InputStage是Android实现事件责任链的基类,deliver()方法中处理是否传递给下一个阶段:forward(),或者结束事件的传递:finish()。

二、InputState

    // ViewRootImpl.java 
    abstract class InputStage {        
        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);
            }
        }
    }

InputStage是一个抽象类,deliver方法中调用了onProcess(q),子类覆盖onProcess方法实现自己的操作逻辑。

在ViewRootImpl的setView方法中定义了InputState的责任链:

    // ViewRootImpl.java 
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
            // 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;
    }

从实例化的顺序可知事件传递阶段的流程:

nativePreImeStage->viewPreImeStage->imeStage->earlyPostImeStage->nativePostImeStage->viewPostImeStage->mSyntheticInputStage

关于各个InputStage的官方说明:

InputState Description
NativePreImeInputStage Delivers pre-ime input events to a native activity. Does not support pointer events.
ViewPreImeInputStage Delivers pre-ime input events to the view hierarchy. Does not support pointer events.
ImeInputStage Delivers input events to the ime. Does not support pointer events.
EarlyPostImeInputStage Performs early processing of post-ime input events.
NativePostImeInputStage Delivers post-ime input events to a native activity.
ViewPostImeInputStage Delivers post-ime input events to the view hierarchy.
SyntheticInputStage Performs synthesis of new input events from unhandled input events.

可参考文章:https://blog.csdn.net/vviccc/article/details/93377708

一般用户接触到最多的Activity和View属于ViewPostImeInputStage阶段。

三、ViewPostImeInputStage

    // ViewRootImpl.java 
    	final class ViewPostImeInputStage extends InputStage {
            @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);
                    }
                }
            }
        }

processKeyEvent处理KeyEvent,MotionEvent有两种情况,触屏操作的MotionEvent调用processPointerEvent处理,轨迹球操作的MotionEvent调用processTrackballEvent处理。

接着查看processPointerEvent方法:

    // ViewRootImpl.java 
    private int processPointerEvent(QueuedInputEvent q) {
        final MotionEvent event = (MotionEvent)q.mEvent;
        boolean handled = mView.dispatchPointerEvent(event);
        maybeUpdatePointerIcon(event);
        maybeUpdateTooltip(event);
        return handled ? FINISH_HANDLED : FORWARD;
    }

调用mView.dispatchPointerEvent(event)方法。

    // View.java    
    	@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
        public final boolean dispatchPointerEvent(MotionEvent event) {
            if (event.isTouchEvent()) {
                return dispatchTouchEvent(event);
            } else {
                return dispatchGenericMotionEvent(event);
            }
        }

终于到了熟悉的dispatchTouchEvent(event)方法。接下来就是熟悉onInterceptTouchEvent(),onTouchEvent()。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android InputEventReceiver事件接收流程分析 的相关文章

随机推荐

  • Python中绘制离散型colorbar

    可以按照以下步骤进行 1 导入必要的库 matplotlib pyplot 和 matplotlib colors import matplotlib pyplot as plt import matplotlib colors as mc
  • python随机生成英文字母_在Python中生成随机字母

    有没有一种方法可以在Python中生成随机字母 如random randint 但用于字母 random randint的范围功能会很好 但是拥有仅输出随机字母的生成器总比没有好 简单 gt gt gt import string gt g
  • 【办公】word中实现三线表(跨页,续表)

    文章目录 前言 三线表 生成一张表格 设置标题和交叉引用 设置三线表 选中表格的全部 设置边框与底纹 设置跨页 分割成两个表 设置标题行重复 设置续表标识 巧妙用文本框 END 前言 在办公中 一些场景需要将普遍表格设设置为三线表 如 论文
  • HTTP状态 500 - 内部服务器错误之java.lang.ClassNotFoundException: org.apache.jsp.index_jsp

    该错误是由于 jar 包冲突引起 在 Tomcat 中 servlet 和 jsp 的 jar 包和使用 maven 导入的 jar 包产生了冲突 解决方法 将pom xml中以下代码删除 即不使用 maven 中的 jar 包
  • JavaScript图像处理(5) - 曲线操作(Curve Manipulation)

    直方图均衡作为一个自动的方法虽然可以在大多数情况下获得不错的效果 但是很多时候也受限于其单一的功能而无法满足多样化的图像处理需求 尤其是在图像的艺术处理方面 直方图均衡往往并不能达到期望的效果 有时候我们需要增强图像中的高光或者是明亮的背景
  • 使用Map集合作为封装SQL查询结果的场景和注意事项

    一 使用Map集合作为封装SQL查询结果的场景 返回给前端的参数可以看做一个集合里面封装了多个对象 并且返回的字段较少 没有合适的类可以接收 这种时候可以使用Map集合进行接收参数 在XML配置文件中返回值类型可以用Map集合 使用Map集
  • AR地图微信小程序:数字化时代下地图应用的新突破

    随着数字化时代的到来 地图应用成为人们日常生活中不可或缺的工具 而随着增强现实 AR 技术的快速发展 AR地图微信小程序应运而生 为用户提供了一种全新的地图导航体验 本文将深入探讨AR地图微信小程序的专业性和思考深度 并分析其在地图应用领域
  • 有Mysql数据库的情况下为什么要用Hive数据库?

    有Mysql数据库的情况下为什么要用Hive 最近接到公司的一个需求 要求使用Hive做数据查询 当时第一反应就是What Hive是什么鬼 一脸懵逼状 请原谅一个刚开始实习的Java实习生见识短浅 然后发现了hive的一些问题 下面简单介
  • SPSS(二)SPSS实现多因素方差分析模型(图文教程+数据集)

    SPSS 二 SPSS实现多因素方差分析模型 单因素方差分析上一篇博客https blog csdn net LuYi WeiLin article details 89917656已经介绍完毕 这篇博客我们主要来学习多因素方差分析 多因素
  • 给点云添加不同类型的噪声

    1 对于点云 首先要归一化 即将点云最大值归一化为1 matlab代码如下 path which normalization path path 1 end size normalization m 2 fpath fullfile pat
  • getshell神器工具怎么用

    getshell神器工具怎么用 多线程http并发编写出来的扫描工具 速度快 稳定性高 内存占用小扫到的百分之95都是一手的 可以更好的进行安全检测 更会不定时更新exp漏洞完全打破了目前网上所有的后缀扫描方式 tg xise404演示地址
  • 第五章 循环结构程序设计 习题(后五题编程题)

    1 文字 定义整数变量i j n 0 sum i 3 判断i lt 1000值为真走4 否则输出n 结束 执行赋值 sum 0 j 1 j判断
  • [网络安全自学篇] 三十四.Windows系统安全缺陷之5次Shift漏洞启动计算机机理分析

    这是作者的系列网络安全自学教程 主要是关于安全工具和实践操作的在线笔记 特分享出来与博友们学习 希望您们喜欢 一起进步 前文详细讲解了绕狗一句话原理 绕过安全狗的常用方法和过狗菜刀 并实践安装安全狗进行实际测试 本文将分享Windows系统
  • mysql存放double_double在数据库怎么定义 如何将double数组转成二进制存到数据库里...

    double是什么数据类型 它有什么作用 怎么在MYSQL数据库的表中插入一个double型数据 我插入double型数据的时候MYSQL会直接将double型数据四舍五入为整数 如double型的没有设置小数后保留几位 执行下面msqly
  • 股票期权与定价以及用python实现

    期权是一种能在未来特定时间以特定价格买进或卖出一定数量的特定资产的权利 期权交易是一种权利的交易 在交易中 买方在支付了一笔费用 权利金 之后 获得了后约赋予的 在合约规定时间 按事先确定的价格 执行价格 向期权卖方买进或卖出一定数量期货合
  • mysql、redis、nginx等linux搭建

    mysql5 7版本搭建 1 从MySQL Download MySQL Community Server Archived Versions 选择5 7版本64位下载 一般选择5 7版本 2 将mysql压缩包放在服务器 usr loca
  • sqli-labs/Less-26a

    这一关还是提示我们空格和注释符被过滤掉了 可能还是和上一关一样 逻辑运算符也被过滤了 那么和上一关的不同之处可能就在于注入类型的不同 我们首先判断一下注入类型 先判断是否为数字型 输入如下 id 1 0b 26 26 0b1 2 回显如下
  • jquery checkbox选中事件监听

    div span 全选 全不选 span div
  • 深度解析MySQL 5.7之中文全文检索

    InnoDB默认的全文索引parser非常合适于Latin 因为Latin是通过空格来分词的 但对于像中文 日文和韩文来说 没有这样的分隔符 一个词可以由多个字来组成 所以我们需要用不同的方式来处理 在MySQL 5 7 6中我们能使用一个
  • Android InputEventReceiver事件接收流程分析

    本文基于Android 12 InputEvent经过inputflinger读取后 通过Inputchannel发送到Java层的InputEventReceiver对象 输入事件和View的状态强相关 事件发送需要确定当前的焦点App