AccessibilityService讲解

本文详细介绍了AccessibilityService的原理与使用方法,包括其生命周期、配置、事件处理等方面,并探讨了其在实际应用中的多种可能性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.概念

AccessibilityService它运行在后台,而且当AccessibilityEvents事件被触发的时候系统会自动回调AccessibilityService中的相关方法。这些AccessibilityEvents事件表示用户界面上的一些状态的改变,例如:焦点发生变化,一个button被点击等等。AccessibilityService可以根据自己的需要查询活动窗口的内容(也就是说,界面中产生的任何变化都会产生一个时间,并由系统通知给AccessibilityService.这就像监视器监视着界面的一举一动,一旦界面发生变化,立刻发出警报。)我们要开发AccessibilityService需要继承AccessibilityService类并且实现里面的抽象方法即可。

1.5使用AccessibilityService类步骤

别看我下面写这么多,其实AccessibilityService用起来非常简单步骤如下:

1.继承AccessibilityService类,重写里面的方法(下面有介绍)
2.在AndroidMainifest中注册(具体使用参考下面第3条)
3.添加配置文件(具体使用参考下面第4条)

下面我们对继承AccessibilityService类里面需要重写的几个重要方法进行简单介绍:

1.onAccessibilityEvent()必须重写。通过这个函数可以接收系统发送来的AccessibilityEvent,接收来的AccessibilityEvent是经过过滤的,过滤是在配置工作时设置的。  
2.onInterrupt()必须重写。这个在系统想要中断AccessibilityService返给的响应时会调用。在整个生命周期里会被调用多次。
3.onServiceConnected()可选。在系统成功连接上这个AccessibilityService会调用。在这个方法里你可以做一下初始化工作。
4.onUnbind()可选。在系统将要关闭这个AccessibilityService会被调用。在这个方法中进行一些释放资源的工作。

代码如下:

public class RobService extends AccessibilityService {
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {}
    @Override
    public void onInterrupt() {}
    @Override
    protected void onServiceConnected() { }
 }

下面我们对继承AccessibilityService类里面需要重写的几个重要方法进行简单介绍:

1.onAccessibilityEvent()必须重写。通过这个函数可以接收系统发送来的AccessibilityEvent,接收来的AccessibilityEvent是经过过滤的,过滤是在配置工作时设置的。  
2.onInterrupt()必须重写。这个在系统想要中断AccessibilityService返给的响应时会调用。在整个生命周期里会被调用多次。
3.onServiceConnected()可选。在系统成功连接上这个AccessibilityService会调用。在这个方法里你可以做一下初始化工作。
4.onUnbind()可选。在系统将要关闭这个AccessibilityService会被调用。在这个方法中进行一些释放资源的工作。

2.生命周期

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

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

3.声明AccessibilityService

声明一个AccessibilityService和在menifest中声明其他service一样,但是必须做2件事情:

1.配置<intent-filter>,其name为固定的

2.声明BIND_ACCESSIBILITY_SERVICE权限,以便系统能够绑定该服务(4.1版本后要求)

注意:任何一点配置错误,系统都检测不到该服务,因此在menifest中其固定配置如下:

 <service android:name=".MyAccessibilityService"
    android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
 <intent-filter>
    <action android:name="android.accessibilityservice.AccessibilityService" />
 </intent-filter>
 . . .
 </service>

4.配置

官网是这样解释的:

AccessibilityService可以添加一些配置信息,目的是接收一些特定的事件.例如:监听特定的包、检索windows内容、在给定的时间范围内只允许从每种类型获取事件一次等等。

android给我们提供2种配置方法:

方法1:meta-data标签方式:

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

 <service android:name=".MyAccessibilityService">
     <intent-filter>
     <action android:name="android.accessibilityservice.AccessibilityService" />
    </intent-filter>
    <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice" />
</service>

接下来我们看看accessibilityservice.xml中有什么信息:

    <?xml version="1.0" encoding="utf-8"?>
    <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
        android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged|typeWindowContentChanged"
        android:accessibilityFeedbackType="feedbackGeneric"
        android:accessibilityFlags="flagDefault"
        android:canRetrieveWindowContent="true"
        android:notificationTimeout="100"
        android:packageNames="com.tencent.mm" />

注意:此方法可以设置所有属性。我们在只需要仿照该配置文件根据自己的需求进行修改即可.

方法2:调用setServiceInfo(AccessibilityServiceInfo)方式:

注意

1.这个方法可以在任何时候调用,动态的去改变service配置信息
2.这个方法只能用来配置动态属性,如:eventTypes,feedbackType,flags,notificationTimeout,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);
}

在这里涉及到了AccessibilityServiceInfo类,做个说明:
AccessibilityServiceInfo该类被用于配置AccessibilityService信息,该类中包含了大量用于配置的常量字段及用来xml 属性,比如常见的android:accessibilityEventTypes,android:canRequestFilterKeyEvents,android:packageNames等等,更多信息参见官方文档

这里,简单的对重要属性进行说明:

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

5.检索窗口内容

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

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

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

6.启动服务

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

7.获取事件信息

上面我们说道,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).

**注意:**onAccessibilityEvent回调函数非常重要,当我们注册了监听事件的时候,当有事件发生就会通知我们这个函数,但是一定要注意这个函数通知是异步的,

8. 检测服务是否开启

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

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

private boolean enabled(String name) {
    AccessibilityManager am = (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE);
    List<AccessibilityServiceInfo> serviceInfos = am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC);
    List<AccessibilityServiceInfo> 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;
                }

            }
        }

9.我们把AccessibilityServic用歪了

官网上明确说明了,AccessibilityServic是用来帮助残障人使用的。google大神们的想法是非常好的,但是现在对它的使用越来越偏离了它的设计初衷。AccessibilityServic用途非常广泛,下面我们就来总结一下:
1.apk自动安装。
2.微信抢红包和自动回复
3.防卸载自己的app
4.监控密码框(呵呵,这个你真想多了,系统还算有点良心,所有的设置为password类型的EditText都是无法被监控的。)
5.浏览器地址栏劫持、搜索劫持、桌面点击劫持以及防卸载等(参考这篇文章

参考文献

1.深入了解AccessibilityService

2.官网

3.AccessibilityService从入门到出轨

结尾

AccessibilityServic是一把双刃剑,希望大家能正确对待它。

自己在技术上依旧是个小渣渣,加油勉励自己!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序编织梦想

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值