通过抢红包插件学习Accessibility Service
我们时常会遇到一些人使用 ”抢红包”插件来帮忙抢红包,它实际上就是通过Android上常见的Accessibility Services功能实现的,今天咱们通过这个插件来聊一下Accessibility Services
1 什么是Accessibility Services
概述
Accessibility Services是为一些有障碍人士使用Android设备和应用所提供的,它在后台运行,并当AccessibilityEvents被触发时接受系统的回调,这些Events表示在用户界面某些状态的转换,比如说焦点的变换,一个Button被点击等等,AccessibilityEvents后面也会说到,这个Services可以有选择地请求和查询活动窗口内容的能力。
Lifecycle
Accessibility Services的生命周期由系统专门管理,并且遵循既定服务的生命周期,启动Accessibility Services完全是由用户在设置中打开并触发。系统在绑定Services之后将调起onServiceConnected()。这个方法可以由客户端继续重写,做重新配置。
摘自:https://developer.android.google.cn/reference/android/accessibilityservice/AccessibilityService.html
2 如何使用
AndroidMainfest声明Services
Accessibility跟其他Services一样,都需要在 AndroidManifest.xml中进行声明,但是它必须做两件事情:
- 它必须设置Intent的Action:“android.accessibilityservice.AccessibilityService”
- 声明请求 BIND_ACCESSIBILITY_SERVICE 权限,确保只能系统才能绑定到它
上面两点中如果有任意一项缺失系统将忽略 Accessibility Services

配置Services
一个Accessibility Services可以通过配置来接收特殊类型的 AccessibilityEvents。可以通过两种方式进行配置:
- 在Mainfest中声明Accessibility Services时提供一个meta-data ,如上图中所示,其中配置文件可参考下图

-
调用 setServiceInfo(AccessibilityServiceInfo) . 注意,这个方法可以随时调用去更改Services的配置。
注:这个方法只能允许设置动态可配置的属性有: eventTypes, feedbackType, flags, notificationTimeout, packageNames
检索窗口内容(Retrieving window content)
新建一个继承自AccessibilityService的类,实现它的两个抽象方法onAccessibilityEvent和onInterrupt。用于检索窗口信息。我们将在后面实现抢红包插件的地方详细说明。

- onAccessibilityEvent(AccessibilityEvent event) 此事件由调用方拥有,此方法返回后不能使用。希望在此方法返回后使用事件的服务应该复制。(???)窗体上有什么变化都会回调该方法.
- onInterrupt() 中断accessibility反馈的回调
Accessibility Services可以在它的声明中指定检索窗口的内容, 这些内容被表示成AccessibilityWindowInfo树或者AccessibilityWindowInfo对象的形式。值得注意的是,这些声明需要通过上文提到的meta_data中的xml文件中配置.
注:Accessibility Services可能已经请求通知了事件类型的子集,因此当节点层次结构发生更改时可能并不知情。由于窗口内容可能随时发生更改,所以节点也可能包含过时的信息。
Note An accessibility service may have requested to be notified for a subset of the event types, and thus be unaware when the node hierarchy has changed. It is also possible for a node to contain outdated information because the window content may change at any time.
打开辅助功能权限
要想使用Accessibility Services就必须让用户手动打开辅助功能的权限。可以用下面的方法来检测是否开启了辅助功能权限:

如果检查没有该权限,可以使用startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));去引导用户打开辅助功能权限。
3 抢红包插件开发
完成配置
根据上一小节内容,完成在AndroidMainfest.xml中的声明以及在xml中配置。新建一个RedEnvelopAccessibility继承自AccessibilityService,实现其两抽象方法.
分析微信抢红包页面
通过打开Android SDK自带的uiautomatorviewer来分析微信抢红包的页面信息。

我们要分析三个页面:
- 收到红包的页面
- 弹出红包的页面
- 抢完红包的页面
我们要分析它的包名、层次结构、关键文本信息View id等。
实现自动抢红包
在onAccessibilityEvent方法中,要根据我们分析的内容进行相应的过滤,来获取或者响应我们的内容。
首先,我们可以通过过滤包名将其他软件过滤在外:

这里我们说明一下getRootInActiveWindow()和onAccessibilityEvent(AccessibilityEvent event)中的event.getSource(),他们都是返回AccessibilityNodeInfo但是区别是有的
- getRootInActiveWindow():整个窗口的对象
- event.getSource:得到的是被点击的单体对象
很明显我们应该使用前者。另外,我们可以通过调用AccessibilityNodeInfo中的findAccessibilityNodeInfosByText和findAccessibilityNodeInfosByViewId,分别用来根据子View的文字和Id来查询相应的AccessibilityNodeInfo,并且它的performAction方法可以模拟很多“动作”就像是点击、长按等,当然这都是在该View是可以被点击的前提下生效。有了这些,实现自动抢红包插件就不困难了.
收到红包的页面
收到红包时,对方发的红包会有显示“领取红包”的字样,自己的发送的红包会有“查看红包”的字样,根据这一点,我们找到对应可以点击的View。

通过实验我们发现是通过点击一个LinearLayout实现的弹出红包的功能.


弹出红包页面
同样的道理,我们用上面的方法继续找到了对应点击的位置。

但是需要特别注意的是,在真正执行抢红包动作时,尽量延迟几百毫秒,如果微信自查出来,这很危险!
抢完红包的页面
抢完红包的页面跟其他不同的是它在单独的一个Activity,需要使用到performGlobalAction来模拟全局返回键。

通知栏点击抢红包
如果当前不在微信页面怎么办,小意思,我们也有办法,当你开启微信群的通知时,当红包来时将会触发 AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED,所以此时可以通过它过滤是否是红包来了,完成通知栏的点击然后进行后面的抢红包操作。


至此,自动抢红包插件的主要实现就介绍完毕。详情请看GitHub代码.
4 其他
有些时候我们需要模拟点击屏幕中的某个位置,而不局限于某个View,这里我从网上找到了一个方法放在这里,方便以后查阅:

水平有限,内容粗略,敬请谅解
欢迎关注我的微信公众号:

点击阅读原文查看GitHub项目