导读:

资深的安卓程序员想必都了解,安卓的通知监听服务(NotificationListenerService)可以监听通知栏的信息,从通知栏信息里获取到我们想要的收款信息(比如收款类型、收款金额)。但是,这个通知监听服务有个弊端,如果APP没有发送通知,那就没办法知道有没有收到款,特别是现在的微信 和QQ,二维码收款不再发送通知,而是在自身的APP里给出提示,这样就没办法使用通知监听服务了。因此,我们需要使用安卓的无障碍服务(AccessibilityService),来监听APP界面的变化,实时获取微信、QQ的收款信息。

准备工作:

  • Android Studio开发工具;
  • 电脑安装安卓模拟器(夜神模拟器或者逍遥模拟器),模拟器里安装好微信、QQ;

步骤:

一、创建服务

1、新建一个Service类,继承自AccessibilityService,重写onServiceConnected()方法、onAccessibilityEvent()方法和onInterrupt()方法

package net.zy13.skhelper.service;import android.accessibilityservice.AccessibilityService;
import android.view.accessibility.AccessibilityEvent;
import net.zy13.skhelper.utils.LogUtil;
/*** 收款无障碍服务*/
public class SkAccessibilityService extends AccessibilityService{/*** 当服务启动的时候会被调用*/@Overridepublic void onServiceConnected(){LogUtil.debug( ":无障碍服务连接成功");}/*** 监听窗口变化的回调*/@Overridepublic void onAccessibilityEvent(AccessibilityEvent event) {// 获取事件类型,在对应的事件类型中对相应的节点进行操作int eventType = event.getEventType();//根据事件回调类型进行处理switch (eventType) {//当通知栏发生改变时case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:break;//当窗口的状态发生改变时(界面改变)case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:break;//内容改变case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:break;//滑动变化case AccessibilityEvent.TYPE_VIEW_SCROLLED:break;}}/*** 中断服务的回调*/@Overridepublic void onInterrupt() {}
}

2、在AndroidManifest.xml清单里注册服务

<serviceandroid:name=".service.SkAccessibilityService"android:enabled="true"android:exported="true"android:label="收款助手"android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"><intent-filter><action android:name="android.accessibilityservice.AccessibilityService" /></intent-filter><meta-dataandroid:name="android.accessibilityservice"android:resource="@xml/config_accessibility" /></service>

3、在res资源目录里创建一个xml目录,然后在xml里创建config_accessibility.xml配置文件,这个config_accessibility.xml文件主要是保存AccessibilityService服务的配置参数,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"android:accessibilityEventTypes="typeAllMask"android:accessibilityFeedbackType="feedbackAllMask"android:canRetrieveWindowContent="true"android:notificationTimeout="100"android:packageNames="com.eg.android.AlipayGphone,com.tencent.mm,com.tencent.mobileqq"android:description="@string/access_desc" />

上面的android:description项要提取到strings.xml里,不要问为什么,因为不这样会报错。

<resources><string name="access_desc">收款助手使用无障碍服务监听收款信息</string>
</resources>

xml参数说明:

accessibilityEventTypes:表示该服务对界面中的哪些变化感兴趣,即哪些事件通知,比如窗口打开,滑动,焦点变化,长按等。具体的值可以在AccessibilityEvent类中查到,如typeAllMask表示接受所有的事件通知;

accessibilityFeedbackType:表示反馈方式,比如是语音播放,还是震动;

canRetrieveWindowContent:表示该服务能否访问活动窗口中的内容。也就是如果你希望在服务中获取窗体内容,则需要设置其值为true;

notificationTimeout:接受事件的时间间隔,通常将其设置为100即可;

packageNames:表示对该服务是用来监听哪个包的产生的事件,要监听多个APP包,就用逗号隔开(如果不设置就是接收所有的);

description:对该无障碍功能的描述,会显示在系统开启服务的设置界面;

accessibilityEventTypes可选参数
typeAllMask 接收所有事件
typeWindowStateChanged 监听窗口状态变化,比如打开一个popupWindow,dialog,Activity切换等等
typeWindowContentChanged 监听窗口内容改变,比如根布局子view的变化
typeWindowsChanged 监听屏幕上显示的系统窗口中的事件更改,此事件类型只应由系统分派
typeNotificationStateChanged 监听通知变化,比如notifacation和toast
typeViewClicked 监听view点击事件
typeViewLongClicked 监听view长按事件
typeViewFocused 监听view焦点事件
typeViewSelected 监听AdapterView中的上下文选择事件
typeViewTextChanged 监听EditText的文本改变事件

typeViewHoverEnter

typeViewHoverExit

监听view的视图悬停进入和退出事件
typeViewScrolled 监听view滚动,此类事件通常不直接发送
typeViewTextSelectionChanged 监听EditText选择改变事件
typeViewAccessibilityFocused 监听view获得可访问性焦点事件
typeViewAccessibilityFocusCleared 监听view清除可访问性焦点事件

typeGestureDetectionStart

typeGestureDetectionEnd

监听手势开始和结束事件

typeTouchInteractionStart

typeTouchInteractionEnd

监听用户触摸屏幕事件的开始和结束

typeTouchExplorationGestureStart

typeTouchExplorationGestureEnd

监听触摸探索手势的开始和结束

二、使用服务

1、当APP退出注销时,无障碍服务也会关闭,因此我们最好是在APP上判断服务是否开启,没开启的话,就提醒用户主动授权开启无障碍服务,判断无障碍服务是否开启的方法如下:

