构建无障碍服务

无障碍服务是一个为残疾人或可能暂时无法与设备完全互动的人提供用户界面扩展功能的应用程序。例如,在开车、照顾孩子或参加一个非常喧闹的宴会时,用户可能需要另外的交互反馈作为补充或替代。

Android提供了标准的无障碍服务,包括TalkBack,开发人员可以创建和发布自己的服务。 本文档介绍了构建无障碍服务的基本知识。

构建和部署无障碍服务的能力在Android 1.6(API等级4)被推出,并在Android 4.0(API等级14)明显改善其功能。Android支持库也被更新,Android 4.0发布的支持库可以使得这些无障碍特性也能支持android 1.6。鼓励那些致力于实现广泛可兼容的无障碍服务开发者们使用支持库,并且在Android4.0版本中开发更多先进的无障碍特性。

Manifests声明和权限

提供无障碍服务的应用,必须在应用的声明(manifests)文件中明确声明,以便android系统把此应用程序作为无障碍服务处理。本节介绍无障碍服务的必需和可选设置。

无障碍服务声明

为了能被看作一个无障碍服务,必须在声明文件中的应用程序(application)元素中包含服务(service)元素(而不是活动(activity)元素)。此外,在服务(service)元素内,还必须包括无障碍服务的目标过滤器。为了兼容Android 4.1及更高版本的系统,声明文件中必须请求BIND_ACCESSIBILITY_SERVICE的权限,如下例所示:

<manifest>
  ...
  <uses-permission ... />
  ...
  <application>
    ...
    <service android:name=".MyAccessibilityService"
        android:label="@string/accessibility_service_label"
        android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
      <intent-filter>
        <action android:name="android.accessibilityservice.AccessibilityService" />
      </intent-filter>
    </service>
    <uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
  </application>
</manifest>

在Android 1.6(API等级4)和更高版本的系统中,所有无障碍服务都需要部署这些声明。

无障碍服务配置

无障碍服务还必须提供一个配置,该配置指定了无障碍服务处理的无障碍事件类型和有关无障碍服务的附加信息。无障碍服务的配置被包含在AccessibilityServiceInfo类中。无障碍服务可以使用该类的实例创建一个配置,并在运行时使用setServiceInfo()设置此配置。但是,使用该方法并不能配置所有的配置项。

从Android 4.0开始,可以在声明文件中包含一个<meta-data>元素,该元素指向一个配置文件。该配置文件允许开发者设置无障碍服务的所有内容,具体如下例所示:

<service android:name=".MyAccessibilityService">
  ...
  <meta-data
    android:name="android.accessibilityservice"
    android:resource="@xml/accessibility_service_config" />
</service>

该meta-data元素指向的是开发者在应用资源目录下创建的XML文件(<project_dir>/res/xml/accessibility_service_config.xml)。下面的代码展示了无障碍服务配置文件的样例:

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/accessibility_service_description"
    android:packageNames="com.example.android.apis"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFlags="flagDefault"
    android:accessibilityFeedbackType="feedbackSpoken"
    android:notificationTimeout="100"
    android:canRetrieveWindowContent="true"
    android:settingsActivity="com.example.android.accessibility.ServiceSettingsActivity"
/>

无障碍服务配置文件使用的XML属性,点击以下链接查看更多信息:

哪些配置设置可以在运行时动态地设置的详细信息,请参阅AccessibilityServiceInfo参考文档。

注册无障碍事件

无障碍服务配置参数最重要的功能之一是允许开发者指定无障碍服务能处理的无障碍事件类型。成功指定该信息使无障碍服务间能相互合作,并允许开发者灵活地处理特定应用的特定事件类型。事件过滤可以包含以下规则:

  • 包名(PackageNames): - 指定想要无障碍服务处理的无障碍事件的应用程序包名。如果省略该参数,无障碍服务将被视为服务于任何应用程序的无障碍事件。在无障碍服务配置文件中,该参数可使用android:packageNames属性被设置为一个逗号分隔的列表,或使用AccessibilityServiceInfo.packageNames成员设置。
  • 事件类型(Event Types) - 指定开发者想要无障碍服务处理的无障碍事件的类型。在无障碍服务配置文件中,该参数可使用android:accessibilityEventTypes属性设置为由“ | ”分隔的列表(例如accessibilityEventTypes="typeViewClicked|typeViewFocused"),或者使用AccessibilityServiceInfo.eventTypes成员设置。

