Android系统获取event事件回调等几种实现和原理分析

简介: Android系统获取event事件回调等几种实现和原理分析

Android系统,它支持多种输入设备,如触摸屏、键盘、鼠标、遥控器等。Android系统如何处理这些输入设备的事件,并将它们传递给应用程序呢?本文将从源码的角度,Android系统获取event事件回调等几种实现和原理分析。

input系列文章:

Android Input系统(1) Input事件的产生与传递

一、输入事件的定义和分类

在Android系统中,输入事件是指用户通过输入设备与系统或应用程序交互产生的信号,如按键、触摸、滑动等。输入事件是一种抽象的概念,它可以被表示为不同的类,如KeyEvent、MotionEvent、InputEvent等。这些类都继承自InputEvent类,它是所有输入事件的基类,定义了一些通用的属性和方法。

输入事件可以根据不同的维度进行分类,如:

  • 按照来源分类,可以分为物理输入事件和虚拟输入事件。物理输入事件是指由真实的输入设备产生的事件,如按键、触摸屏等。虚拟输入事件是指由系统或应用程序模拟产生的事件,如导航栏、软键盘等。
  • 按照类型分类,可以分为按键事件和动作事件。按键事件是指由按下或松开某个按键产生的事件,如键盘、遥控器等。动作事件是指由触摸或移动某个位置产生的事件,如触摸屏、鼠标等。
  • 按照目标分类,可以分为系统级别的输入事件和应用级别的输入事件。系统级别的输入事件是指由系统处理或拦截的事件,如电源键、音量键、返回键等。应用级别的输入事件是指由应用程序处理或消费的事件,如按钮、文本框、滑动条等。

二、输入事件的获取和分发

Android系统获取和分发输入事件的过程涉及到多个组件和层次,如下图所示:

  • 输入设备:是指能够产生输入事件的硬件设备,如触摸屏、键盘、鼠标等。每个输入设备都有一个唯一的ID和名称,以及一组属性和功能。
  • EventHub:是位于Native层的一个组件,它负责打开和关闭输入设备,并从Linux内核读取原始的event数据,并将其封装成RawEvent结构体。
  • InputReader:是位于Native层的一个组件,它负责接收EventHub传递过来的RawEvent,并对其进行解析和转换,生成InputEvent对象,并将其发送给InputDispatcher。
  • InputDispatcher:是位于Native层的一个组件,它负责接收InputReader传递过来的InputEvent,并对其进行筛选和分发,根据不同的策略将其发送给不同的Window或应用程序。
  • InputManagerService:是位于Java层的一个服务,它负责管理InputDispatcher,并提供一些接口供其他组件调用,如注册或注销监听器、设置或获取过滤器等。
  • WindowManagerService:是位于Java层的一个服务,它负责管理Window和视图层次,并与InputDispatcher进行交互,提供一些回调方法供InputDispatcher调用,如拦截或分发输入事件等。
  • ViewRootImpl:是位于Java层的一个类,它负责连接Window和View,并从InputDispatcher接收输入事件,并将其传递给View。
  • View:是位于Java层的一个类,它是所有视图的基类,它负责处理或消费输入事件,并根据需要进行响应或反馈。

三、输入事件回调的实现和原理

在Android系统中,有两种方式可以实现输入事件回调,即注册监听器和重写方法。下面以按键事件为例,分析这两种方式的实现和原理。

方案1. 注册监听器

在Android系统中,我们有时需要获取到系统的event事件,例如键盘的按键事件,触摸屏的触摸事件等。这篇文章将详细介绍如何在Android系统中获取event事件,并实现回调。

1. 创建事件接收器接口

需要创建一个事件接收器接口,该接口定义了一个onInputEvent方法,用于接收事件的详细信息,包括键码、动作、进程ID和用户ID。

文件路径:frameworks/base/core/java/android/xxx/IInputEventReceiver.aidl

package android.xxx;
interface IInputEventReceiver {
    void onInputEvent(int keycode, int action, int pid, int uid);
}
2. 实现事件接收器

需要实现这个接口。在这个实现中,我们将事件的详细信息打印出来。

文件路径:frameworks/base/core/java/android/btf/InputEventReceiver.java

package android.xxx;
import android.util.Log;
public class InputEventReceiver extends IInputEventReceiver.Stub {
    @Override
    public void onInputEvent(int keycode, int action, int pid, int uid) {
        Log.d("ln28","keycode:"+keycode + ", action:"+action + ", pid:"+pid+", uid:"+uid);
    }
}
3. 在InputManagerService中注册事件监听器

需要在InputManagerService中注册事件监听器。我们首先创建一个inputEventListeners列表,用于存储所有注册的监听器。然后,我们提供registerInputEventListenerunregisterInputEventListener方法,用于注册和注销监听器。

文件路径:frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