 /*** 判断无障碍服务是否开启* @param mContext* @return*/private 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 {}return false;}

2、只要用户授权,无障碍服务就会自动运行,下面这串代码是跳转到无障碍服务授权页面的,用户授权才可以真正使用无障碍服务

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

三、无障碍服务处理事件信息

AccessibilityService中常用的方法的介绍:

  1. disableSelf():禁用当前服务,也就是在服务可以通过该方法停止运行
  2. findFoucs(int falg):查找拥有特定焦点类型的控件
  3. getRootInActiveWindow():如果配置能够获取窗口内容,则会返回当前活动窗口的根结点
  4. getSeviceInfo():获取当前服务的配置信息
  5. onAccessibilityEvent(AccessibilityEvent event):有关AccessibilityEvent事件的回调函数,系统通过sendAccessibiliyEvent()不断的发送AccessibilityEvent到此处
  6. performGlobalAction(int action):执行全局操作,比如返回,回到主页,打开最近等操作
  7. setServiceInfo(AccessibilityServiceInfo info):设置当前服务的配置信息
  8. getSystemService(String name):获取系统服务
  9. onKeyEvent(KeyEvent event):如果允许服务监听按键操作,该方法是按键事件的回调,需要注意,这个过程发生了系统处理按键事件之前
  10. onServiceConnected():系统成功绑定该服务时被触发,也就是当你在设置中开启相应的服务,系统成功的绑定了该服务时会触发,通常我们可以在这里做一些初始化操作
  11. onInterrupt():服务中断时的回调
