AccessibilityService(无障碍辅助服务)

深入AccessibilityService使用

1、创建服务

编写自己的服务类,需要继承AccessibilityService类.其中要实现onAccessibilityEvent(AccessibilityEvent event)onInterruput()两个重要的方法:

public class RobService extends AccessibilityService {

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {   }

    @Override
    public void onInterrupt() {  }
}
   

这里我们对该类常用的方法做下说明,更详细的内容参见官方文档

方法 作用
disableSelf() 禁用当前服务,也就是在服务可以通过该方法停止运行
findFoucs(int falg) 查找拥有特定焦点类型的控件
getRootInActiveWindow() 如果配置能够获取窗口内容,则会返回当前活动窗口的根结点
getSeviceInfo() 获取当前服务的配置信息
onAccessibilityEvent(AccessibilityEvent event) 有关AccessibilityEvent事件的回调函数.系统通过sendAccessibiliyEvent()不断的发送AccessibilityEvent到此处
performGlobalAction(int action) 执行全局操作,比如返回,回到主页,打开最近等操作
setServiceInfo(AccessibilityServiceInfo info) 设置当前服务的配置信息
getSystemService(String name) 获取系统服务
onKeyEvent(KeyEvent event) 如果允许服务监听按键操作,该方法是按键事件的回调,需要注意,这个过程发生了系统处理按键事件之前
onServiceConnected() 系统成功绑定该服务时被触发,也就是当你在设置中开启相应的服务,系统成功的绑定了该服务时会触发,通常我们可以在这里做一些初始化操作

2、生命服务

像其他Service服务一样,需要在AndroidManifest.xml中声明该服务.除此之外,该服务还必须配置以下两项:

  • 配置,其name为固定的:
    android.accessibilityservice.AccessibilityService
  • 声明BIND_ACCESSIBILITY_SERVICE权限,以便系统能够绑定该服务(4.0版本后要求)

注意:任何一点配置错误,系统都无反应,因此其固定配置如下:


    
        
    

3、参数设置


在AndroidManifest.xml声明了该服务之后,接下来就是需要对该服务进行一些参数设置了.该服务能够被配置用来接受指定类型的事件,监听指定package,检索窗口内容,获取事件类型的时间等等.目前有两种配置方法:

  • 方法一:4.0之后提供了可以通过标签进行配置
  • 方法二:通过setServiceInfo()进行配置

3.1. 通过进行配置

在AndroidManifest.xml生命的的service中提供一个meta-data标签,然后通过android:resource指定相应的配置文件(在res目录下创建xml文件,并在其中创建配置文件accessibility.xml):

 
    
        
        
    
    
    

接下来我们来看accessibility.xml的相关配置:



3.2 通过setServiceInfo(AccessibilityServiceInfo info)

也可以通过setServiceInfo(AccessibilityServiceInfo)为其配置信息,除此之外,通过该方法可以在运行期间动态修改服务配置.需要注意,该方法只能用来配置动态属性:eventTypes,feedbackType,flags,notificaionTimeout及packageNames.

通常是在onServiceConnected()进行配置,如下代码:

@Override
    protected void onServiceConnected() {
        AccessibilityServiceInfo serviceInfo = new AccessibilityServiceInfo();
        serviceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
        serviceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
        serviceInfo.packageNames = new String[]{"com.tencent.mm"}; 
        serviceInfo.notificationTimeout=100;
        setServiceInfo(serviceInfo);
    }

accessibilityEventTypes:表示该服务对界面中的哪些变化感兴趣,即哪些事件通知,比如窗口打开,滑动,焦点变化,长按等.具体的值可以在AccessibilityEvent类中查到,如typeAllMask表示接受所有的事件通知.
accessibilityFeedbackType:表示反馈方式,比如是语音播放,还是震动
canRetrieveWindowContent:表示该服务能否访问活动窗口中的内容.也就是如果你希望在服务中获取窗体内容的化,则需要设置其值为true.
notificationTimeout:接受事件的时间间隔,通常将其设置为100即可.
packageNames:表示对该服务是用来监听哪个包的产生的事件

4、启动服务


当我们做完以上操作,便可将app安装到手机.安装成功后,在设置->辅助功能中便可以找到我们的服务.该服务默认处在关闭状态,需要手动开启.

5、获取事件信息


上面我们说道,onAccessibilityEvent(AccessibilityEvent event)是该服务的核心方法,其中参数event封装来自界面相关事件的信息,比如我们可以获得该事件的事件类型,进而根据起类型选择不同的处理方式:

public void onAccessibilityEvent(AccessibilityEvent event) {
        int eventType = event.getEventType();
        switch (eventType) {
            case AccessibilityEvent.TYPE_VIEW_CLICKED:
                //界面点击
                break;
            case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
                //界面文字改动
                break;
        }
    }

这里我们对AccessibilityEvent进行简单的说明:
当用户发生发生变化时,系统会发送一系列的AccessibilityEvent事件,比如按钮被点击时会发送TYPE_VIEW_CLICKED类型的事件.

