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 AccessibilityService --- 小白视角_第1张图片

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


 

一、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,这个文件名随意起。内容示例如下:



 

 

 

 

 

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

 

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


 
    
        
    
 
    
 

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

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

 

三、正主登场

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

/**
* 检测辅助功能是否开启
*/ 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 nodeInfoList = rootNode.findAccessibilityNodeInfosByText(text);
 
//通过屏幕显示的view组件的id查找节点,不过前提是要知道该view的节点id,可以通过android SDK -- tools -- monitor.bat 启动 Android Device Monitor 通过捕获屏幕组件获取各个组件的信息,不过这家伙太TM不好用了,点100次也不一定能成功一次,所以建议在 AccessiblityServicerHelper 中遍历根节点,找到你要找的组件的节点,然后查看其信息,进行下一步操作
List 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 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 的相关操作就结束了,可能还有没有说到的地方,但,我相信上面所讲的已经能满足大部分需求了。

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

你可能感兴趣的:(脚本辅助开发,android,java,安卓)