• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >Android > Android系统input设备的插拔和事件监听

Android系统input设备的插拔和事件监听

作者:网友 字体:[增加 减小] 来源:互联网 时间:2017-05-26

网友通过本文主要向大家介绍了android input子系统,android系统下载,android系统,android系统架构,android系统开发等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

Android系统input设备的插拔和事件监听


我们平台的build system移植于android,在android组件中,我们使用了Binder驱动,来实现进程间的交互,对于input系统,因为我们将android的java application framework换成了Qt,为了实现更好的图形渲染,主要是我们需要实现主从设备的显示屏公用,这样我们需要将从设备的surface传输到主设备的来实现不同设备间的surface合成,来实现主从设备共同显示。我们使用了Wayland,其自带input系统。今天我深入android源码,看看android是怎么样来完成input设备的插拔和事件监听,和事件的分发。

源码位置

这个我们以4.4.4的源码为基础。
一个在先android源码阅读网站:androidxref

input相关源码
/frameworks/native/include/android/
/frameworks/native/include/input/
/frameworks/native/libs/input/
/frameworks/base/services/input/
/frameworks/base/services/java/com/android/server/input/
/frameworks/base/services/jni/

先看这个文件
/frameworks/native/include/android/input.h
这个文件定义了android系统input相关的枚举值和一些辅助函数。input事件主要分为两种一种是Key按下抬起事件,另一种是移动事件,比如触摸屏手势,TouchPad的Cursor移动。

整个流程

Input系统分为分为两个部分,一个是事件的读取,一个是分发,分别对应/frameworks/base/services/input/InputReader.cpp和/frameworks/base/services/input/InputDispatcher.cpp。他们分别运行在不同的线程。

input事件监听

linux系统input系统,当有输入设备插拔的时候,会在/dev/input目录下生成和删除相应的驱动文件。这里介绍一个重要的类EventHub,其封装了事件监听相关的操作。android使用epoll来实现I/O的事件的监听,以前我也在博文中介绍过epoll,用于高性能的服务器开发。

   mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);

   mINotifyFd = inotify_init();
   int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
    LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s.  errno=%d",
            DEVICE_PATH, errno);

    struct epoll_event eventItem;
    memset(&eventItem, 0, sizeof(eventItem));
    eventItem.events = EPOLLIN;
  eventItem.data.u32 = EPOLL_ID_INOTIFY;
   result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);

使用inotify来实现/dev/input目录的监听,将inotify的句柄加入到epoll的监听链表中实现当设备插拔时的事件监听。
InputReader在loopOnce中通过调用EventHub的getEvent函数来驱input事件监听。
设备插拔监听
在将inotify的id加入epoll时

    struct epoll_event eventItem;
  memset(&eventItem, 0, sizeof(eventItem));
  eventItem.events = EPOLLIN;
   eventItem.data.u32 = EPOLL_ID_INOTIFY;

EPOLL_ID_INOTIFY作为额外参数传入
这样

   const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
          if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
             if (eventItem.events & EPOLLIN) {
                 mPendingINotify = true;
              } else {
                  ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
               }
               continue;
            }

根据这个标识我们mPendingINotify置成true,这样在epell_wait 调用后来执行设备插拔操作

     if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
         mPendingINotify = false;
         readNotifyLocked();
            deviceChanged = true;
     }

readNotifyLocked函数去读取/dev/input目录下的create和delete的事件。

1462 status_t EventHub::readNotifyLocked() {
1463    int res;
1464    char devname[PATH_MAX];
1465    char *filename;
1466    char event_buf[512];
1467    int event_size;
1468    int event_pos = 0;
1469    struct inotify_event *event;
1470
1471    ALOGV("EventHub::readNotify nfd: %d\n", mINotifyFd);
1472    res = read(mINotifyFd, event_buf, sizeof(event_buf));
1473    if(res < (int)sizeof(*event)) {
1474        if(errno == EINTR)
1475            return 0;
1476        ALOGW("could not get event, %s\n", strerror(errno));
1477        return -1;
1478    }
1479    //printf("got %d bytes of event information\n", res);
1480
1481    strcpy(devname, DEVICE_PATH);
1482    filename = devname + strlen(devname);
1483    *filename++ = '/';
1484
1485    while(res >= (int)sizeof(*event)) {
1486        event = (struct inotify_event *)(event_buf + event_pos);
1487        //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
1488        if(event->len) {
1489            strcpy(filename, event->name);
1490            if(event->mask & IN_CREATE) {
1491                openDeviceLocked(devname);
1492            } else {
1493                ALOGI("Removing device '%s' due to inotify event\n", devname);
1494                closeDeviceByPathLocked(devname);
1495            }
1496        }
1497        event_size = sizeof(*event) + event->len;
1498        res -= event_size;
1499        event_pos += event_size;
1500    }
1501    return 0;
1502}