这里我们主要关注onAccessibilityEvent事件:
/*** 监听窗口变化的回调*/@Overridepublic void onAccessibilityEvent(AccessibilityEvent event) {// 获取事件类型,在对应的事件类型中对相应的节点进行操作int eventType = event.getEventType();//根据事件回调类型进行处理switch (eventType) {//当通知栏发生改变时case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:break;//当窗口的状态发生改变时(界面改变)case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED://主要是在这里监听支付宝、微信、QQ界面上的收款信息break;//内容改变case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:break;//滑动变化case AccessibilityEvent.TYPE_VIEW_SCROLLED:break;}}

事件回调类型AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,表示窗口的内容发生改变时,比如微信收到消息,会显示在消息列表里,就会触发这个事件的回调,我们就可以在这个回调里获取相应的信息。

四、获取节点信息

获取了界面窗口变化后,这个时候就要获取控件的节点,整个窗口的节点本质是个树结构,通过以下方式操作节点信息

1、获取窗口节点(根节点)

AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();

2、获取指定子节点(控件节点)

//通过文本找到对应的节点集合
List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText(text);
//通过控件ID找到对应的节点集合,如com.tencent.mm:id/gd
List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId(id);

通过控件ID获取节点是最方便的,下面开始教大家怎么获取APP的控件ID

五、获取APP的控件ID

先说明一下,控件ID实际上是我们在界面布局里设定的android:id的编译后的值,这个是唯一的。

1、前面的准备工作里,我们已经在安卓模拟器里安装了支付宝、微信和QQ,这里以微信为例子,我们在安卓模拟器里登录微信

2、在Android的Sdk目录里找到tools工具目录,双击打开里面的monitor.bat

3、在DDMS的Devices列表里会看到有一个安卓模拟器,如下图:

4、点击Dump View Hierarchy UI Automotor图标,会自动获取当前模拟器中打开的APP界面UI信息

5、打开后出现如下图,通过鼠标点击到界面的文字或图片上,可以在节点详情里看到控件ID

6、通过控件ID获取节点信息的方法

    /*** 通过控件ID获取节点信息* @param id* @return*/@SuppressLint("NewApi")public String getNodeInfo(String id){String result="";// 获取当前整个活动窗口的根节点AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();// 获取父节点//nodeInfo.getParent();// 获取子节点//nodeInfo.getChild(0);if (nodeInfo != null) {// 通过文本找到对应的节点集合// List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText("");// 通过控件ID找到对应的节点集合,如com.tencent.mm:id/gdList<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId(id);for (AccessibilityNodeInfo item : list) {String str=item.getText().toString();if (str != null && str.length() != 0){result=str;break;}//模拟点击//item.performAction(AccessibilityNodeInfo.ACTION_CLICK);//模拟长按//item.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);//模拟获取焦点//item.performAction(AccessibilityNodeInfo.ACTION_FOCUS);//模拟粘贴//item.performAction(AccessibilityNodeInfo.ACTION_PASTE);}}return result;}

六、通过无障碍服务获取微信收款信息的代码:

package net.zy13.skhelper.service;import android.accessibilityservice.AccessibilityService;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.PowerManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;import net.zy13.skhelper.MainApplication;
import net.zy13.skhelper.handle.PostHandle;
import net.zy13.skhelper.utils.LogUtil;
import net.zy13.skhelper.utils.PreferenceUtil;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** 收款无障碍服务*/
public class SkAccessibilityService extends AccessibilityService{private PreferenceUtil preference;private PowerManager.WakeLock wakeLock = null;private PostHandle postHandle=null;/*** 当服务启动的时候会被调用*/@Overridepublic void onServiceConnected(){LogUtil.debug( ":无障碍服务连接成功");if(preference==null) {preference = PreferenceUtil.getInstance(MainApplication.getAppContext());}//开启wakelock,使CPU处于不休眠的状态,开启后需要重启手机if(preference.isWakelock()){PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, SkAccessibilityService.class.getName());wakeLock.acquire();}}/*** 监听窗口变化的回调*/@Overridepublic void onAccessibilityEvent(AccessibilityEvent event) {// 获取事件类型,在对应的事件类型中对相应的节点进行操作int eventType = event.getEventType();//根据事件回调类型进行处理switch (eventType) {//当通知栏发生改变时case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:break;//当窗口的状态发生改变时(界面改变)case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:String className = event.getClassName().toString();//获取界面信息getUiInfo(className);break;//内容改变case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:break;//滑动变化case AccessibilityEvent.TYPE_VIEW_SCROLLED:break;}}/*** 中断服务的回调*/@Overridepublic void onInterrupt() {}/*** 通过控件ID获取节点信息* @param id* @return*/@SuppressLint("NewApi")public String getNodeInfo(String id){String result="";// 获取当前整个活动窗口的根节点AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();// 获取父节点//nodeInfo.getParent();// 获取子节点//nodeInfo.getChild(0);if (nodeInfo != null) {// 通过文本找到对应的节点集合// List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText("");// 通过控件ID找到对应的节点集合,如com.tencent.mm:id/gdList<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId(id);for (AccessibilityNodeInfo item : list) {String str=item.getText().toString();if (str != null && str.length() != 0){result=str;break;}//模拟点击//item.performAction(AccessibilityNodeInfo.ACTION_CLICK);//模拟长按//item.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);//模拟获取焦点//item.performAction(AccessibilityNodeInfo.ACTION_FOCUS);//模拟粘贴//item.performAction(AccessibilityNodeInfo.ACTION_PASTE);}}return result;}/*** 获取界面信息* @param classname*/@SuppressLint("NewApi")public void getUiInfo(String classname){LogUtil.debug( "无障碍服务窗口状态改变,类名为"+classname);//通过类名判断是不是微信if(classname.equals("com.tencent.mm.ui.LauncherUI")) {LogUtil.debug( "正在使用无障碍服务获取微信收款信息:");String title=getNodeInfo("com.tencent.mm:id/fzg");if(title.contains("微信支付")||title.contains("微信收款助手")) {String content = getNodeInfo("com.tencent.mm:id/e7t");//String time = getNodeInfo("com.tencent.mm:id/j0l");Map<String,String> postmap=new HashMap<String,String>();postmap.put("type","weixin");postmap.put("title",title);postmap.put("money",extractMoney(content));postmap.put("content",content);SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");postmap.put("time",sdf.format(new Date()));LogUtil.debug("获取到的信息集合:"+postmap.toString());}}}/*** 从字符串里提取金额* @param content* @return*/protected  String extractMoney(String content){Pattern pattern = Pattern.compile("(收款|收款¥|向你付款|向您付款|入账|到帐)(([1-9]{1}\\d*)|([0]{1}))(\\.(\\d){0,2})?元");Matcher matcher = pattern.matcher(content);List<String> list = new ArrayList<>();while(matcher.find()){list.add(matcher.group());}if(list.size()>0){String tmp=list.get(list.size()-1);Pattern patternnum = Pattern.compile("(([1-9]{1}\\d*)|([0]{1}))(\\.(\\d){0,2})?");Matcher matchernum = patternnum.matcher(tmp);if(matchernum.find())return matchernum.group();return null;}elsereturn null;}
}

1、现在在安卓模拟器里运行我们的APP,来监听微信的收款信息

2、通过查看debug日志 ,发现无障碍服务已经连接成功了,现在我用另一个微信号扫收款二维码进行付款,看看能不能监听到。

3、可以获取到收款信息,但有个bug,就是你要主动打开微信才可以获取到,如果微信退到后台或者关闭,无障碍服务就不能实时监听到收款信息了。

安卓使用无障碍服务监听微信和QQ的收款信息相关推荐

  1. linux系统中ntp服务监听端口是,Linux系统下测试UDP端口是否正常监听的办法

    TCP端口,比如80端口,可使用 telnet 主机ip 80,来验证端口是否正常监听,那UDP端口是否可同样测试呢?关于UDP端口怎样测试监听的问题, 下面我们来进行测试,如:123端口是服务器 A ...

  2. 微信浏览器返回刷新,监听微信浏览器返回事件,网页防复制,移动端禁止图片长按和vivo手机点击img标签放大图片

    以下代码都经过iphone7,华为MT7 ,谷歌浏览器,微信开发者工具,PC端微信验证.如有bug,还请在评论区留言. demo链接:https://pan.baidu.com/s/1c35mbjM ...

  3. 用Python机器人监听微信群聊

    随着微信社交的兴起,我们加入的群也越来越多,一个不经意就被拉入好几个群,群是大家协同交流的平台,但是微信群却越来越泛滥,不知道大家有没有统计过自己浪费在毫无营养的群中的时间? 因为群质量太低或者群太吵 ...

  4. 用Python机器人监听微信群聊 !

    随着微信社交的兴起,我们加入的群也越来越多,一个不经意就被拉入好几个群,群是大家协同交流的平台,但是微信群却越来越泛滥,不知道大家有没有统计过自己浪费在毫无营养的群中的时间? Python学习交流群: ...

  5. 用Python机器人监听微信群聊, 我看谁这么大的胆子敢调侃老板和前台小姐姐!

    随着微信社交的兴起,我们加入的群也越来越多,一个不经意就被拉入好几个群,群是大家协同交流的平台,但是微信群却越来越泛滥,不知道大家有没有统计过自己浪费在毫无营养的群中的时间? 因为群质量太低或者群太吵 ...

  6. php h5支付没有返回app中,很多人都不知道的监听微信、支付宝等移动app及浏览器的返回、后退、上一页按钮的事件方法...

    在实际的应用中,我们常常需要实现在移动app和浏览器中点击返回.后退.上一页等按钮实现自己的关闭页面.调整到指定页面或执行一些其它操作的 需求,那在代码中怎样监听当点击微信.支付宝.百度糯米.百度钱包 ...

  7. Flutter app 安卓iOS前后台切换监听

    Flutter app 安卓iOS前后台切换监听 class _MyAppState extends State<MyApp> with WidgetsBindingObserver {@ ...

  8. php 微信监听返回键,JavaScript_JS监听微信、支付宝等移动app及浏览器的返回、后退、上一页按钮的事件方法,在实际的应用中,我们常常需 - phpStudy...

    JS监听微信.支付宝等移动app及浏览器的返回.后退.上一页按钮的事件方法 在实际的应用中,我们常常需要实现在移动app和浏览器中点击返回.后退.上一页等按钮实现自己的关闭页面.调整到指定页面或执行一 ...

  9. 【监听微信公众号消息】

    监听微信公众号消息 效果图如下: 环境要求 效果图如下: 环境要求 需要windows服务器,登陆上微信.获取到的消息是xml格式,需要解析内容获取自己想要的公众号推送的消息. 详情:https:// ...

最新文章

  1. android studio 通过界面快速查看md5
  2. opencv进阶学习笔记2:numpy操作图像,色彩空间,查找指定颜色范围,通道分离与合并
  3. 动画演示男性结扎手术 | 今日趣图
  4. DHCP+TFTP+PXE自动网络引导安装Linux操作系统
  5. Maxent猛犸反欺诈入选Gartner 2018 Cool Vendors
  6. Python with 结构
  7. 如何登录新浪微博html5,新浪微博怎么登陆
  8. EVE上传Dynamips和IOL
  9. MySQL索引面试题六连击
  10. 不会压测?没关系,手把手教你用jmeter做压力测试及结果分析
  11. 51单片机移植UCOS-II的方法与改进
  12. 键盘的基本使用(包含各种快捷键的使用)
  13. 对于产业互联网的认识,直接关系着我们究竟会以怎样的心态来看待它
  14. SEO小白学习与实践(二):SEO相关基础知识与自有网站诊断
  15. 作业1.老狼几点了。凌晨,上午,下午,晚上。
  16. 百度前端技术学院-斌斌学院-任务五
  17. 前端面试总结(持续更新中~~~~)
  18. LaTex 连字符多篇连续引用
  19. Insightful Thought on Load Balance Solution Choice
  20. 高通骁龙410C开发板基础知识

热门文章

  1. 天迹云通信短信分销系统源码
  2. php mysql首字母查询_MySQL应用技巧实现查询汉字的拼音首字母
  3. 大数据离线---网站日志流量分析系统(1)---简介及框架
  4. 2020年必知的15个中小型企业网络安全统计数据
  5. 高项论文练习--项目进度管理
  6. 前端实习一个月的收获与思考
  7. 【CSS】固定定位示例 ( 屏幕左右两侧广告栏 | 开发要点分析 | 代码示例 )
  8. 你有10万粉丝又如何,然而并没有什么卵用!
  9. 学计算机编程用苹果笔记本好么,苹果macbook好不好啊,想我想学编程用这电脑好不好,听别人说可能会不好用,是真的么...
  10. SQLIntegrityConstraintViolationException: Duplicate entry 'xxx' for key 'yyyzzz'异常解决