Android构建Accessibility Service

前言

    构建Android的AccessibilityService主要就是围绕android自带的AccessibilityService类,该类接受由系统发出的一些事件(AccessibilityEvent),这些事件代表着用户界面的变化,如焦点改变、输入输出内容变化等;通过获取这些事件并针对不同事件进行不同的逻辑处理,这个就是构建AccessibilityService的基本原理。TalkBack就是基于此类展开的。

服务的声明与权限

1.AccessibilityService声明

    应用提供的AccessibilityService必须在清单文件中进行声明,这样才能被Android系统作为AccessibilityService处理。在普通的service基础上,需要在intent-filter下声明一个AccessibilityService的action,同时需要声明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>
        <!-- 声明该服务是一个AccessibilityService -->
        <action android:name="android.accessibilityservice.AccessibilityService" />
      </intent-filter>
    </service>
    <uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
  </application>
</manifest>
2.Accessibility Service的配置

    除了在清单文件中声明和设置权限,还需要对Accessibility Service进行配置(configuration),用于指定当前的Accessibility Service对哪些AccessibilityEvent进行处理,以及当前Accessibility Service的一些信息。配置这些信息的方式有两种,一种是在程序运行的时候通过AccessibilityServiceInfo类中的setServiceInfo方法进行配置,这种配置方式的局限性在于不是所有的属性都可以通过该方法进行配置。从Android4.0开始,还可以通过在清单文件中设置标签来指定配置文件,这种方式可以配置service的所有参数,示例如下:

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

这种方式配置configuration可以设置所有的参数,参数设置的文件指定到了一个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|flagRequestTouchExplorationMode|flagRequestEnhancedWebAccessibility
    |flagReportViewIds|flagRequestFilterKeyEvents"
    android:accessibilityFeedbackType="feedbackSpoken|feedbackHaptic|feedbackAudible"
    android:notificationTimeout="100"
    android:canRetrieveWindowContent="true"
    android:settingsActivity="com.example.android.accessibility.ServiceSettingsActivity"
/>

各参数解释如下:
- description: 对所要配置的Accessibility Service的简短描述,类型为reference
- packageNames: 指定需要监听AccessibilityEvent的应用包名,多个包名中间用”,”隔开,如果不指定则会监听所有的应用。类型为reference或者字符串
- accessibilityEventTypes: 指定要配置的Accessibility Service要接收的AccessibilityEvent事件类型,当需要接收多个事件时用”|”隔开,具体的事件类型AccessibilityEvent类,另外该参数可以也可以通过AccessibilityServiceInfo.setServiceInfo()方法进行动态添加。示例中的typeAllMask代表接收所有事件类型。
- accessibilityFlags: 向配置的Accessibility Service添加额外的标识,多个标识之间用”|”隔开,具体的类型参照AccessibilityServiceInfo类,该参数可以也可以通过AccessibilityServiceInfo.setServiceInfo()方法进行动态添加,示例中的参数含义为:
- flagDefault:If an AccessibilityService is the default for a given type. Default service is invoked only if no package specific one exists. In case of more than one package specific service only the earlier registered is notified. (官方解释,没完全明白- -!)
- flagRequestTouchExplorationMode:这个Flag请求系统进入触摸探测模式,在此模式下,单指滑动触摸屏幕将变成一个悬浮于用户界面的点,类似电脑桌面的鼠标,同时该模式下系统也能检测到特定的手势从而激活服务。需要注意的是,至少要有一个Accessibility Service设置了该标志,系统才能被请求进入到该模式中。
- flagRequestEnhancedWebAccessibility:该标识用于增强web浏览对无障碍的支持
- flagReportViewIds:这个标识用于请求AccessibilityService获取的AccessibilityServiceInfo中包含有源视图(the source view)的ID,这个标识默认不会设置。
- flagRequestFilterKeyEvents:这个标识请求监听手机的KeyEvent事件,如果AccessibilityService设置了该标识,则AccessibilityService会优先于应用接收到KeyEvent事件。为了避免用户混乱,只有一个AccessibilityService能够接收到KeyEvent(如果两个或多个都设置该标识)。
- accessibilityFeedbackType: 设置当前配置服务的反馈类型,多个中间同样用”|”隔开,具体的类型参照AccessibilityServiceInfo类,该参数同样也可以通过AccessibilityServiceInfo.setServiceInfo()方法进行动态添加,示例中的参数含义如下:
- feedbackSpoken:通过语音播报反馈
- feedbackHaptic:通过触觉反馈
- feedbackAudible:通过声音(非语音)反馈
- notificationTimeout: 接收AccessibilityEvent的最小间隔时长,必须为int值,单位毫秒;这个值也可以在代码中动态设置跟修改,如示例中的100表示当服务接收了一个AccessibilityEvent之后,在接下来的100毫秒内将不会再接收AccessibilityEvent事件。
- canRetrieveWindowContent: 该标识表示配置的服务能否检索活动窗口的内容,只能为boolean值,该参数不能在代码中动态设置。
- settingsActivity: 指定设置该服务的activity,这个参数不能在程序运行时进行设置。

