我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android编程输入事件流程详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android编程输入事件流程详解

本文实例讲述了Android编程输入事件流程。分享给大家供大家参考,具体如下:

EventHub对输入设备进行了封装。输入设备驱动程序对用户空间应用程序提供一些设备文件,这些设备文件放在/dev/input里面。

EventHub扫描/dev/input下所有设备文件,并打开它们。


bool EventHub::openPlatformInput(void)
{
...
  mFDCount = 1;
  mFDs = (pollfd *)calloc(1, sizeof(mFDs[0]));
  mDevices = (device_t **)calloc(1, sizeof(mDevices[0]));
  mFDs[0].events = POLLIN;
  mDevices[0] = NULL;
  res = scan_dir(device_path);
...
  return true;
}

EventHub对外提供了一个函数用于从输入设备文件中读取数据。


bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
    int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
    int32_t* outValue, nsecs_t* outWhen)
    {
     ...
      while(1) {
    // First, report any devices that had last been added/removed.
    if (mClosingDevices != NULL) {
      device_t* device = mClosingDevices;
      LOGV("Reporting device closed: id=0x%x, name=%s\n",
         device->id, device->path.string());
      mClosingDevices = device->next;
      *outDeviceId = device->id;
      if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
      *outType = DEVICE_REMOVED;
      delete device;
      return true;
    }
    if (mOpeningDevices != NULL) {
      device_t* device = mOpeningDevices;
      LOGV("Reporting device opened: id=0x%x, name=%s\n",
         device->id, device->path.string());
      mOpeningDevices = device->next;
      *outDeviceId = device->id;
      if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
      *outType = DEVICE_ADDED;
      return true;
    }
    release_wake_lock(WAKE_LOCK_ID);
    pollres = poll(mFDs, mFDCount, -1);
    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
    if (pollres <= 0) {
      if (errno != EINTR) {
        LOGW("select failed (errno=%d)\n", errno);
        usleep(100000);
      }
      continue;
    }
    for(i = 1; i < mFDCount; i++) {
      if(mFDs[i].revents) {
        LOGV("revents for %d = 0x%08x", i, mFDs[i].revents);
        if(mFDs[i].revents & POLLIN) {
          res = read(mFDs[i].fd, &iev, sizeof(iev));
          if (res == sizeof(iev)) {
            LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d",
               mDevices[i]->path.string(),
               (int) iev.time.tv_sec, (int) iev.time.tv_usec,
               iev.type, iev.code, iev.value);
            *outDeviceId = mDevices[i]->id;
            if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
            *outType = iev.type;
            *outScancode = iev.code;
            if (iev.type == EV_KEY) {
              err = mDevices[i]->layoutMap->map(iev.code, outKeycode, outFlags);
              LOGV("iev.code=%d outKeycode=%d outFlags=0x%08x err=%d\n",
                iev.code, *outKeycode, *outFlags, err);
              if (err != 0) {
                *outKeycode = 0;
                *outFlags = 0;
              }
            } else {
              *outKeycode = iev.code;
            }
            *outValue = iev.value;
            *outWhen = s2ns(iev.time.tv_sec) + us2ns(iev.time.tv_usec);
            return true;
          } else {
            if (res<0) {
              LOGW("could not get event (errno=%d)", errno);
            } else {
              LOGE("could not get event (wrong size: %d)", res);
            }
            continue;
          }
        }
      }
    }
 ...
}

对于按键事件,调用mDevices[i]->layoutMap->map进行映射。映射实际是由 KeyLayoutMap::map完成的,KeyLayoutMap类里读取配置文件qwerty.kl,由配置文件qwerty.kl决定键值的映射关系。你可以通过修改./development/emulator/keymaps/qwerty.kl来改变键值的映射关系。

JNI函数

在frameworks/base/services/jni/com_android_server_KeyInputQueue.cpp文件中,向JAVA提供了函数android_server_KeyInputQueue_readEvent,用于读取输入设备事件。


static jboolean
android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz, jobject event)
{
  gLock.lock();
  sp hub = gHub;
  if (hub == NULL) {
    hub = new EventHub;
    gHub = hub;
  }
  gLock.unlock();
  int32_t deviceId;
  int32_t type;
  int32_t scancode, keycode;
  uint32_t flags;
  int32_t value;
  nsecs_t when;
  bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode, &flags, &value, &when);
  env->SetIntField(event, gInputOffsets.mDeviceId, (jint)deviceId);
  env->SetIntField(event, gInputOffsets.mType, (jint)type);
  env->SetIntField(event, gInputOffsets.mScancode, (jint)scancode);
  env->SetIntField(event, gInputOffsets.mKeycode, (jint)keycode);
  env->SetIntField(event, gInputOffsets.mFlags, (jint)flags);
  env->SetIntField(event, gInputOffsets.mValue, value);
  env->SetLongField(event, gInputOffsets.mWhen, (jlong)(nanoseconds_to_milliseconds(when)));
  return res;
}