当创建无障碍服务时,开发者需要全面考虑无障碍服务能处理的无障碍事件,并只注册这些事件。因为用户可能同时激活多个无障碍服务,开发者创建的服务不能浪费在该服务不能处理的事件上。记住,其他的服务也可能会为了提供用户体验来处理这些事件。

注:如果服务提供不同反馈类型( feedback types),Android框架可以为无障碍事件分派一个以上的无障碍服务。但是,如果为两个或更多的服务提供相同的反馈类型,只有第一个注册的服务才能接收事件。

无障碍服务方法

一个无障碍服务必须继承AccessibilityService类,重写该类的以下方法。这些方法被Android系统调用时按顺序呈现,从服务被启动(onServiceConnected()),一直运行(onAccessibilityEvent()onInterrupt()),到服务被关闭(onUnbind())。

  • onServiceConnected()
    -(可选)当系统成功连接到无障碍服务时,调用该方法。使用该方法为无障碍服务提供任何一次性的启动步骤,包含连接到用户反馈系统服务,例如音频管理或设备震动。如果想要在应用运行时设置无障碍服务的配置或做一次性的调整,可以在该方法中调用setServiceInfo()来实现。
  • onAccessibilityEvent()
    -(必选)当系统检测到一个符合无障碍服务设定的无障碍事件过滤参数的事件时,该方法被系统回调。例如,当用户在应用程序中点击一个按钮或聚焦一个用户界面控件时,你的无障碍服务会为此应用程序提供反馈。此时,系统调用这个方法,传递相关联的无障碍事件,无障碍服务就可以翻译和使用该事件为用户提供反馈。在无障碍服务的生命周期中,该方法会被调用多次。
  • onInterrupt()
    - (必选)当系统想要打断无障碍服务提供的反馈时该方法被调用,一般情况下是响应用户操作,如移动焦点到一个不同的控件。在无障碍服务的生命周期中,该方法会被调用很多次。
  • onUnbind()
    -(可选)当系统想要关闭无障碍服务的时候,调用该方法。使用该方法去完成一次性的关闭进程,包括解除分配用户反馈系统服务,例如音频管理或者设备震动。

这些回调方法为无障碍服务提供基本架构。在AccessibilityEvent对象中怎样处理由Android系统提供的数据并为用户提供反馈,是开发者自己决定的。获取无障碍事件信息的更多内容,详见实现无障碍(Implementing Accessibility)培训。

获得事件详情

Android系统通过AccessibilityEvent对象为无障碍服务提供用户界面交互的信息。在Android4.0之前,可从无障碍事件中获得信息,且无障碍事件能够提供用户所选择的界面控件的大量详细信息,但这些信息仅包含有限的上下文信息。在很多情况下,丢失的上下文信息可能是理解已选定控件的意义的关键。

一个界面的上下文是很关键,例如,日历或每日计划。如果用户选择周一到周五的下午四点,无障碍服务告知下午四点,但是没有说明是哪个工作日,哪个月的哪一天,反馈结果就会令人非常困扰。在这种情况下,用户界面控件的上下文对那些做时间规划的人非常关键。

Android4.0较大的扩展了无障碍服务能获取到的关于用户界面交互的信息量,这些信息是通过基于视图层次组合的无障碍事件提供。视图层次是用户界面元素的组合,该组合包含组件(父亲)和可能包含在其中的子元素(孩子)。因此,Android系统可以提供无障碍事件的更丰富的详细信息,允许无障碍服务为用户提供更多有用的反馈。

系统将AccessibilityEvent传递到无障碍服务的onAccessibilityEvent()回调方法,因此,无障碍服务获得用户交互事件的信息。AccessibilityEvent会提供事件的详细信息,包含AccessibilityEvent的执行类型、描述文本和其他信息。从Android4.0开始(Android4.0以前的支持情况详见支持库(Support Library)中的AccessibilityEventCompat),可以通过调用以下方法获得更多信息。

为用户采取措施

