Android AccessibilityService --- 小白视角

目录

前言

一、Android studio 新建项目

二、项目配置

1、新建服务:

2、新建xml配置。app--res 下新建 xml 文件夹,xml文件夹下新建 accessibility_service_config.xml,这个文件名随意起。内容示例如下:

3、注册服务、配置服务。(AndroidManifest.xml 中注册服务就不用我说了吧)  示例内容如下:

三、正主登场

1、判断当前服务开启状态。

2、跳转至设置也开启服务。

3、获取屏幕根节点。

4、通知内容的捕获,就是 event.getEventType() == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED 时获取的内容 。

5、获取 WebView 内容。



前言

朋友的一个奇思妙想,促使我接触了 AccessibilityService ,这是个什么东西呢,我们看看申请权限时系统怎么提醒的

意思吧,很清楚,就是这项服务开启之后,您的屏幕操作动作,甚至是操作的屏幕上的视图的内容,这货都能获取到,发挥一下想象力,可以搞事情!(微信自动抢红包等自动化软件就是用的这货)。


 

一、Android studio 新建项目

首先,鉴于此功能一般有开发经验的人员才会想到或者用到,所以,android studio 那一堆新建项目之类的就直接跳过了。

二、项目配置

1、新建服务:

public class AccessibilityServiceHelper extends AccessibilityService {
 
    private static final String TAG = "AccessibilityServiceHelper";
 
 
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        try {
            if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
                
            } else if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
                
            } else if (event.getEventType() == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) {
                
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
   
 
 
    @Override
    public void onInterrupt() {
 
    }
}

根据本人测试的经验解释一下以上监听的三种状态(可监听的状态不止这三种,我这里只用到了这三种,具体可看AccessibilityEvent 类下的各个状态,基本上见名知意):

  •  event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:这种状态是页面切换才会触发,我测试出来的是,必须是Activity切换才会触发,如果Activity中包含ViewPager,ViewPager中是Fragment,那么切换Fragment是不会触发此状态的。
  • event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:这种状态是监听窗口内容变化,看名字意思是窗口内容变化才会触发,但是我这里实测的是即使没看到变化也会触发,不知道是不是有肉眼看不到的变化。
  • event.getEventType() == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:通知状态变化,实测是当有弹窗和有通知栏消息时都会触发。

2、新建xml配置。app--res 下新建 xml 文件夹,xml文件夹下新建 accessibility_service_config.xml,这个文件名随意起。内容示例如下:

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:canRetrieveWindowContent="true"
    android:description="@string/accessibility_service_description"
    android:notificationTimeout="100"
    android:packageNames="com.xxx.xxx"/>
 
<!--accessibilityEventTypes:表示该服务对界面中的哪些变化感兴趣,即哪些事件通知,比如窗口打开,
滑动,焦点变化,长按等.具体的值可以在AccessibilityEvent类中查到,如typeAllMask表示接受所有的事
件通知.-->
 
<!--accessibilityFeedbackType:表示反馈方式,比如是语音播放,还是震动-->
 
<!--canRetrieveWindowContent:表示该服务能否访问活动窗口中的内容.也就是如果你希望在服务中获取
窗体内容的化,则需要设置其值为true.-->
 
<!--notificationTimeout:接受事件的时间间隔,通常将其设置为100即可.-->
 
<!--packageNames:表示对该服务是用来监听哪个包的产生的事件-->

各个属性注释都有做解释。(此文件的配置也可在 AccessibilityServiceHelper  的 onServiceConnected 进行配置)

 

3、注册服务、配置服务。(AndroidManifest.xml 中注册服务就不用我说了吧)  示例内容如下:

<service
    android:name="com.xxx.xxx.AccessibilityServiceHelper"
    android:label="自动化助手"
    android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
 
    <intent-filter>
        <action android:name="android.accessibilityservice.AccessibilityService"/>
    </intent-filter>
 
    <meta-data
        android:name="android.accessibilityservice"
        android:resource="@xml/accessibility_service_config"/>
 
</service>

CSDN这个代码编辑器太不智能了,都不会缩进,还得我一行一行调,心累。

注意:meta-data部分,android:resource 指向的就是上面 第 2 小步中的xml文件。

 

三、正主登场

1、判断当前服务开启状态。