readEvent调用hub->getEvent读了取事件,然后转换成JAVA的结构。

事件中转线程

在frameworks/base/services/java/com/android/server/KeyInputQueue.java里创建了一个线程,它循环的读取事件,然后把事件放入事件队列里。


Thread mThread = new Thread("InputDeviceReader") {
  public void run() {
      android.os.Process.setThreadPriority(
          android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
        try {
        RawInputEvent ev = new RawInputEvent();
        while (true) {
          InputDevice di;
        readEvent(ev);
        send = preprocessEvent(di, ev);
          addLocked(di, curTime, ev.flags, ..., me);
        }
    }
  };
}

输入事件分发线程

在frameworks/base/services/java/com/android/server/WindowManagerService.java里创建了一个输入事件分发线程,它负责把事件分发到相应的窗口上去。


mQueue.getEvent
dispatchKey/dispatchPointer/dispatchTrackball

更多关于Android相关内容感兴趣的读者可查看本站专题:《Android开发入门与进阶教程》、《Android调试技巧与常见问题解决方法汇总》、《Android多媒体操作技巧汇总(音频,视频,录音等)》、《Android基本组件用法总结》、《Android视图View技巧总结》、《Android布局layout技巧总结》及《Android控件用法总结》

希望本文所述对大家Android程序设计有所帮助。

您可能感兴趣的文章:Android APP启动方式、启动流程及启动优化分析分析Android中应用的启动流程Android教程之开机流程全面解析从源码分析Android的Glide库的图片加载流程及特点Android系统关机的全流程解析Android Bluetooth蓝牙技术使用流程详解Android Mms之:短信发送流程(图文详解)Android Mms之:接收信息流程(图文详解)Android中打电话的数据流程分析Android 2.3 拨号上网流程从源码角度进行分析


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android编程输入事件流程详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android编程输入事件流程详解

本文实例讲述了Android编程输入事件流程。分享给大家供大家参考,具体如下: EventHub对输入设备进行了封装。输入设备驱动程序对用户空间应用程序提供一些设备文件,这些设备文件放在/dev/input里面。 EventHub扫描/de
2022-06-06

Android自定义View事件分发流程详解

这篇文章主要为大家介绍了Android自定义View事件分发流程详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-02-02

Android编程之滑动按钮事件实例详解

本文实例讲述了Android滑动按钮事件。分享给大家供大家参考,具体如下: 今天纪录一下滑动按钮功能。。 首先效果图:然后是分别建立三个文件,第一个是main.class,第二个是SlipButton.class,第三个是 onchange
2022-06-06

Android Touch事件分发过程详解

本文以实例形式讲述了Android Touch事件分发过程,对于深入理解与掌握Android程序设计有很大的帮助作用。具体分析如下: 首先,从一个简单示例入手: 先看一个示例如下图所示:布局文件 :
2022-06-06

Node.js中的事件驱动编程详解

在传统程编程模里,I/O操作就像一个普通的本地函数调用:在函数执行完之前程序被堵塞,无法继续运行。堵塞I/O起源于早先的时间片模型,这种模型下每个进程就像一个独立的人,目的是将每个人区分开,而且每个人在同一时刻通常只能做一件事,必须等待前面
2022-06-04

android事件处理流程是什么

Android事件处理流程如下:1. 用户触发事件:用户在Android设备上进行了某种操作,如点击屏幕、滑动、按下按键等。2. 事件分发:Android系统接收到用户触发的事件,并将其分发给当前活动的窗口,即前台Activity。3. 窗
2023-09-13

android事件分发流程是什么

Android事件分发流程主要包括以下几个步骤:1. 事件产生:用户在屏幕上进行触摸、点击、滑动等操作时,会产生相应的事件。2. 事件传递:事件首先由顶层的ViewGroup接收,然后按照View树的层次结构依次传递给各个View,直到找到
2023-08-15

事件在Android Framework层的完整流程

在MessageQueue中,有两个重要的方法:一个是enqueueMessage,负责将消息加入队列;一个是next,负责从消息队列中取出一个待处理的消息。 // android.os.MessageQueue MessageQueue
2022-06-06

Android事件分发的流程是什么

Android事件分发的流程如下:事件发生:用户在屏幕上进行触摸或其他操作。事件捕获:事件首先被传递给顶级父视图(通常是Activity或Window)的dispatchTouchEvent方法。事件分发:顶级父视图将事件传递给其子视图的d
2023-10-24

Android APK文件结构 完整打包编译的流程 APK安装过程 详解

Android apk文件结构 打包编译的流程Android官网 配置构建 流程Configure your buildThe build processAPK文件结构assetsreslibMETA-INFAndroidManifest.
2022-06-06

Android Java crash 处理流程详解

这篇文章主要为大家介绍了Android Java crash 处理流程详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录