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

news2024/12/22 17:08:51

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

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://www.coloradmin.cn/o/165185.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

COM,Component Object Model 简介

COM&#xff0c;Component Object Model 简介 1. COM 是什么 COM 的英文全称是&#xff0c;Component Object Model&#xff0c;中文译为&#xff0c;组件对象模型。它官方的概念是&#xff1a; The Microsoft Component Object Model (COM) is a platform-independent, dis…

一个简单、功能完整的开源WMS​仓库管理系统

今天给大家推荐一个简单、功能完整的仓库管理系统。 项目简介 这是基于.NetCore 7、Vue 3开发的开源项目&#xff0c;支持中英文&#xff0c;界面简单清爽&#xff0c;采用前后端分离架构。 该项目主要是针对小型物流仓储供应链流程&#xff0c;采用的技术也都比较新&#x…

Redis基础配置之RDB持久化

测试环境&#xff1a; windows RDB的触发机制 save bgsave flushall 自动触发(配置文件配置) save命令触发&#xff1a; 触发方式 &#xff1a;首先手动删除dump.rdb文件。 当执行save命令时&#xff0c;自动生成dump.rdb文建测试成功 bgsave命令触发&#xff1a; 触…

jsp网络课程管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 网络课程管理系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统采用web模式开发&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff…

JVM内存组成简介

按照官方的说法&#xff1a;“Java 虚拟机具有一个堆&#xff0c;堆是运行时数据区域&#xff0c;所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的。”“在JVM中堆之外的内存称为非堆内存(Non-heap memory)”。可以看出JVM主要管理两种类型的内存&#xf…

【C语言航路】第十二站:自定义类型:结构体、枚举、联合体

目录 一、结构体 1.结构体的基础知识 2.结构体的声明 3.特殊的声明&#xff08;匿名结构体&#xff09; 4.结构体的自引用 5.结构体变量的定义和初始化 6.结构体的内存对齐 7.修改默认对齐数 8.结构体传参 二、位段 1.什么是位段 2.位段的内存分配 3.位段的跨平台问…

小程序目录结构和全局配置

小程序目录结构和全局配置小程序目录结构目录结构和web结构对比全局配置—pages & windows配置文件简介全局配置pages & window全局配置—tabBartabBar简介页面配置页面配置简介小程序目录结构 目录结构 和web结构对比 全局配置—pages & windows 配置文件简介 …

块级元素、行内元素、元素嵌套

HTML标签有两类&#xff1a;块级元素行内元素 &#xff08;1&#xff09;块级元素-默认总是在新行开始 div、h1~h6、blockquote、dl、dt、dd、form、hr、ol、p、pre、table、ul … 特点&#xff1a;总是在新行上开始&#xff0c;高度、行高以及顶和底边距都可控制&#xff0c;宽…

Kubernetes_HPA实践使用

