[Android Input系统]MotionEvent的序列化传送

article/2025/9/14 4:09:48

这里从云游戏的触控操作看起,

PC端的客户端支持按键和鼠标滑动操作,手机上的云游戏客户端则是和手机游戏一样的touch触控,客户端的touch操作是怎样处理给服务端的呢,猜测是把touch操作“实时”的传送给了服务器,Android服务器上把这些数据转换为相应的MotionEvent事件。

MotionEvent主要包含触控点的信息,由于其实现了Parcelable接口,所以可以使用Parcel来进行序列化保存和传输。

public final class MotionEvent extends InputEvent implements Parcelable

这里再进一步考虑一下,在Input事件分发的时候,InputDispatcher把input事件通过InputChannel发送给对应的应用进程的时候,是怎样传送input事件数据的呢,

在frameworks/native/services/inputflinger/InputDispatcher.cpp中有


1999void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
2000        const sp<Connection>& connection) {
2001#if DEBUG_DISPATCH_CYCLE
2002    ALOGD("channel '%s' ~ startDispatchCycle",
2003            connection->getInputChannelName().c_str());
2004#endif
2005
2006    while (connection->status == Connection::STATUS_NORMAL
2007            && !connection->outboundQueue.isEmpty()) {
2008        DispatchEntry* dispatchEntry = connection->outboundQueue.head;
2009        dispatchEntry->deliveryTime = currentTime;
2010
2011        // Publish the event.
2012        status_t status;
2013        EventEntry* eventEntry = dispatchEntry->eventEntry;
2014        switch (eventEntry->type) {
2015        case EventEntry::TYPE_KEY: {
2016            KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
2017
2018            // Publish the key event.
2019            status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
2020                    keyEntry->deviceId, keyEntry->source,
2021                    dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
2022                    keyEntry->keyCode, keyEntry->scanCode,
2023                    keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
2024                    keyEntry->eventTime);
2025            break;
2026        }
2027
2028        case EventEntry::TYPE_MOTION: {
2029            MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
2030
2031            PointerCoords scaledCoords[MAX_POINTERS];
2032            const PointerCoords* usingCoords = motionEntry->pointerCoords;
2033
2034            // Set the X and Y offset depending on the input source.
2035            float xOffset, yOffset;
2036            if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
2037                    && !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
2038                float scaleFactor = dispatchEntry->scaleFactor;
2039                xOffset = dispatchEntry->xOffset * scaleFactor;
2040                yOffset = dispatchEntry->yOffset * scaleFactor;
2041                if (scaleFactor != 1.0f) {
2042                    for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
2043                        scaledCoords[i] = motionEntry->pointerCoords[i];
2044                        scaledCoords[i].scale(scaleFactor);
2045                    }
2046                    usingCoords = scaledCoords;
2047                }
2048            } else {
2049                xOffset = 0.0f;
2050                yOffset = 0.0f;
2051
2052                // We don't want the dispatch target to know.
2053                if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {
2054                    for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
2055                        scaledCoords[i].clear();
2056                    }
2057                    usingCoords = scaledCoords;
2058                }
2059            }
2060
2061            // Publish the motion event.
2062            status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,
2063                    motionEntry->deviceId, motionEntry->source, motionEntry->displayId,
2064                    dispatchEntry->resolvedAction, motionEntry->actionButton,
2065                    dispatchEntry->resolvedFlags, motionEntry->edgeFlags,
2066                    motionEntry->metaState, motionEntry->buttonState,
2067                    xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision,
2068                    motionEntry->downTime, motionEntry->eventTime,
2069                    motionEntry->pointerCount, motionEntry->pointerProperties,
2070                    usingCoords);
2071            break;
2072        }

调用到inputPublisher.publishMotionEvent