/**
* 检测辅助功能是否开启<br>
*/
public static boolean isAccessibilitySettingsOn(Context mContext) {
    int accessibilityEnabled = 0;
    final String service = mContext.getPackageName() + "/" + 替换为自定义的服务类名.class.getCanonicalName();
    try {
        accessibilityEnabled = Settings.Secure.getInt(mContext.getApplicationContext().getContentResolver(),
                    android.provider.Settings.Secure.ACCESSIBILITY_ENABLED);
        } catch (Settings.SettingNotFoundException e) {
            
        }
    TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');
 
    if (accessibilityEnabled == 1) {
        String settingValue = Settings.Secure.getString(mContext.getApplicationContext().getContentResolver(),
                    Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
        if (settingValue != null) {
            mStringColonSplitter.setString(settingValue);
            while (mStringColonSplitter.hasNext()) {
                String accessibilityService = mStringColonSplitter.next();
                if (accessibilityService.equalsIgnoreCase(service)) {
                    return true;
                }
            }
        }
    } else {
        Log.v(TAG, "***ACCESSIBILITY IS DISABLED***");
    }
    return false;
}

 

2、跳转至设置也开启服务。

Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
startActivity(intent);

 

3、获取屏幕根节点。

在 AccessibilityServiceHelper 中的

@Override
public void onAccessibilityEvent(AccessibilityEvent event){
    
}

可以通过如下代码获取当前屏幕根节点:

AccessibilityNodeInfo accessibilityNodeInfo = accessibilityService.getRootInActiveWindow();

 

TIP 1: AccessibilityNodeInfo 有以下几个方法,用于组件的查找:

//通过屏幕显示的内容,比如:下一步、完成等字眼,获取包含该文本的所有节点
List<AccessibilityNodeInfo> nodeInfoList = rootNode.findAccessibilityNodeInfosByText(text);
 
//通过屏幕显示的view组件的id查找节点,不过前提是要知道该view的节点id,可以通过android SDK -- tools -- monitor.bat 启动 Android Device Monitor 通过捕获屏幕组件获取各个组件的信息,不过这家伙太TM不好用了,点100次也不一定能成功一次,所以建议在 AccessiblityServicerHelper 中遍历根节点,找到你要找的组件的节点,然后查看其信息,进行下一步操作
List<AccessibilityNodeInfo> nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(id);

 

有以下几个方法,用于组件的操作:

//对当前nodeInfo执行单击操作
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
 
//对当前nodeInfo执行长按操作
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);
 
//对当前nodeInfo执行复制操作
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_COPY);
 
//对当前nodeInfo执行剪切操作
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CUT);
 
//对当前nodeInfo执行粘贴操作
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_PASTE);

仅列出常用的几个,在 AccessibilityNodeInfo 中也可以找到更多操作指令。

 

TIP 2: AccessibilityService 也有一个本人比较常用的方法,用于执行全局返回操作:

//执行全局返回操作
service.performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);

在 AccessibilityService 中也可以找到更多操作指令。

 

4、通知内容的捕获,就是 event.getEventType() == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED 时获取的内容 。

//捕获的信息都在集合里了,断点看一下就知道都有啥了,后续内容处理不再赘述
List<CharSequence> text = event.getText();

5、获取 WebView 内容。

@Override
protected void onServiceConnected() {
    super.onServiceConnected();
    AccessibilityServiceInfo serviceInfo = new AccessibilityServiceInfo();
    serviceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
    serviceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
    serviceInfo.packageNames = new String[]{"com.unionpay"};// 监控的app
    serviceInfo.notificationTimeout = 100;
    serviceInfo.flags = serviceInfo.flags | AccessibilityServiceInfo.FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY;
    setServiceInfo(serviceInfo);
}

重点:serviceInfo.flags = serviceInfo.flags | AccessibilityServiceInfo.FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY;

这个设置可以让 AccessibilityService 能捕获到WebView,只有捕获到了才能拿里面的内容。

其他设置可以保持和 accessibility_service_config.xml 中的各项设置一致,不设置好像会让 accessibility_service_config.xml 对应属性失效,建议设置一遍。

 

下面是对获取到的WebView节点的实际操作:

//这里我是根据页面标题定位获取的,因为我不知道WebView的相关信息,但是我遍历根节点时拿到了页面标题的信息,然后通过节点的 getParent()方法往上找其父节点,一直找到页面根节点,然后再往下找各个子节点,直到找到需要的那个WebView节点
AccessibilityNodeInfo text = AccessibilityUtils.findViewId(this, "com.xxx.xxx:id/tv_title").get(0);
 
//找到父节点为一个FrameLayout
AccessibilityNodeInfo framelayout = text.getParent();
 
//发现FrameLayout下的第三个字节点为WebView,但此WebView节点的子节点并不是我要的内容,继续往下找
AccessibilityNodeInfo webview = framelayout.getChild(2);
 
//又是一个WebView,仍不是目标view
AccessibilityNodeInfo textwebview = webview.getChild(0);
 
//继续深入找
AccessibilityNodeInfo textwebview1 = textwebview.getChild(0);
 
//终于不是WebView了,而是一个View结合,其下有很多子view,而子view的信息正是我要找的
AccessibilityNodeInfo views = textwebview1.getChild(0);
 
//获取子view的内容
String content = views.getChild(0).getContentDescription().toString();

注释已很详细,每个要监听的app页面内容都是不一样的,这里只是一个示例,不要死板的复制,这里只是一个查找思路和方法。

至此,AccessibilityService 的相关操作就结束了,可能还有没有说到的地方,但,我相信上面所讲的已经能满足大部分需求了。

也算是呕心沥血了,认真看完必有收获。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值