发此文还是想分享一些自己在android模拟点击这块的收获和想法
为什么开发模拟点击?
这是一个很好的问题。从我们开始开发微信相关的xposed模块的时候,就知道xposed这东西用着不踏实,时不时地微信给你来几个封号,搞的你的客户怨声载道。作为开发,面对销售显示出的大多是无奈、尴尬。
转而考虑市面上的类似于微商小蜜、微商工具箱一些微信辅助营销工具,准备开始模拟点击这方面的开发。
开始
首先新建一个service extends AccessibilityService,然后在manifest中注册,并且需要申明你采用的配置方案
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibility" />
accessibility.xml:
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeWindowStateChanged|typeNotificationStateChanged|typeViewTextSelectionChanged|typeViewClicked"
android:accessibilityFeedbackType="feedbackGeneric"
android:canRetrieveWindowContent="true"
android:accessibilityFlags="flagDefault|flagRetrieveInteractiveWindows"
android:canRequestTouchExplorationMode="true"
android:packageNames="com.tencent.mm"
android:description="@string/as_description"
android:notificationTimeout="100"/>
配置中的属性大家可以自行了解,特别需要说明的是 accessibilityEventTypes 这个属性,最好不好接收全部的行为类型,一个是日志不好看,一个是没有必要。常用的类型就上面几个就够了。其中typeWindowStateChanged中重点,他是activity切换的时候,或者一个dialog alert的时候触发的行为,常用来判断页面的切换,当前操作完成的依据。
其次是accessibilityFlags,这个属性需要设置flagRetrieveInteractiveWindows,为什么呢,当你用 mService.getRootInActiveWindow()
这个方法获取当前视图的最顶级window的方法,当两个activity切换的时候,中间有个渲染的时间差,要resume的activity附着的window还没渲染出来,你就调用了,就会返回null,但是加上这个属性,会返回手机底层的window,就是titleBar附着的那一层window,这样就避免了有时候会发生的空指针异常。
/**
* Gets the root node in the currently active window if this service
* can retrieve window content. The active window is the one that the user
* is currently touching or the window with input focus, if the user is not
* touching any window.
public AccessibilityNodeInfo getRootInActiveWindow() {
return AccessibilityInteractionClient.getInstance().getRootInActiveWindow(mConnectionId);
}
引导用户去打开无障碍-辅助服务
判断你的服务在不在运行?
这里有几个坑需注意,你要是简单的用获取你的应用的服务栈来判断服务在不在运行的方法是不可取的,因为即使是service在内存中运行,但是因为一些报错或者其他的原因,你的AccessibilityService服务可能会在AccessibilityManager里面消失掉。
这里我用的方法是在onServiceConnected 方法中先保存service实例,然后通过以下方法判断你的service可不可用
/**
* 判断辅助服务是否正在运行
*/
public static boolean isServiceRunning(AccessibilityService service) {
if (service == null) {
return false;
}
AccessibilityManager accessibilityManager = (AccessibilityManager) service.getSystemService(Context.ACCESSIBILITY_SERVICE);
AccessibilityServiceInfo info = service.getServiceInfo();
if (info == null) {
return false;
}
List<AccessibilityServiceInfo> list = accessibilityManager.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC);
Iterator<AccessibilityServiceInfo> iterator = list.iterator();
boolean isConnect = false;
while (iterator.hasNext()) {
AccessibilityServiceInfo i = iterator.next();
if (i.getId().equals(info.getId())) {
isConnect = true;
break;
}
}
if (!isConnect) {
return false;
}
return true;
}
跳转到开启服务页面
/**
* 打开辅助服务的设置
*/
public static void openAccessibilityServiceSettings(Activity context) {
try {
Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
context.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
接收事件
做了以上这些时候能 你就可以愉快的在你的service里面接收微信相关的控件出发的事件了
public class MainService extends AccessibilityService {
@Override
protected void onServiceConnected() {
super.onServiceConnected();
AccessibilityHelper.mService = this;
L.e("onServiceConnected");
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
L.e("当前事件:" + event.toString());
}
}
下一篇将介绍对于接收事件的处理