284status_t InputPublisher::publishMotionEvent(
285        uint32_t seq,
286        int32_t deviceId,
287        int32_t source,
288        int32_t displayId,
289        int32_t action,
290        int32_t actionButton,
291        int32_t flags,
292        int32_t edgeFlags,
293        int32_t metaState,
294        int32_t buttonState,
295        float xOffset,
296        float yOffset,
297        float xPrecision,
298        float yPrecision,
299        nsecs_t downTime,
300        nsecs_t eventTime,
301        uint32_t pointerCount,
302        const PointerProperties* pointerProperties,
303        const PointerCoords* pointerCoords) {
304#if DEBUG_TRANSPORT_ACTIONS
305    ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
306            "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
307            "metaState=0x%x, buttonState=0x%x, xOffset=%f, yOffset=%f, "
308            "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
309            "pointerCount=%" PRIu32,
310            mChannel->getName().c_str(), seq,
311            deviceId, source, action, actionButton, flags, edgeFlags, metaState, buttonState,
312            xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount);
313#endif
314
315    if (!seq) {
316        ALOGE("Attempted to publish a motion event with sequence number 0.");
317        return BAD_VALUE;
318    }
319
320    if (pointerCount > MAX_POINTERS || pointerCount < 1) {
321        ALOGE("channel '%s' publisher ~ Invalid number of pointers provided: %" PRIu32 ".",
322                mChannel->getName().c_str(), pointerCount);
323        return BAD_VALUE;
324    }
325
326    InputMessage msg;
327    msg.header.type = InputMessage::TYPE_MOTION;
328    msg.body.motion.seq = seq;
329    msg.body.motion.deviceId = deviceId;
330    msg.body.motion.source = source;
331    msg.body.motion.displayId = displayId;
332    msg.body.motion.action = action;
333    msg.body.motion.actionButton = actionButton;
334    msg.body.motion.flags = flags;
335    msg.body.motion.edgeFlags = edgeFlags;
336    msg.body.motion.metaState = metaState;
337    msg.body.motion.buttonState = buttonState;
338    msg.body.motion.xOffset = xOffset;
339    msg.body.motion.yOffset = yOffset;
340    msg.body.motion.xPrecision = xPrecision;
341    msg.body.motion.yPrecision = yPrecision;
342    msg.body.motion.downTime = downTime;
343    msg.body.motion.eventTime = eventTime;
344    msg.body.motion.pointerCount = pointerCount;
345    for (uint32_t i = 0; i < pointerCount; i++) {
346        msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
347        msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
348    }
349    return mChannel->sendMessage(&msg);
350}

input事件被转换成InputMessage来进行传送


151status_t InputChannel::sendMessage(const InputMessage* msg) {
152    size_t msgLength = msg->size();
153    ssize_t nWrite;
154    do {
155        nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
156    } while (nWrite == -1 && errno == EINTR);
157
158    if (nWrite < 0) {
159        int error = errno;
160#if DEBUG_CHANNEL_MESSAGES
161        ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.c_str(),
162                msg->header.type, error);
163#endif
164        if (error == EAGAIN || error == EWOULDBLOCK) {
165            return WOULD_BLOCK;
166        }
167        if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED || error == ECONNRESET) {
168            return DEAD_OBJECT;
169        }
170        return -error;
171    }
172
173    if (size_t(nWrite) != msgLength) {
174#if DEBUG_CHANNEL_MESSAGES
175        ALOGD("channel '%s' ~ error sending message type %d, send was incomplete",
176                mName.c_str(), msg->header.type);
177#endif
178        return DEAD_OBJECT;
179    }
180
181#if DEBUG_CHANNEL_MESSAGES
182    ALOGD("channel '%s' ~ sent message of type %d", mName.c_str(), msg->header.type);
183#endif
184    return OK;
185}

接收InputMessage数据的socket处理如下:


187status_t InputChannel::receiveMessage(InputMessage* msg) {
188    ssize_t nRead;
189    do {
190        nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
191    } while (nRead == -1 && errno == EINTR);
192
193    if (nRead < 0) {
194        int error = errno;
195#if DEBUG_CHANNEL_MESSAGES
196        ALOGD("channel '%s' ~ receive message failed, errno=%d", mName.c_str(), errno);
197#endif
198        if (error == EAGAIN || error == EWOULDBLOCK) {
199            return WOULD_BLOCK;
200        }
201        if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED) {
202            return DEAD_OBJECT;
203        }
204        return -error;
205    }
206
207    if (nRead == 0) { // check for EOF
208#if DEBUG_CHANNEL_MESSAGES
209        ALOGD("channel '%s' ~ receive message failed because peer was closed", mName.c_str());
210#endif
211        return DEAD_OBJECT;
212    }
213
214    if (!msg->isValid(nRead)) {
215#if DEBUG_CHANNEL_MESSAGES
216        ALOGD("channel '%s' ~ received invalid message", mName.c_str());
217#endif
218        return BAD_VALUE;
219    }
220
221#if DEBUG_CHANNEL_MESSAGES
222    ALOGD("channel '%s' ~ received message of type %d", mName.c_str(), msg->header.type);
223#endif
224    return OK;
225}
226
227sp<InputChannel> InputChannel::dup() const {
228    int fd = ::dup(getFd());
229    return fd >= 0 ? new InputChannel(getName(), fd) : NULL;
230}