private List<IInputEventListener> inputEventListeners = new ArrayList<>();
@Override
public void registerInputEventListener(IInputEventListener listener) {
    inputEventListeners.add(listener);
}
@Override
public void unregisterInputEventListener(IInputEventListener listener) {
    inputEventListeners.remove(listener);
}

interceptKeyBeforeDispatching方法中,我们获取到事件的详细信息,然后遍历所有注册的监听器,调用其onInputEvent方法。

private long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
    // 获取键码、动作、PID和UID等信息
    int keyCode = event.getKeyCode();
    int action = event.getAction();
    int pid = Binder.getCallingPid();
    int uid = Binder.getCallingUid();
    // 遍历所有注册的监听器,调用其回调方法
    for (IInputEventListener listener : inputEventListeners) {
        try {
            listener.onInputEvent(keyCode, action, pid, uid);
        } catch (RemoteException e) {
            // 处理异常情况
            e.printStackTrace();
        }
    }
    return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
}
4. 在SystemBootAppService中注册事件接收器

需要在SystemBootAppService中注册事件接收器。我们首先创建一个InputEventReceiver实例,并将其注册到InputManagerService中。

文件路径:frameworks/base/services/core/java/com/android/server/SystemBootAppService.java(我自己加的 ,你们根据实际情况 搞到自己的系统服务或者调用地方)

//private IInputEventListener listener;
//private IInputManager inputManager;
//private IInputEventReceiver receiver;
receiver = new InputEventReceiver();
listener = new IInputEventListener.Stub() {
    @Override
    public void onInputEvent(int keycode, int action, int pid, int uid) throws RemoteException {
        receiver.onInputEvent(keycode, action, pid, uid);
    }
};
inputManager = IInputManager.Stub.asInterface(ServiceManager.getService(Context.INPUT_SERVICE));
if (inputManager != null) {
    try {
        inputManager.registerInputEventListener(listener);
    } catch (RemoteException e) {
        e.printStackTrace();
    }
} else {
    Log.e("SystemBootAppService", "Failed to get INPUT_SERVICE");
}

当系统产生event事件时,我们就可以在InputEventReceiveronInputEvent方法中获取到事件的详细信息。

D/ln28: keycode:24, action:0, pid:1000, uid:1000
D/ln28: keycode:24, action:1, pid:1000, uid:1000
D/ln28: keycode:25, action:0, pid:1000, uid:1000
D/ln28: keycode:25, action:1, pid:1000, uid:1000

这种方式的原理是:

  • 当用户按下或松开某个按键时,输入设备会向Linux内核发送一个event数据,包含了按键的代码、动作、时间等信息。
  • EventHub会从Linux内核读取event数据,并将其封装成RawEvent结构体,并发送给InputReader。
  • InputReader会根据输入设备的类型和属性,将RawEvent转换成KeyEvent对象,并发送给InputDispatcher。
  • InputDispatcher会根据不同的策略,将KeyEvent分发给不同的Window或应用程序。同时,它也会遍历所有注册的IInputEventListener,调用其onInputEvent方法,将KeyEvent的信息传递给它们。
  • IInputEventListener的实现类会在onInputEvent方法中接收KeyEvent的信息,并根据需要进行处理或响应。例如,我们可以在这里打印日志、启动服务、发送广播等。

方案2. 事件监听广播

我之前写过一篇调试 Android Mstar增加IR 自定义遥控头码完整调试过程

这里有讲解 请看

5.3 调试系统层映射是否正确
5.4 拦截系统按键和发送特定按键广播
5.5 app增加注册广播接收framework发送的按键值

方案3. 重写方法

重写方法是一种简单的方式,它可以在Activity或View中实现输入事件回调,只需要重写一个方法并返回true即可。例如,我们可以在MainActivity中重写onKeyDown方法,用于接收按键事件,并根据按键的代码进行不同的操作。具体的代码如下:

// 重写onKeyDown方法
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    // 根据按键的代码进行不同的操作
    switch (keyCode) {
        case KeyEvent.KEYCODE_VOLUME_UP:
            // 增加音量
            adjustVolume(AudioManager.ADJUST_RAISE);
            return true;
        case KeyEvent.KEYCODE_VOLUME_DOWN:
            // 减少音量
            adjustVolume(AudioManager.ADJUST_LOWER);
            return true;
        case KeyEvent.KEYCODE_BACK:
            // 退出应用
            finish();
            return true;
        default:
            // 调用父类的方法
            return super.onKeyDown(keyCode, event);
    }
}