3.注册AccessibilityEvent事件

    注册AccessibilityEvent即设置之前的accessibilityEventTypes参数。在这里需要指出的是,尽可能的只注册需要用到的事件,假如说注册了一些不必要的事件并将其消费,很可能会导致其他的AccessibilityService接收不到这个AccessibilityEvent。此外,当多个AccessibilityService注册了相同的AccessibilityEvent,但反馈的类型(feedbackType)不同时,Android的framework层将会把同一个AccessibilityEvent分发给这些反馈类型不同的AccessibilityService,但如果两个Service的反馈类型相同,则只会分发给第一个注册的服务。

4.AccessibilityService中的主要方法

    一个Accessibility Service必须继承AccessibilityService类,并重写以下几个方法(这些方法按顺序执行):
- onServiceConnected() (可选)当系统成功连接上该服务时将会调用该方法。使用这个方法可以进行任何一次性设置的操作,包括关联用户的反馈服务例如音频管理或者震动等等。如果希望动态设置Service属性的也可以在此时设置。
- onAccessibilityEvent() (必须重写)这个方法是系统在接收到指定的AccessibilityEvent之后的回调,方法参数中携带有AccessibilityEvent,服务在获取到指定的Event后可以根据具体事件类型进行不同的逻辑处理,并反馈给用户。
- onIntrrupt() (必须重写)当系统准备中断当前服务的用户反馈时会调用此方法,通常是响应用户的动作如焦点切换
- onUnbind() (可选)该方法在系统准备关闭AccessibilityService时调用,使用这个方法可以关闭一些资源如相关联的服务等。

5.获取Event事件细节

    Android系统通过AccessibilityEvent对象向服务提供指定的监听事件。在Android4.0版本之前,系统向服务所提供的Event是限制的上下文内容,这样就有可能导致不能够获取到准确的信息。例如,在一个包含有具体时间和年月日的日期中,用户得到的反馈结果可能只有具体的时间,而年月日等信息都获取不到,这会对用户造成很大的困扰。

    Android4.0版本之后,AccessibilityEvent可以获取到更多的事件信息,其中很大的原因在于4.0版本之后系统传递给服务的AccessibilityEvent事件是基于所监听组件的视图结构层(View hierarchy)信息,这样就可以准确的获取到应用的很多信息,如当前控件的父控件,它的子控件,控件本身的描述信息以及文本信息等等。通过这种方式,系统就可以向AccessibilityService提供更加丰富的信息和细节。AccessibilityService通过onAccessibilityEvent()方法来获取系统传递的AccessibilityEvent事件,android4.0以下的版本可以使用support包下的AccessibilityEventCompat兼容类获取同样的信息。可以通过以下方法来获取额外的事件信息:
- AccessibilityEvent.getRecordCount() 和 AccessibilityEvent.getRecord(index) 通过这两个方法可以获取到对应事件中AccessibilityRecord对象的数量以及每个AccessibilityRecord对象,在AccessibilityRecord对象中提供了更多的信息,可以供服务使用。(这是官方文档的说法,实际上AccessibilityEvent是AccessibilityRecord的子类,所以AccessibilityRecord中有的信息在AccessibilityEvent中也可以使用- -!)
- AccessibilityEvent.getResource() 这个方法可以获取AccessibilityNodeInfo对象,也就是获取到当前的event事件的源在视图层中的节点信息,通过该节点信息可以探究整个视图层。

注意: 如果想要通过AccessibilityEvent事件探究整个视图层的情况,当前的AccessibilityService需要有足够的权限,所以在配置服务的XML文件时,必须将canRetrieveWindowContent参数设置为true,否则获取AccessibilityNodeInfo将会失败。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值