http://chatgpt.dhexx.cn/article/uQthQ9qP.shtml

相关文章

Android MotionEvent 坐标获取

Android MotionEvent中getX()与getRawX()都是获取屏幕坐标&#xff08;横&#xff09;&#xff0c;但二者又有区别 getX() &#xff1a; 是获取相对当前控件&#xff08;View&#xff09;的坐标 getRawX() &#xff1a; 是获取相对显示屏幕左上角的坐标 演示…

Android事件拦截(一)——触摸事件MotionEvent分析

&#xff08;1&#xff09;MotionEvent相关动作事件 //按下动作 public static final int ACTION_DOWN 0; //抬起动作 public static final int ACTION_UP 1; //移动动作 public static final int ACTION_MOVE 2; //触摸取消动作 public static final int ACTION_CANCEL 3…

Android MotionEvent

1.MotionEvent Android将所有的输入事件都放在了 MotionEvent 中&#xff0c;MotionEvent 负责集中处理所有类型设备的输入事件&#xff0c;包括单点触控、手势、多点触控、触控笔、鼠标、键盘、操纵杆、游戏控制器等。 事件类型&#xff1a; MotionEvent的事件类型主要有&…

[Android]视图的控触操作-MotionEvent

引入 对屏幕的任何操作&#xff0c;系统都会创建一个触摸事件的对象MotionEvent来应对这个操作。当点击手机屏幕的某一个视图时&#xff0c;最先感应到的是屏幕&#xff0c;因为Activity系统是分层的结构&#xff0c;底层是一些驱动&#xff0c;所以驱动就会得到信息并且把信息…

MotionEvent 详解

Android MotionEvent详解: https://www.jianshu.com/p/0c863bbde8eb https://www.diycode.cc/topics/392 Android 将所有的输入事件都放在了 MotionEvent 中&#xff0c;随着安卓的不断发展壮大&#xff0c;MotionEvent 也开始变得越来越复杂&#xff0c;下面是我自己整理的 …

MotionLayout MotionScene 动画从未如此简单!

话不多说先上图。 这是要做的最终效果。通过这些动画我们将了解MotionLayout的使用方法和常用的一些属性。 第一步&#xff1a;添加依赖 如果要使用MotionLayout请将ConstraintLayout更新到2.0及以上。在build.gradle文件中添加依赖 如果使用的是AndroidX&#xff0c;添加依赖…

Android的MotionEvent和事件处理

之前几篇文章我们讲解了自定义View和ViewGroup, 今天我们来看下View和ViewGroup常见的触摸事件和按键事件。 MotionEvent MotionEvent对象是与用户触摸相关的时间序列&#xff0c;该序列从用户首次触摸屏幕开始&#xff0c;经历手指在屏幕表面的任何移动&#xff0c;直到手指…

MotionEvent详解

Android MotionEvent 详解&#xff0c;之前用了两篇文章 事件分发机制原理 和 事件分发机制详解 来讲解事件分发&#xff0c;而作为事件分发主角之一的 MotionEvent 并没有过多的说明&#xff0c;本文就带大家了解 MotionEvent 的相关内容&#xff0c;简要介绍触摸事件&#xf…

Android自定义View进阶-MotionEvent详解

欢迎Follow我的GitHub, 关注我的CSDN. 其余参考Android目录 我们微信公众号&#xff1a;杨守乐 推荐文章&#xff1a; 如果你喜欢上了一个程序员小伙&#xff0c;献给所有的程序员女友 学习资料&#xff08;干货汇集&#xff09;不断更新【更新于2017-2-25】 Android Studi…

常用awk命令整理