这里读出每个事件然后与/dev/input拼接成devicename来,然后根据是插拔来调用打开还是关闭驱动文件。
opendeviceLocked函数根据传入的设备名,来开发设备,然后实例化一个Device对象,然后通过ioctl调用来获得设备的各种信息,然后给设备分配一个deviceId,其初始值为1,然后递加分配。

    int32_t deviceId = mNextDeviceId++;

然后是比较重要的一步,就是知道是什么输入设备,是KeyBoard,Cursor,单点屏幕,多点屏幕还是joystick。这个很重要,决定了对raw数据的解析和上层将数据作为Key还是Motion处理。这之后还有一些需要处理,比如为KeyBoard配置Key映射表,设备是否有特殊的部分,内核驱动是否KEY支持repeat。
重要的一步

1245    struct epoll_event eventItem;
1246    memset(&eventItem, 0, sizeof(eventItem));
1247    eventItem.events = EPOLLIN;
1248    eventItem.data.u32 = deviceId;
1249    if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
1250        ALOGE("Could not add device fd to epoll instance.  errno=%d", errno);
1251        delete device;
1252        return -1;
1253    }

将设备加入到epoll中,这样就能监听设备操作了,eventItem.data.u32 = deviceId;可以知道那个设备的事件,根据deviceId可以知道那个设备,就知道怎么对数据进行处理了。
在’InputReadner’中在loopOnce调用了EventHub::getEvents后使用

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
311    for (const RawEvent* rawEvent = rawEvents; count;) {
312        int32_t type = rawEvent->type;
313        size_t batchSize = 1;
314        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
315            int32_t deviceId = rawEvent->deviceId;
316            while (batchSize < count) {
317                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
318                        || rawEvent[batchSize].deviceId != deviceId) {
319                    break;
320                }
321                batchSize += 1;
322            }
323#if DEBUG_RAW_EVENTS
324            ALOGD("BatchSize: %d Count: %d", batchSize, count);
325#endif
326            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
327        } else {
328            swit



 
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • Android系统input设备的插拔和事件监听

相关文章

  • 2017-05-26使用 Diagwait 作为诊断工具,获取用于诊断 Oracle Clusterware 节点驱逐
  • 2017-05-26Spark的调度策略详解
  • 2017-05-26Android在onCreate()方法中可以获取到宽高等信息
  • 2017-05-26android-webview的使用小结,android-webview
  • 2017-05-26Butter Knife使用详解,butterknife
  • 2017-05-26近年RFC文档专题及对应编号 选编 (持续更新)
  • 2017-05-26解决关于 在android studio 出现的 DELETE_FAILED_INTERNAL_ERROR Error while Installing APK 问题,whileinstallingapk
  • 2017-05-26linux tun/tap设备的实现(kennel 3.10)
  • 2017-05-26Android系统架构,android系统结构
  • 2017-05-26FragmentStatePagerAdapter和FragmentPagerAdapter区别,fragmentpageradapter

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • 弧形菜单(Android),弧形菜单android
    • Intent(二)隐式调用intent,调用intent
    • Android--用intent打开各种文件
    • AndroidStudio怎样导入jar包,androidstudiojar
    • Android Fragment生命周期,androidfragment
    • 7.4 Android 调用 WebService
    • 安卓多线程间通信和多进程之间通信有什么不同?分别怎么实现?,安卓多线程
    • Android开发必看-快速提高 Android 开发效率的 Web 工具,android必看
    • 用Android Studio 运行ndk 程序
    • 存储链路问题导致的RAC异常

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有