从Android4.0(API级别14)开始,无障碍服务可以代表用户操作,包含改变输入焦点和选择(激活)用户界面元素。在Android4.1(API级别16),操作的范围被扩展至包含滚动列表和与文本域交互。无障碍服务也可采取全局操作,如导航到主界面、按返回按钮、打开屏幕通知和最近应用列表。Android4.1也包含新焦点类型,无障碍焦点,该焦点类型可让所有视觉元素能够被无障碍服务所选择。

这些新的能力让开发者能够开发替代导航模式,如手势导航,提高残障用户对Android设备的控制。

手势监听

无障碍服务可以监听特定手势,并代表用户响应。该特性被添加在Android4.1(API级别16)版本中,要求无障碍服务激活触摸浏览特性。无障碍服务可以通过设置AccessibilityServiceInfo实例的flags成员为FLAG_REQUEST_TOUCH_EXPLORATION_MODE,请求该特性的激活,样例如下所示:

public class MyAccessibilityService extends AccessibilityService {
    @Override
    public void onCreate() {
        getServiceInfo().flags = AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE;
    }
    ...
}

一旦无障碍服务请求激活触摸浏览,如果该特性未打开,用户必须允许打开该特性。当该特性激活,无障碍服务将会通过onGesture()回调方法接收无障碍手势的通知,并且为用户响应操作。

使用无障碍操作

无障碍服务可以代替用户操作,让用户与应用的交互更简单有效。无障碍服务的这个能力在Android4.0(API级别14)被加入,在Android4.1(API级别16)被显著扩展。

为了代替用户操作,无障碍服务必须注册来接收来自某些或者更多应用中的事件,并在无障碍服务配置文件中设置android:canRetrieveWindowContent属性为true,来请求查看应用程序的内容。当接收到事件,无障碍服务可以使用getSource()方法在事件中检索AccessibilityNodeInfo对象。使用AccessibilityNodeInfo对象你的无障碍服务可以浏览视图层次来判定采取什么操作并使用performAction()为用户操作。

public class MyAccessibilityService extends AccessibilityService {

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
       // 获得事件的资源节点
        AccessibilityNodeInfo nodeInfo = event.getSource();

        //使用事件和节点信息判定为用户执行什么样的操作。
        nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);

        //循环nodeInfo对象
        nodeInfo.recycle();
    }
    ...
}

performAction()方法允许无障碍服务在应用中执行操作。如果无障碍服务需要去执行全局操作,例如导航到主屏幕、按返回键、打开屏幕通知或者最近应用列表等,使用performGlobalAction()方法。

使用焦点模式

Android4.1(API级别16)引入了一种新的用户界面焦点,被称为无障碍焦点(Accessibility Focus)。这种类型的焦点可用于在无障碍服务中,选择任何视觉用户界面元素并操作该元素。该焦点类型不同于输入焦点(Input Focus)。当用户输入字符,点击键盘上的enter键或D-pad控制器的中心按钮时,输入焦点决定屏幕上哪个元素接收输入。

无障碍焦点与输入焦点是完全分开和独立的。事实上,用户界面中的一个元素有输入焦点,同时另一个元素有无障碍焦点,这种情况是可能出现的。无障碍焦点的目的是,为无障碍服务提供一种与屏幕上视觉元素进行交互的方法,而不管该元素是否是系统层面的可输入聚焦元素。开发者可以在无障碍手势测试中看到无障碍焦点。更多关于该特性的信息,详见测试手势导航

注:当元素具有输入焦点,使用无障碍焦点的无障碍服务有责任整合当前输入焦点。如果服务没有整合输入焦点和无障碍焦点,有可能会在应用中引起问题,问题是当采用特定操作时,需要将输入焦点固定在特定的位置。

无障碍服务可以使用AccessibilityNodeInfo.findFocus()方法来判定哪些用户界面元素有输入焦点或无障碍焦点,也可以使用focusSearch()方法来检索哪些元素可以用输入焦点选择。最后,可以使用performAction(AccessibilityNodeInfo.ACTION_SET_ACCESSIBILITY_FOCUS)方法来设置无障碍焦点。

样例代码

API Demo项目包含两个样例,可以用来作为生成无障碍服务的起点 (<sdk>/samples/<platform>/ApiDemos/src/com/example/android/apis/accessibility):