>>长路漫漫立志行高远,寒夜漆漆心勇敢独行。 --致青春

前言:

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

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

 

正文:

STEP 1    Android studio 新建项目

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

STEP 2    项目配置

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"?>

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

 

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

                    

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

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

 

STEP 3    正主登场

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 中的

@Overridepublic 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 内容。

@Overrideprotected 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);//找到父节点为一个FrameLayoutAccessibilityNodeInfo framelayout = text.getParent();//发现FrameLayout下的第三个字节点为WebView,但此WebView节点的子节点并不是我要的内容,继续往下找AccessibilityNodeInfo webview = framelayout.getChild(2);//又是一个WebView,仍不是目标viewAccessibilityNodeInfo 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 的相关操作就结束了,可能还有没有说到的地方,但,我相信上面所讲的已经能满足大部分需求了。

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

更多相关文章

  1. Android(安卓)系统Recovery工作原理
  2. Android之数据库Greedao的使用
  3. Android应用程序包解析过程浅析
  4. Android实现一键清除输入内容和震动效果的EditText
  5. Android来电和短信操作
  6. android 蓝牙隐藏对话框 后台配对
  7. android NoticificationManager状态栏操作
  8. Android(安卓)解决 HorizontalScrollView 里的内容滑动不全的问
  9. Android中EditText实现不可编辑解决办法

随机推荐

  1. 判断变量是否是DataFrame 或者 Series
  2. Python学习之路:函数的非固定参数
  3. python编程从入门到实战1-3章
  4. 简谈-如何将图片下载到本地
  5. [D]用python提取多段字符串该怎么写正则
  6. Python实现快速傅里叶变换(FFT)
  7. Python出现Could not find a version tha
  8. python之选课系统详解[功能未完善]
  9. Python是如何进行内存管理的
  10. Python内置函数介绍