抢红包软件的核心原理----AccessibilityService
使用
1 先新建一个工程,然后在 res 下新建一个 xml 目录,在xml目录内新建一个accessibility_service_config.xml文件,内容如下:
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagDefault"
android:canRetrieveWindowContent="true"
android:description="@string/accessibility_desc"
android:packageNames="com.android.packageinstaller"/>
- packageNames :指定我们要监听哪个应用程序下的窗口活动
- accessibility_desc:指定在无障碍服务当中显示给用户看的说明信息
- accessibilityEventTypes:指定我们在监听窗口中可以模拟哪些事件,这里typeAllMask表示所有的事件都能模拟
- accessibilityFlags:可以指定无障碍服务的一些附加参数,这里我们传默认值flagDefault就行
- accessibilityFeedbackType:指定无障碍服务的反馈方式,实际上辅助功能(无障碍服务)这个功能是 Android 提供给一些残疾人士使用的,比如说盲人不方便使用手机,就可以借助无障碍服务配合语音反馈来操作手机,而我们其实是不需要反馈的,因此随便传一个值就可以,这里传入feedbackGeneric
- canRetrieveWindowContent:指定是否允许我们的程序读取窗口中的节点和内容,必须写true
2 创建一个service继承 AccessibilityService。
public class SmartInstallAPKAccessibilityService extends AccessibilityService {
private static final String TAG = "[SmartInstallAPKAccessibilityService]";
private Map<Integer, Boolean> handleMap = new HashMap<>();
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
AccessibilityNodeInfo nodeInfo = event.getSource();
if (nodeInfo != null) {
int eventType = event.getEventType();
if (eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED ||
eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
if (handleMap.get(event.getWindowId()) == null) {
boolean handled = iterateNodesAndHandle(nodeInfo);
if (handled) {
handleMap.put(event.getWindowId(), true);
}
}
}
}
}
@Override
public void onInterrupt() {
}
//遍历节点,模拟点击安装按钮
private boolean iterateNodesAndHandle(AccessibilityNodeInfo nodeInfo) {
if (nodeInfo != null) {
int childCount = nodeInfo.getChildCount();
if ("android.widget.Button".equals(nodeInfo.getClassName())) {
String nodeCotent = nodeInfo.getText().toString();
Log.d(TAG, "content is: " + nodeCotent);
if ("安装".equals(nodeCotent)
// || "完成".equals(nodeCotent)
|| "确定".equals(nodeCotent)
|| "打开".equals(nodeCotent)) {
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
return true;
}
}
//遇到ScrollView的时候模拟滑动一下
else if ("android.widget.ScrollView".equals(nodeInfo.getClassName())) {
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
}
for (int i = 0; i < childCount; i++) {
AccessibilityNodeInfo childNodeInfo = nodeInfo.getChild(i);
if (iterateNodesAndHandle(childNodeInfo)) {
return true;
}
}
}
return false;
}
}
每当有窗口活动时,就会触发 onAccessibilityEvent() 方法,我们根据传入的 AccessibilityEvent 参数来判断当前事件的类型我们只需要监听TYPE_WINDOW_CONTENT_CHANGED和TYPE_WINDOW_STATE_CHANGED这两种事件就可以了,因为在整个安装过程中,这两个事件必定有一个会被触发。当然也有两个同时都被触发的可能,那么为了防止二次处理的情况,这里我们使用了一个Map集合来过滤掉重复事件。然后通过iterateNodesAndHandle方法来进行当前界面节点的判断,如果是按钮节点,我们就判断按钮上的文字是不是安装、完成、确定、打开这几项,如果是,就进行模拟点击。(完成和打开不能同时在代码中进行判断,如果你安装完软件不想打开软件,就保留完成。反之则保留打开) 。
3 注册服务
<service
android:name=".SmartInstallAPKAccessibilityService"
android:label="智能安装 APK"
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>