最近研究了自动化操做的相关事宜,辅助服务就是其中一项技术。下面介绍一下相关方面技术。这项技术能够用做抢红包、App自动安装卸载、页面内容抓取,WX消息的自动发送、自动发送朋友圈,H5页面内容抓取也能够。php
对于那些因为视力、听力或其它身体缘由致使不能方便使用Android智能手机的用户,Android提供了Accessibility功能和服务辅助这些用户更加简单地操做设备,包括文字转语音(不支持中文)、触觉反馈、手势操做、轨迹球和手柄操做。开发者能够搭建本身的Accessibility服务,这能够增强可用性,例如声音提示,物理反馈,和其余可选的操做模式。java
AccessibilityService是一个运行在后台的服务,有相关回调方法onAccessibilityEvent(AccessibilityEvent event)可以收到由系统发出的一些事件。界面中产生的任何变化都会产生一个事件,并由系统通知给AccessibilityService这就像监视器监视着界面的一举一动,一旦界面发生变化,马上进行相关操做。node
Api 4就已经加入了这项技术,但Android 4.1算是一个分水岭,此版本之后,系统辅助服务增长了与窗口元素的双向交互,能够经过辅助功能服务操做窗口元素,好比点击按钮等android
import android.accessibilityservice.AccessibilityService;
import android.view.accessibility.AccessibilityEvent;
public class AutoAccessibilityService extends AccessibilityService {
@Override
protected void onServiceConnected() {
super.onServiceConnected();
//当咱们当服务被系统服务连接成功后,这里能够作一些初始化当工做
}
@Override
public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {
int eventType = accessibilityEvent.getEventType();
//事件监听回调,开发者能够作不少事情
switch (eventType) {
//当窗口的状态发生改变时
case AccessibilityEvent.TYPE_WINDOWS_CHANGED://窗口变化
break;
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED://对话框、popupwindow、菜单
break;
case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED://通知
break;
case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED://基于当前event.source中子view的内容变化
break;
.....
.....
}
}
@Override
public void onInterrupt() {
//这个在系统想要中断AccessibilityService返给的响应时会调用。在整个生命周期里会被调用屡次。
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
//在系统将要关闭Service时被调用。在这个方法主要作释放资源的工做。
}
}
复制代码
<service android:name=".AutoAccessibilityService" android:enabled="true" android:exported="true" android:canRetrieveWindowContent="true" 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>
复制代码
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged|typeWindowContentChanged|typeWindowsChanged" android:accessibilityFeedbackType="feedbackGeneric" android:accessibilityFlags="flagDefault|flagRequestEnhancedWebAccessibility" android:canRetrieveWindowContent="true" android:description="@string/app_name" android:notificationTimeout="100" android:packageNames="com.weibo"//这是咱们要监听的包名 android:canRequestEnhancedWebAccessibility="true" />
复制代码
added API level 14微信
AccessibilityManager accessibilityManager = (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE);
accessibilityManager.addAccessibilityStateChangeListener(new AccessibilityManager.AccessibilityStateChangeListener() {
@Override
public void onAccessibilityStateChanged(boolean isChange) {
Log.i(TAG,"onAccessibilityStateChanged isChange = " + isChange);
if(isChange){
//do something
}else{
//do something
}
}
});
复制代码
以上就是最基本的实现,可是这个辅助服务须要用户手动开启受权才能用,固然某些黑科技下也是能够不用手动开启的,好比,电脑经过adb命令来控制已经root的手机。呵呵~app
抢红包的插件、微信消息的自动发送、自动发送朋友圈网上不少了,就不说了。下面说一下像H5页面的内容怎么抓取,直接上代码:ide
一、遍历当前页面每一个节点工具
AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();//获取当前页面的跟页面的节点
for (int i = 0; i < accessibilityNodeInfo.getChildCount(); i++) {
AccessibilityNodeInfo child = accessibilityNodeInfo.getChild(i);
findEveryViewNode(child);
}
复制代码
二、找出每一个节点的子节点的信息源码分析
public void findEveryViewNode(AccessibilityNodeInfo node) {
if (null != node && node.getChildCount() > 0) {
for (int i = 0; i < node.getChildCount(); i++) {
AccessibilityNodeInfo child = node.getChild(i);
if (child == null) {
continue;
}
Log.i(TAG, "节点数据 ======= " +
",text = " + child.getText()
+ ", descript = " + child.getContentDescription() +
", className = " + child.getClassName() +
", resId = " + child.getViewIdResourceName());
// 递归调用
findEveryViewNode(child);
}
}
}
复制代码
这里须要注意的是,某些状况下,返回的节点信息会是null。this
有了神操做,在自动化的路上,无往不利。
/** * 模拟点击事件 */
public void performViewClick(AccessibilityNodeInfo nodeInfo) {
if (nodeInfo == null) {
return;
}
while (nodeInfo != null) {
if (nodeInfo.isClickable()) {
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
break;
}
nodeInfo = nodeInfo.getParent();
}
}
/** * 模拟返回操做 */
public void performBackClick() {
performGlobalAction(GLOBAL_ACTION_BACK);
}
/** * 模拟Home操做 */
public void performHomeClick() {
performGlobalAction(GLOBAL_ACTION_HOME);
}
/** * 模拟下滑操做 */
public void performScrollBackward() {
performGlobalAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
}
/** * 模拟上滑操做 */
public void performScrollForward() {
performGlobalAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
}
复制代码
方法还有不少这里就不一一列举。
以点击事件为例,AccessibilityService是如何作到监控捕捉用户行为的
一、AccessibilityEvent产生:
View#performClick()
-> View#sendAccessibilityEventUncheckedInternal()
-> ViewGroup#requestSendAccessibilityEvent()
-> ViewRootImpl#requestSendAccessibilityEvent()
-> AccessibilityManager#sendAccessibilityEvent(event)
-> AccessibilityManagerService#sendAccessibilityEvent()
-> AccessibilityManagerService#notifyAccessibilityServicesDelayedLocked()
-> Service#notifyAccessibilityEvent(event)
复制代码
二、AccessibilityEvent处理:
AccessibilityEvent
-> Binder
-> IAccessibilityServiceClientWrapper#onAccessibilityEvent(AccessibilityEvent)
-> HandlerCaller#sendMessage(message); // message中包括AccessibilityEvent
-> IAccessibilityServiceClientWrapper#executeMessage()
-> Callbacks#onAccessibilityEvent(event)
-> AccessibilityService.this.onAccessibilityEvent(event)
复制代码
在研究这些技术时,深感系统的良心大做,用来给使用手机困难的人使用,同时也提供了使用手机方便的人更加方便。
开发人员的困难点就是对目标程序的研究,分析出各个页面以及节点的信息,并对目的操做流程细化抽象,最终完成目标。
工具推荐:UI Automator Viewer分析页面元素