文章目录一、前言二、配置APIServer和安装Metrics2.1 APIServer开启Aggregator2.2 安装Metrics Server (需要用到metris.yaml)安装metrics Server之前安装metrics Server之中全部命令实践演示安装metrics Server之后三、使用HPA测试 (需要使用到test.yaml&#xff0c;里面包括 …

明清专题数据库:企业匹配官办书局距离、科举考试、商帮文化变量等

一、企业到明清官办书局&#xff0c;印刷局的最小距离测算 以明清进士数量的地域分布测度儒家文化的历史积淀&#xff0c;使用企业到古代印刷局的距离作为工具变量解决内生性问题&#xff01; 数据来源&#xff1a;自主整理 时间跨度&#xff1a;-- 区域范围&#xff1a;全国…

第十四届蓝桥杯单片机组学习笔记(1):点亮板子第一个LED

点亮板子第一个LED前言单片机IO控制M74HC573M1R-数据锁存器74HC138-38译码器代码前言 使用CT107D实验板子的时候可以看到为了IO口对多个外设的复用&#xff0c;所以板子使用了几个锁存器来对LED、数码管、蜂鸣器等外设进行了一个选择&#xff0c;最后再使用38译码器来使用三个…

如何对时间序列进行小波分析,得出其周期?

从信号处理角度进行分析 简单的时间序列直接做各种谱分析&#xff08;频谱&#xff0c;包络谱&#xff0c;平方包络谱&#xff0c;功率谱&#xff0c;倒谱等等&#xff09; 比如一些简单的旋转机械振动时间序列信号 ​如果频谱不好分析&#xff0c;那可以分析如下图所示的时间序…

FL Studio21最新版数字音频工作站(DAW)

FL Studio21首先提供了音符编辑器&#xff0c;编辑器可以针对音乐创作人的要求编辑出不同音律的节奏&#xff0c;例如鼓&#xff0c;镲&#xff0c;锣&#xff0c;钢琴&#xff0c;笛&#xff0c;大提琴&#xff0c;筝&#xff0c;扬琴等等任何乐器在音乐中的配乐。 水果音乐制…

【Linux C编程-高级篇】换行回车探讨printf行缓冲write函数掉电保护线程安全相关

换行回车探讨 \r : 回车&#xff0c;定位到本行开头\n : 换行&#xff0c;光标移到下一行\r\n : 将光标移动到下一行开头windows 下&#xff0c;每行结尾 \r\n类unix&#xff0c;每行结尾 \nMac系统&#xff0c;每行结尾\r \r\n&#xff0c;windows下好像改善了&#xff0c;使…

全文最详细的Apache的管理及优化Web(图文详解)

目录 前言 一、Apache的安装及启用 二、Apache的基本信息 三、Apache的基本配置及修改 1、默认发布文件 2、Apache端口修改 3、默认发布目录 三、Apache的访问控制 1、基于客户端ip的访问控制 2、基于用户认证 四、Apache的虚拟主机 五、Apache的语言支持 六…

React--》超详细教程——React脚手架的搭建与使用

目录 React脚手架的创建 全局安装创建 npx安装创建(官方推荐) 指定React版本安装 脚手架文件介绍 React脚手架是开发现代Web应用的必备&#xff0c;其充分利用Webpack、Babel、ESlint等工具辅助项目的开发&#xff0c;当然这些工具也无需手动配置即可使用&#xff0c;脚手…

Java 在云原生中的内存问题

Java 凭借着自身活跃的开源社区和完善的生态优势&#xff0c;在过去的二十几年一直是最受欢迎的编程语言之一。步入云原生时代&#xff0c;蓬勃发展的云原生技术释放云计算红利&#xff0c;推动业务进行云原生化改造&#xff0c;加速企业数字化转型。 然而 Java 的云原生转型之…

Word目录自动生成,不使用word默认样式的,且指定从某页开始为第一页

文章目录一&#xff0c; 设置正文页为第1页&#xff1a;二&#xff0c;自动生成目录。拓展&#xff1a;需求&#xff1a;文章或者论文往往会先写好标题&#xff0c;摘要&#xff0c;写好内容。最后需要生成目录。但是这样布局后&#xff0c;生成的目录的起始页码不是从第1 页开…

ChatGPT通俗笔记:从GPT-N、RL之PPO算法到instructGPT、ChatGPT

前言 自从我那篇BERT通俗笔记一经发布&#xff0c;然后就不断改、不断找人寻求反馈、不断改&#xff0c;其中一位朋友倪老师(之前我司NLP高级班学员现课程助教老师之一)在谬赞BERT笔记无懈可击的同时&#xff0c;给我建议到&#xff0c;“后面估计可以尝试尝试在BERT的基础上&…

搭建OpenCV环境和Jupyter Notebook

使用Anaconda搭建python和OpenCV环境安装Anaconda&#xff0c;全程下一步&#xff0c;修改了一下默认安装路径&#xff0c;修改为D:\Program Files\Anaconda3同时设置了环境变量&#xff0c;将三个文件夹路径都加到了系统环境变量&#xff0c;path中。打开【开始】菜单&#xf…