AWK倾向于一行一行的数据进行处理 awk 用法&#xff1a;awk pattern {action} 变量名 含义 ARGC 命令行变元个数 ARGV 命令行变元数组 FILENAME 当前输入文件名 FNR 当前文件中的记录号 FS 输入域分隔符&#xff0c;默认为一个空格 RS 输入记录分隔符 NF 当…

sed命令和awk命令

sed命令 sed命令■什么是sed■sed的工作流程■命令格式■常用选项:■常用操作:■打印内容■sed编辑器的寻址方式■删除行■替换■插入 awk命令■awk命令的工作原理■命令格式■常见的内建变量■按行输出文本■按字段输出文本:■通过管道、双引号调用Shell 命令■拓展 sed命令 …

awk命令应用

记录&#xff1a;353 场景&#xff1a;在CentOS 7.9操作系统上&#xff0c;使用awk文本处理工具处理文本&#xff1b;使用awk、cat和grep搭配使用处理文本&#xff1b;使用awk直接处理文本&#xff1b;使用shell脚本调用awk脚本处理文本。 版本&#xff1a; 操作系统&#x…

概要设计之功能模块

功能模块描述 所谓功能模块&#xff0c;从字面上理解&#xff0c;就是以功能来进行划分模块。 接着&#xff0c;根据功能特性多少&#xff0c;决定是否要划分“子功能模块”。 这里就容易出现一个问题&#xff0c;如何去确定每个功能的界限呢&#xff0c;以及很多人会拿用户角…

系统架构图编写(概要设计)

系统架构图编写&#xff08;概要设计&#xff09; 应用架构图、技术架构图、业务架构图定义以及到底怎么画好架构图&#xff1f; 常见的数据库架构设计方案&#xff1f; 业务架构的定义、特性和方法 架构图之间的关系 业务架构图 业务架构&#xff0c;是IT架构的基础。 是从业…

概要设计、详细设计:概念、方法、实践步骤

完整软件开发流程&#xff1a; 需求分析、概要设计、详细设计 一 1. 概念、方法、实践步骤 设计是指根据需求开发的结果&#xff0c;对产品的技术实现由粗到细进行设计的过程。根据设计粒度和目的的不同可以将设计分为概要设计、详细设计等阶段以便于管理和确保质量。设计内容…

概要设计与详细设计如何编写

撰写的设计文档主要分为&#xff1a;总体概要设计文档 详细设计文档&#xff0c;后简称为“概设”“详设”。 总设和详设都应该包含的部分&#xff1a; &#xff08;1&#xff09; 需求&#xff1a;一般以产品的语言描述&#xff0c;这一块可以拷贝产品需求文档中的story li…

软件项目总体设计

软件项目总体设计 目录 1.导言 1 1.1目的 1 1.2范围 1 1.3参考资料 2 2.项目设计原则简介 2 3.功能模块设计 2 3.1功能模块设计总述 2 3.2 客户端子系统模块设计 4 3.2.1 模块 CM1 &#xff1a;静态页面 4 3.2.2 模块 CM2&#xff1a;系统登录 5 3.2.3 模块 CM3 &#xff1a;注…

软件概要设计的过程与任务

在完成对软件系统的需求分析之后&#xff0c;接下来需要进行的是软件系统的概要设计。一般说来&#xff0c;对于较大规模的软件项目&#xff0c;软件设计往往被分成两个阶段进行。首先是前期概要设计&#xff0c;用于确定软件系统的基本框架&#xff1b;然后是在概要设计基础上…

软件工程技术--第四章 概要设计

第四章 概要设计 4.1 软件设计概述 4.1.1 软件设计的概念与重要性 ​ 软件设计是软件工程的重要阶段&#xff0c;是一个将软件需求转换为软件表示的过程。软件设计的基本目标是用比较抽象概括的方式确定目标系统如何完成预定的任务&#xff0c;即确定系统的物理模型&#xff0…

ios 新建项目关于Main.storyboard的处理

使用xcode新建新建项目时&#xff0c;都会带一个main.storyboard的主界面。如果你不进行代码控制&#xff0c;默认APP启动会加载main.storyboard这个界面。怎么使用这个界面来加载这里就不在介绍了&#xff0c;这里只说用代码加载主页&#xff0c;不使用main.storyboard时&…