Android 自定义无障碍服务基础使用与概念

1,139 阅读4分钟

Android 无障碍服务是为了解决用户在不方便操作设备的时候,也能够使用设备和应用程序。例如,通过将文本转换为语音或在用户屏幕上方悬停的方式,提供触觉反馈。

什么是无障碍服务?

无障碍服务可以帮助残障用户使用 Android 设备和应用程序。这是一项长期运行的、具有特权的服务,可以帮助用户处理屏幕上的信息,并让他们与设备进行有意义的交互。 常见的无障碍服务有:

  • Swich Access:通过按键进行与设备的交互。
  • Volume Access:允许行动受限的 Android 用户通过语音命令控制设备。
  • Talkback,视障或盲人常用的屏幕阅读器。

定义和启动无障碍服务

无障碍服务必须拓展 AccessibilityService 类,并实现它的一些方法:

class AccessibilityDemoService: AccessibilityService() {
    override fun onAccessibilityEvent(event: AccessibilityEvent?) {}
    override fun onInterrupt() {}
}

然后在 AndroidManifest.xml 中,定义无障碍 Service 的标签:

<!--无障碍服务声明-->
<service android:name=".service.AccessibilityDemoService"
    android:label="AccessibilityDemoService"
    android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
    android:exported="false">
    <intent-filter>
        <action android:name="android.accessibilityservice.AccessibilityService"/>
    </intent-filter>
    <meta-data
        android:name="android.accessibilityservice"
        android:resource="@xml/accessibility_service_config"/>
</service

三项内容需要明确指定,这样才能在设置页 -> 其他设置 -> 无障碍 页面中,看见你的无障碍服务。

  1. 绑定权限
<service
    ...
    android:permission = "android.permission.BIND_ACCESSIBILITY_SERVICE">
    ...             
</service>
  1. AccessibilityService Intent

<service>
    ...  
    <intent-filter>
       <action android:name="android.accessibilityservice.AccessibilityService" />
    </intent-filter>
</service>
  1. 元数据和指定的服务配置
</service>
	...
	<meta-data
           ...
           android:resource="@xml/accessibility_service_config" />
</service>

android:resource 指定的 xml 文件中配置一些服务相关的属性:

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFlags="flagDefault"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:notificationTimeout="100"
    android:canRetrieveWindowContent="true"
    android:canPerformGestures="true"
    android:description="@string/accessibility_service_description" />

这些属性会控制一些服务的行为。比如可以通过 accessibilityEventTypes 指定处理的无障碍事件类型。如需深入了解可以访问官方文档:

developer.android.google.cn/guide/topic…

然后运行这个 App,进入到 设置 -> 其他设置 -> 无障碍 中,最底部会出现你定义的这个服务。

Screenshot_2022-04-12-21-13-07-54.jpg

然后我们为无障碍服务添加一个视图。首先,创建一个 acuton_bar.xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
	  <!--  自定义布局内容  -->
</LinearLayout>

然后在我们自定义 Service 中使用:

class AccessibilityDemoService: AccessibilityService() {
	  // ...
    lateinit var mLayout: FrameLayout
}

onServiceConnected() 中,通过 WindowManager 去添加这个 View:

private fun initView() {
    // 在屏幕顶部添加一个 View
    val wm = getSystemService(WINDOW_SERVICE) as? WindowManager
    mLayout = FrameLayout(this)
    val lp = WindowManager.LayoutParams().apply {
        type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY // 因为此权限才能展示处理, 只能配合 AccessibilityService 使用
        format = PixelFormat.TRANSLUCENT
        flags = flags or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
        width = WindowManager.LayoutParams.WRAP_CONTENT
        height = WindowManager.LayoutParams.WRAP_CONTENT
        gravity = Gravity.TOP
    }
    val inflater = LayoutInflater.from(this@AccessibilityDemoService)
    inflater.inflate(R.layout.action_bar, mLayout)
    wm?.addView(mLayout, lp)
}

然后我们更改运行设置,将默认启动 Activity 改为 nothing :

33517640-ADD8-4E50-9E5E-2DD52E56E641.png

运行 app ,然后我们去 设置 -> 其他设置 -> 无障碍 中打开此服务,就会发现,在屏幕上方展示出来了一个 View:

Screenshot_2022-04-12-21-40-22-01.jpg

在我的 action_bar.xml 中,自定义了四个按钮,接下来为按钮添加点击事件,以电源按钮为例,可以直接通过 performGlobalAction(int) 方法直接出发电源按钮的逻辑:

private fun configPowerButton() {
    val powerBtn = mLayout.findViewById<Button>(R.id.power)
    powerBtn.setOnClickListener {
        performGlobalAction(GLOBAL_ACTION_POWER_DIALOG)
    }
}

并把这个方法在 onServiceConnected() 中调用即可。同样的,其他按钮也可根据不同的逻辑实现。

无障碍服务处理的生命周期

在实现 AccessibilityService 的时候,需要实现一些方法,这些方法是无障碍服务运行期间接收事件的重要方法。与普通的 Service 不同的是无障碍服务,没有对子类暴露 onBind 方法,因为它在内部做了实现逻辑;其他普通 Service 生命周期方法都有,但在整体的生命周期上,与常规的方法有些不同。 AccessibilityService 中根据 Android 系统调用它的方法顺序,有四个方法,三个生命周期阶段:

  • 服务启动时 onServiceConnected() :当系统成功连接到无障碍服务时,调用此方法。在这个方法中,可以对服务配置进行修改,最后一定要通过调用 setServiceInfo() 使配置生效。
  • 运行时 在运行时,有两个可配置方法:onAccessibilityEvent(event: AccessibilityEvent?)onInterrupt()onAccessibilityEvent(AccessibilityEvent?) 是必须实现的,当系统检测到与无障碍服务指定的事件过滤参数匹配的 AccessibilityEvent 时,会回调此方法;此方法在服务的整个生命周期中会多次被调用。 onInterrupt() :也是一个必须实现的方法,当系统需要中断服务正在提供的反馈时,会调用此方法(通常是为了响应将焦点移到其他控件等用户操作);此方法在服务的整个生命周期中会多次被调用。
  • 关闭时 onUnbind() :当系统要关闭无障碍服务时,调用此方法。执行关闭流程。