方法 说明
getEventType() 事件类型
getSource() 获取事件源对应的结点信息
getClassName() 获取事件源对应类的类型,比如点击事件是有某个Button产生的,那么此时获取的就是Button的完整类名
getText() 获取事件源的文本信息,比如事件是有TextView发出的,此时获取的就是TextView的text属性.如果该事件源是树结构,那么此时获取的是这个树上所有具有text属性的值的集合
isEnabled() 事件源(对应的界面控件)是否处在可用状态
getItemCount() 如果事件源是树结构,将返回该树根节点下子节点的数量

系统不断的产生各种事件,有些是界面控件产生的,有些是系统产生的.对于由界面控件的产生的事件,通常我们将该控件称之为事件源.并不是所有的事件都能通过getSource()方法获取到事件源,比如像通知消息类型的事件(TYPE_NOTIFICATION_STATE_CHANGED).

6、获取窗口内容 


仅仅知道事件的信息是不够的,我们还希望通过事件来获取发出该事件(事件源)的信息,比如Button按钮被点击时它的text.一个服务可以配置为允许服务检索窗口内容,即获取窗口内容.整个窗口内容本质上是关于AccessibilityWindowInfo和AccessibilityNodeInfo的树结构,我称之为内容树.(类似View Tree,但由不完全相同)

需要注意,该服务可能配置了只检测了部分事件,而不是全部事件,这就意味着,当内容树发生变化后,该服务可能并不知道,即该服务无法及时的了解当前的内容树是否发生了变化.比如说,你的服务只检测了点击事件,但是此时界面的输入焦点已经变化,这样整个结点树也发生了变化,但是你的服务却不知道,此时你在结点中拿到的窗口内容可能已经不是最新的了.因此,如果你想及时的获知当前窗口的内容,那么就在配置的时候,设置监听全部事件.

正如上面所提到的,要想获取窗口内容,在配置AccessibilityService时需设置canRetrieveWindowContent为true.之后,便可以通过一下方法获取窗口内容:AccessibilityEvent.getSource(),findFocus(int),getWindow()或者getRootInActiveWindow()

7、服务的生命周期


要理解该中服务的生命周期只需要记住一下三点即可:

  • 该种服务完全由系统管理,并遵循已有的服务周期.
  • 开启一个服务只能由用户在设置中打开,而关闭则只能由用户在设置中关闭或者服务本身通过diableSelf()方法关闭(当然,现在有些第三放软件也可以强制关闭该类型服务)
  • 系统绑定该服务之后,会调用onServiceConnected()方法,这个方法可以被重写,在其中,你可以做一些初始化的操作.

8、检测服务是否开启


介绍了一些AccessibilityService的基础知识之后,再补充一点关于检测某个服务是否开启的知识.通常来说大体有一下两种方法来检测服务是否启用:

方法一:借助服务管理器AccessibilityManager来判断,但是该方法不能检测app本身开启的服务.

private boolean enabled(String name) {
        AccessibilityManager am = (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE);
        List serviceInfos = am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC);
        List installedAccessibilityServiceList = am.getInstalledAccessibilityServiceList();
        for (AccessibilityServiceInfo info : installedAccessibilityServiceList) {
            Log.d("MainActivity", "all -->" + info.getId());
            if (name.equals(info.getId())) {
                return true;
            }
        }
        return false;
    }

既然谈到了AccessibilityManager,那么在这里我们就做个简单的介绍:
AccessibilityManager是系统级别的服务,用来管理AccessibilityService服务,比如分发事件,查询系统中服务的状态等等,更多信息参考官方文档,常见方法如下:

方法 说明
getAccessibilityServiceList() 获取服务列表(api 14之后废弃,用下面的方法代替)
getInstalledAccessibilityServiceList() 获取已安装到系统的服务列表
getEnabledAccessibilityServiceList(int feedbackTypeFlags) 获取已启用的服务列表
isEnabled() 判断服务是否启用
sendAccessibilityEvent(AccessibilityEvent event) 发送事件

方法二:我们知道大部分的系统属性都在settings中进行设置,比如wifi,蓝牙状态等,而这些信息主要是存储在settings对应的的数据库中(system表和serure表),这就意味我们可以通过直接读取setting设置来判断相关服务是否开启:

private boolean checkStealFeature1(String service) {
        int ok = 0;
        try {
            ok = Settings.Secure.getInt(getApplicationContext().getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED);
        } catch (Settings.SettingNotFoundException e) {
        }

        TextUtils.SimpleStringSplitter ms = new TextUtils.SimpleStringSplitter(':');
        if (ok == 1) {
            String settingValue = Settings.Secure.getString(getApplicationContext().getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
            if (settingValue != null) {
                ms.setString(settingValue);
                while (ms.hasNext()) {
                    String accessibilityService = ms.next();
                    if (accessibilityService.equalsIgnoreCase(service)) {
                        return true;
                    }

                }
            }

实际应用

到现在有关AccessibilityService的一些知识,我们已经讲完,下面我们就看它的具体使用,其中典型的应用就是抢红包插件.

 

你可能感兴趣的:(【android奇特组件】)