这种方式的原理是:

  • 当用户按下或松开某个按键时,输入设备会向Linux内核发送一个event数据,包含了按键的代码、动作、时间等信息。
  • EventHub会从Linux内核读取event数据,并将其封装成RawEvent结构体,并发送给InputReader。
  • InputReader会根据输入设备的类型和属性,将RawEvent转换成KeyEvent对象,并发送给InputDispatcher。
  • InputDispatcher会根据不同的策略,将KeyEvent分发给不同的Window或应用程序。如果KeyEvent是系统级别的,则由WindowManagerService处理或拦截,如电源键、音量键、返回键等。
  • WindowManagerService处理或拦截,如电源键、音量键、返回键等。如果KeyEvent是应用级别的,则由ViewRootImpl接收并传递给View。
  • ViewRootImpl会根据KeyEvent的目标Window和焦点View,将KeyEvent传递给相应的View。
  • View会根据KeyEvent的动作和代码,调用相应的方法进行处理或消费,如onKeyDown、onKeyUp等。如果View重写了这些方法,并返回true,则表示KeyEvent被消费,不再向上层传递。如果View没有重写这些方法,或者返回false,则表示KeyEvent未被消费,继续向上层传递,直到被消费或者到达最顶层。

四、细节注意

  • 方案1 : 适合系统源码任意位置使用 , 比如自定义Server 系统Server , 或者系统源码其他位置。
  • 方案2: 适合给客户app使用 , 让客户App监听注册个广播 系统收到事件就通过广播发出去 , 同时也兼容Server。
  • 方案3: 标准的搞法 适合App Activity用, Server的话重载不了方法 拿不到事件。

五、总结

本文从源码的角度,分析了Android系统获取event事件回调的实现和原理。了解了输入事件的定义和分类,以及输入事件的获取和分发的过程和组件。我还介绍了三种实现输入事件回调的方式,即注册监听器和事件监听广播 , 重写方法,并掌握了它们的原理和优缺点。

希望本文对你有所帮助,如果你有任何疑问或建议,欢迎在评论区留言。谢谢!

相关文章
|
24天前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
94 4
|
25天前
|
IDE Java 开发工具
移动应用与系统:探索Android开发之旅
在这篇文章中,我们将深入探讨Android开发的各个方面,从基础知识到高级技术。我们将通过代码示例和案例分析,帮助读者更好地理解和掌握Android开发。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧。让我们一起开启Android开发的旅程吧!
|
13天前
|
监控 Java Android开发
深入探索Android系统的内存管理机制
本文旨在全面解析Android系统的内存管理机制,包括其工作原理、常见问题及其解决方案。通过对Android内存模型的深入分析,本文将帮助开发者更好地理解内存分配、回收以及优化策略,从而提高应用性能和用户体验。
|
16天前
|
Java 开发工具 Android开发
安卓与iOS开发环境对比分析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自占据半壁江山。本文深入探讨了这两个平台的开发环境,从编程语言、开发工具到用户界面设计等多个角度进行比较。通过实际案例分析和代码示例,我们旨在为开发者提供一个清晰的指南,帮助他们根据项目需求和个人偏好做出明智的选择。无论你是初涉移动开发领域的新手,还是寻求跨平台解决方案的资深开发者,这篇文章都将为你提供宝贵的信息和启示。
24 8
|
14天前
|
存储 安全 Android开发
探索Android系统的最新安全特性
在数字时代,智能手机已成为我们生活中不可或缺的一部分。随着技术的不断进步,手机操作系统的安全性也越来越受到重视。本文将深入探讨Android系统最新的安全特性,包括其设计理念、实施方式以及对用户的影响。通过分析这些安全措施如何保护用户免受恶意软件和网络攻击的威胁,我们希望为读者提供对Android安全性的全面了解。
|
20天前
|
安全 Android开发 iOS开发
深入探讨Android与iOS系统的差异及未来发展趋势
本文旨在深入分析Android和iOS两大移动操作系统的核心技术差异、用户体验以及各自的市场表现,进一步探讨它们在未来技术革新中可能的发展方向。通过对比两者的开放性、安全性、生态系统等方面,本文揭示了两大系统在移动设备市场中的竞争态势和潜在变革。
|
20天前
|
安全 Android开发 数据安全/隐私保护
深入探索Android与iOS系统安全性的对比分析
在当今数字化时代,移动操作系统的安全已成为用户和开发者共同关注的重点。本文旨在通过比较Android与iOS两大主流操作系统在安全性方面的差异,揭示两者在设计理念、权限管理、应用审核机制等方面的不同之处。我们将探讨这些差异如何影响用户的安全体验以及可能带来的风险。
24 1
|
Linux API Android开发
Android中mmap原理及应用简析
Android中mmap原理及应用简析
525 0
Android中mmap原理及应用简析
|
前端开发 Java API
Android drawFunctor原理及应用
一. 背景AntGraphic项目Android平台中使用了基于TextureView环境实现GL渲染的技术方案,而TextureView需使用与Activity Window独立的GraphicBuffer,RenderThread在上屏TextureView内容时需要将GraphicBuffer封装为EGLImage上传为纹理再渲染,内存占用较高。为降低内存占用,经仔细调研Android源码,
631 0
|
12天前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
38 19