预备知识:了解Android开发环境,linux/mac环境,设备引荐运用pixel。
写在前面
AccessibilityService是Android体系供给的辅佐功用,能够让咱们实时监听体系的各种事情,并做出相应的操作。事情包含:屏幕activity改变,按钮点击,界面切换等;可履行的操作包含:点击,滑动,对话框输入等等。因而咱们能够运用这功用来完成一些自动化操作。本文以完成抖音的自动点赞,重视,谈论功用作为实战例子,详细介绍AccessibilityService的用法。
服务注册与权限请求
App运用Android AccessibilityService服务首要需求请求相应的权限,以及注册对应的服务可供体系回调。在AndroidManifest.xml文件中装备信息如下:
<service
android:name=".AutoAccessibilityService"
android:exported="true"
android:label="@string/app_name"
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>
android.permission.BIND_ACCESSIBILITY_SERVICE
是需求请求的权限。
.AutoAccessibilityService
是自定义的服务类称号,可自定义命名。
@xml/accessibility_service_config
自定义装备的辅佐服务信息,需求在res
文件下创立accessibility_service_config.xml
文件,文件事例如下:
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/accessibility_service_description"
android:packageNames="com.android.automeng, com.ss.android.ugc.aweme"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFlags="flagReportViewIds"
android:accessibilityFeedbackType="feedbackSpoken"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true"
android:canPerformGestures="true"
android:settingsActivity="com.example.android.accessibility.ServiceSettingsActivity"
/>
- android:description:服务的描述,出现在辅佐中心列表中,引用字符串资源。
- android:packageNames:监听的运用包名,用逗号离隔,这里监听的包名为com.android.automeng,com.ss.android.ugc.aweme这两个运用触发的事情。
- android:accessibilityEventTypes:指明要监听的事情类型,这里运用
typeAllMask
表明监听一切类型的辅佐事情。 - android:accessibilityFlags:事情回调中报告视图ID,这样服务能够找到触发事情的具体视图。
- ndroid:accessibilityFeedbackType:供给语音反馈。
- android:notificationTimeout:事情通知的超时时刻,单位ms。假如超过这个时刻服务没有响应,该事情将被丢掉。
- android:canRetrieveWindowContent:服务能够检索窗口内容,假如为false,onAccessibilityEvent()中的事情无法获取子View信息。
- android:canPerformGestures:服务能够履行手势操作,如双击、滑动等。
- android:settingsActivity服务的设置界面Activity,答应用户设置服务相关选项。
创立辅佐服务
创立辅佐服务类需求继承 android.accessibilityservice.AccessibilityService
并重载onAccessibilityEvent
及 onInterrupt
办法,让体系在触发到咱们accessibility_service_config.xml
文件中注册的事情时,能够回调咱们重载的辅佐服务代码。
public class AutoAccessibilityService extends AccessibilityService {
@Override
public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {
//恣意事情触发都会调用该函数,因而能够在此完成辅佐功用代码。
}
@Override
public void onInterrupt() {
//退出时调用。
}
完成点击事情
要完成模仿点击,咱们首要需求获取需求点击的点坐(x,y)。在此能够运用AccessibilityServier
中的AccessibilityEvent
对象来获取,经过遍历当前设备Activity界面中各个view来得到咱们所要点击的view的坐标。
//依据veiwID拿到对应的节点信息。
static AccessibilityNodeInfo searchNodeInfoByViewId(AccessibilityEvent event, String viewId){
AccessibilityNodeInfo nodeInfo = event.getSource();
if (nodeInfo == null) return null;
List<AccessibilityNodeInfo> nodeList = nodeInfo.findAccessibilityNodeInfosByViewId(viewId);
if(nodeList == null){
for(int i=0; i < nodeInfo.getChildCount(); ++i){
AccessibilityNodeInfo node = searchNodeInfoByViewId(nodeInfo.getChild(i), viewId);
if(node != null){
return node;
}
}
}else if(nodeList != null && nodeList.size() > 0){
for(AccessibilityNodeInfo node : nodeList){
if(node.isVisibleToUser()){ //查找到可见的view则回来。
return node;
}
}
}
return null;
}
//依据节点信息可获得对应的x,y坐标
static Point getPointtByNode(AccessibilityNodeInfo node){
if (node == null){
return new Point(0, 0);
}
Rect rect = new Rect();
node.getBoundsInScreen(rect);
printNodeInfo(node, "- ");
Point point = new Point(rect.centerX(), rect.centerY());
return point;
}
运用GestureDescription
完成点击操作。
//完成对(x,y)坐标进行点击操作。
public static boolean clickByNode(AccessibilityService service, int x, int y) {
if (service == null) {
AutoLog.e("clickByNode, service is NULL.");
return false;
}
Point point = new Point(x, y);
GestureDescription.Builder builder = new GestureDescription.Builder();
Path path = new Path();
path.moveTo(point.x, point.y);
builder.addStroke(new GestureDescription.StrokeDescription(path, 0L, 200L));
GestureDescription gesture = builder.build();
boolean isDispatched = service.dispatchGesture(gesture, new AccessibilityService.GestureResultCallback() {
@Override
public void onCompleted(GestureDescription gestureDescription) {
super.onCompleted(gestureDescription);
AutoLog.d("dispatchGesture click onCompleted.");
}
@Override
public void onCancelled(GestureDescription gestureDescription) {
super.onCancelled(gestureDescription);
AutoLog.d("dispatchGesture click onCancelled.");
}
}, null);
return isDispatched;
}
在咱们得知dou音app中点赞位置的viewid为"com.ss.android.ugc.aweme:id/dxk"
后,即可运用上面代码完成自动点击操作。
public class AutoAccessibilityService extends AccessibilityService {
@Override
public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {
//恣意事情触发都会调用该函数,因而能够在此完成辅佐功用代码。
String viewId = "com.ss.android.ugc.aweme:id/dxk"; //该id为点赞view的id。
AccessibilityNodeInfo node = searchNodeInfoByViewId(accessibilityEvent, viewId);
Point p = getPointtByNode(node);
clickByNode(this, p.x, p.y);
}
@Override
public void onInterrupt() {
//退出时调用。
}
如何获取viewid?最简单的就是经过对一切view进行打印,然后依据getContentDescription
或许getText
回来的view描述信息来猜想对应的view。下面为打印出来的部分AccessibilityNodeInfo
信息,从中不难看出来“重视”的viewid为com.ss.android.ugc.aweme:id/vf4
。
- className[android.widget.TextView], viewIdResourceName[null], text[NULL], contentDescription[探究,按钮], stateDescription[NULL], top[63], bottom[178], left[145], right[303], centerX[224], centerY[120]
---- className[android.widget.TextView], viewIdResourceName[com.ss.android.ugc.aweme:id/vf4], text[探究], contentDescription[NULL], stateDescription[NULL], top[90], bottom[151], left[179], right[269], centerX[224], centerY[120]
--- className[android.widget.TextView], viewIdResourceName[null], text[NULL], contentDescription[同城,按钮], stateDescription[NULL], top[63], bottom[178], left[303], right[461], centerX[382], centerY[120]
---- className[android.widget.TextView], viewIdResourceName[com.ss.android.ugc.aweme:id/vf4], text[同城], contentDescription[NULL], stateDescription[NULL], top[90], bottom[151], left[337], right[427], centerX[382], centerY[120]
--- className[android.widget.TextView], viewIdResourceName[null], text[NULL], contentDescription[重视,按钮有未读消息], stateDescription[NULL], top[63], bottom[178], left[461], right[619], centerX[540], centerY[120]
---- className[android.widget.TextView], viewIdResourceName[com.ss.android.ugc.aweme:id/vf4], text[重视], contentDescription[NULL], stateDescription[NULL], top[90], bottom[151], left[495], right[585], centerX[540], centerY[120]
获取viewId还能够经过运用Androidstudio
中的Layout Inspection
来检查页面布局信息,可是该办法只能对debug包或许处于debug状态的体系才能生效。假如关于已发布的app则需求运用apktool
等东西来反编译修正Manifest
中的debuggable
信息。或许直接修正体系的debuggable
特点。因为与本文章主题不太一致,在此不做详细描述。不建议运用该办法,因为关于视频类app来说界面改变太快,Layout Inspection
会反应不过来,经常出现卡死状态。
完成滑动事情
与“点击事情”类似,相同运用GestureDescription
完成点击操作,履行向上滑动操作,代码如下。
private boolean scrolUp(AccessibilityService service, int center_X, int center_Y) {
if (service == null) {
AutoLog.e("ScrolUp, service is NULL.");
return false;
}
AutoLog.d("center position:" + "(" + center_X + "," + center_Y + ")");
final android.graphics.Path path = new Path();
path.moveTo((int) (center_X), (int) (center_Y * 1.5)); //起点坐标。
path.lineTo((int) (center_X), (int) (center_Y * 0.5)); //终点坐标。
GestureDescription.Builder builder = new GestureDescription.Builder();
GestureDescription gestureDescription = builder.addStroke(
new GestureDescription.StrokeDescription(path, 0, 800)
).build();
boolean isDispatched = service.dispatchGesture(gestureDescription, new AccessibilityService.GestureResultCallback() {
@Override
public void onCompleted(GestureDescription gestureDescription) {
super.onCompleted(gestureDescription);
AutoLog.d("dispatchGesture ScrollUp onCompleted.");
path.close();
}
@Override
public void onCancelled(GestureDescription gestureDescription) {
super.onCancelled(gestureDescription);
AutoLog.d("dispatchGesture ScrollUp cancel.");
}
}, null);
return isDispatched;
}
完成谈论事情
谈论操作需求往文本框中设置文本内容,并点击发送操作,即可完成自动谈论操作。
//往谈论框输入文本内容。
String commentEditViewId = "com.ss.android.ugc.aweme:id/csa"; //谈论文本框viewId
AccessibilityNodeInfo node = searchNodeInfoByViewId(accessibilityEvent, commentEditViewId);
if (node != null) {
Bundle bundle = new Bundle();
String commentMsg = "hello world";
bundle.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
commentMsg);
node.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, bundle);
}
Thread.sleep(2000); //等待2秒
//点击发送按钮,发送谈论内容。
String sendViewId = "com.ss.android.ugc.aweme:id/ct_";
AccessibilityNodeInfo node2 = AccessibilityUtils.searchNodeInfoByViewId(rootNodeInfo, sendViewId);
if (node2 != null) {
Point p = getPointtByNode(node2);
boolean ret = clickByNode(this, p.x, p.y);
}
最终作用
将上面的内容组合起来即可完成某音的自动化操作,示例视频如下,其中运用的抖音23.1.0版本包。
注:示例代码中,的viewid均为Android端抖音23.1.0版本包。