菜单

系统服务封装

相关源文件

概述

系统服务包装器是 scrcpy 服务器基础设施的关键组成部分,它们提供对 Android 系统服务的抽象访问。这些包装器使 scrcpy 能够以版本无关的方式与 Android 的内部 API 进行交互,从而在不同的 Android 版本(从 Android 5 到 Android 14+)和设备特定实现之间保持兼容性。

有关如何使用这些服务来控制设备的信息,请参阅 控制系统

架构

系统服务包装器遵循一致的设计模式:

  1. 一个中央 ServiceManager 类,提供访问系统服务的工厂方法。
  2. 为每个系统服务提供单独的包装器类。
  3. 基于反射的 Android 内部 API 访问。
  4. 版本检测和兼容性逻辑,用于处理跨 Android 版本的 API 更改。

系统服务管理器设计

来源:server/src/main/java/com/genymobile/scrcpy/wrappers/ServiceManager.java1-111

服务管理器

ServiceManager 是一个核心组件,它提供访问各种 Android 系统服务的方法。它使用 Java 反射来访问 Android 内部的 ServiceManager 类,并通过名称检索服务实例。

ServiceManager 的主要特点:

  • 使用单例模式维护服务包装器的单个实例。
  • 使用反射检索系统服务。
  • 将原始接口映射到类型化的包装器对象。
  • 处理错误和边缘情况(例如,没有剪贴板管理器的设备)。

来源:server/src/main/java/com/genymobile/scrcpy/wrappers/ServiceManager.java15-111

核心系统服务包装器

InputManager

InputManager 包装器提供将输入事件(触摸、键盘、鼠标)注入 Android 系统的功能。它解决了以下挑战:

  1. API 签名不断变化的各种 Android 版本。
  2. 为多显示器设备设置显示 ID。
  3. 为高级鼠标事件设置操作按钮。

关键方法

  • injectInputEvent(InputEvent inputEvent, int mode):将输入事件注入系统。
  • setDisplayId(InputEvent inputEvent, int displayId):将输入事件与特定显示器关联。
  • setActionButton(MotionEvent motionEvent, int actionButton):为鼠标事件设置操作按钮。

来源:server/src/main/java/com/genymobile/scrcpy/wrappers/InputManager.java12-100

ClipboardManager

ClipboardManager 包装器促进了主机计算机和 Android 设备之间的剪贴板操作。由于 Android 版本之间存在显著的 API 更改,它是最复杂的包装器之一。

包装器处理:

  • 访问剪贴板内容。
  • 设置剪贴板内容。
  • 设置剪贴板更改监听器。

该类实现了广泛的方法签名检测,以处理跨 API 更改的兼容性。

显著特点

  • 支持跨 Android 版本 getPrimaryClip 的 7 种不同的方法签名。
  • 支持跨 Android 版本 setPrimaryClip 的 4 种不同的方法签名。
  • 处理某些设备上缺失的剪贴板管理器。

来源:server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java14-270

PowerManager

PowerManager 包装器提供检查设备屏幕状态的方法。

主要功能

  • 处理 Android 14 中的 API 更改,其中检查屏幕状态的方法从 isInteractive() 更改为 isDisplayInteractive(int displayId)
  • 为多屏设备提供兼容层。

来源:server/src/main/java/com/genymobile/scrcpy/wrappers/PowerManager.java11-49

StatusBarManager

StatusBarManager 包装器提供控制设备通知和设置面板的方法。

关键方法

  • expandNotificationsPanel():展开通知面板。
  • expandSettingsPanel():展开快速设置面板。
  • collapsePanels():折叠已展开的面板。

该包装器处理各种方法签名,包括针对某些供应商 ROM 的自定义版本。

来源:server/src/main/java/com/genymobile/scrcpy/wrappers/StatusBarManager.java9-98

高级系统服务包装器

ActivityManager 和 ContentProvider

ActivityManager 包装器提供以下方法:

  1. 在设备上启动活动。
  2. 强制停止应用程序。
  3. 访问内容提供程序(特别是用于设备设置)。

ContentProvider 包装器是 ActivityManager 的配套组件,它:

  1. 提供对 Android 系统设置的受控访问。
  2. 处理系统、安全和全局设置的表。
  3. 通过 Closeable 接口实现正确的生命周期管理。

解决的显著挑战:

  • 跨 Android 版本的 API 签名更改(例如,Android 12 引入了 AttributionSource)。
  • 使用 root UID 进行适当的权限处理。
  • 资源管理以防止泄漏。

来源

基于反射的 API 访问

所有系统服务包装器都依赖于反射来访问 Android 内部 API。下表概述了常用的反射模式:

模式目的示例
方法发现。查找可用的方法签名。ClipboardManager 中的 getGetPrimaryClipMethod()
方法缓存。缓存已发现的方法以提高性能。if (getPrimaryClipMethod == null) 模式。
版本跟踪记住哪种方法签名有效。getMethodVersion 变量。
回退机制。尝试多种方法签名。级联 try-catch 块。
参数适配。为不同 API 版本适配参数。方法调用程序中的 switch 语句。

这种反射方法提供了几个好处:

  • 无需重新编译即可在任何 Android 版本上运行。
  • 处理供应商特定的 API 修改。
  • 自动适应新 Android 版本中的 API 更改。

常解决的挑战:

  • 新版本的方法有时会有额外的参数。
  • 参数顺序有时在不同版本之间会发生变化。
  • 某些方法会被重命名或移动到不同的类中。

来源

处理 Android 版本差异

包装器使用多种技术来处理 Android 版本差异:

  1. API 级别检测。:

  2. 方法签名探测。:

  3. 版本跟踪变量。:

  4. Switch 分派。:

这些技术创建了一个灵活的系统,能够自动适应不同的 Android 版本和供应商特定的修改。

来源

总结

系统服务包装器组件提供了一个强大的抽象层,使 scrcpy 能够以版本无关的方式访问 Android 系统服务。这种设计允许 scrcpy:

  1. 支持许多 Android 版本(从 5.0 到 14+)。
  2. 兼容供应商特定的 Android 修改。
  3. 优雅地处理 API 更改。
  4. 访问屏幕镜像和设备控制所需的系统功能。

基于反射的方法发现、版本检测、参数适配和仔细的错误处理相结合,创建了一个灵活且有弹性的系统,极大地增强了 scrcpy 与不同 Android 设备的广泛兼容性。