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
三项内容需要明确指定,这样才能在设置页 -> 其他设置 -> 无障碍 页面中,看见你的无障碍服务。
- 绑定权限
<service
...
android:permission = "android.permission.BIND_ACCESSIBILITY_SERVICE">
...
</service>
- AccessibilityService Intent
<service>
...
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
</service>
- 元数据和指定的服务配置
</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
指定处理的无障碍事件类型。如需深入了解可以访问官方文档:
然后运行这个 App,进入到 设置 -> 其他设置 -> 无障碍 中,最底部会出现你定义的这个服务。
然后我们为无障碍服务添加一个视图。首先,创建一个 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 :
运行 app ,然后我们去 设置 -> 其他设置 -> 无障碍 中打开此服务,就会发现,在屏幕上方展示出来了一个 View:
在我的 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()
:当系统要关闭无障碍服务时,调用此方法。执行关闭流程。