原理
大致简述一下,谷歌已经在View、ViewGroup、TextView等控件的文字改变、滑动、UI变化埋下了接口,当这些状态变化时控件会回调系统API,API系统然后对这些对象的数据进行组装,为了数据的安全性,系统会重新创建一些对象(AccessibilityEvent、AccessibilityNodeInfo)来间接保存这些数据,然后通过跨进程将这些数据返回给对应的Service中。使用范围
首先,辅助功能不可能直接操作外部对象,辅助功能只能在本进程调用指定系统方法,由系统再分发给指定外部对象,辅助功能做的事基本和用户能做的差不多(比如回复微信, 微信抢红包)
AccessibilityService用法
- 首先,
AccessibilityService
本质上也是一个Service, 所以在Manifest.xml 中还是需要注册对应的Service内容, 并且声明权限, 示例如下:
<service
android:name=".RobService"
android:enabled="true"
android:exported="true"
android:label="学习强国"
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/myaccessibility"/>
</service>
其中resource中的配置项是单独创建一个myaccessibility.xml文件
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask" // 监听的事件类型, 这里表示全局监听
android:accessibilityFeedbackType="feedbackGeneric" //反馈类型, 这里表示通用反馈
android:accessibilityFlags="flagDefault|flagRetrieveInteractiveWindows|flagIncludeNotImportantViews"
android:canRetrieveWindowContent="true" //表示该服务能否访问活动窗口中的内容.也就是如果你希望在服务中获取窗体内容的话,则需要设置其值为true.
android:notificationTimeout="100" // 接受事件的时间间隔
android:canPerformGestures="true" //分发手势
android:packageNames="com.tencent.mm"/>
在注册服务完成后, 就可以创建一个Service继承AccessibilityService, 并且实现它的
onServiceConnected
和onAccessibilityEvent
方法, 其中, 在onServiceConnected
方法中放一些资源的初始化操作, 此方法会在开启了AccessibilityService权限的时候被调用. 在onAccessibilityEvent
实现对事件的处理, 比如屏幕改变事件, 消息通知事件.-
控件的查找与操作
控件的操作主要分为几个步骤:获取到当前界面的根节点
AccessibilityNodeInfo rootNode = getRootInActiveWindow();
-
通过id或者text文本查找到对应的节点
- 在sdk-tools/tools目录下, 有一个monitor.bat脚本, 双击这个脚本打开
Android Device Monitor
- 在电脑连接了安卓手机并且开启了USB调试模式的情况下,
Android Device Monitor
会显示出对应的手机, 点击上面的Dump View
按钮, 此时工具会自动对当前手机界面进行分析, 并生成一个dump -
最后得到类似下图, 用鼠标移动到对应的控件上, 即可看到控件的属性和文本等
- 在sdk-tools/tools目录下, 有一个monitor.bat脚本, 双击这个脚本打开
对节点进行操作, 这里要注意到, 有一些程序做了特殊处理, 有时候在
Android Device Monitor
看到clickable属性为true, 但不一定它就会响应click事件, 是否响应事件要按实际情况分析.
//获取当前聊天页面的根布局
AccessibilityNodeInfo rootNode = getRootInActiveWindow();
List<AccessibilityNodeInfo> listChat = rootNode.findAccessibilityNodeInfosByViewId("android:id/text1"); //根据id查找控件
//List<AccessibilityNodeInfo> listChat = rootNode.findAccessibilityNodeInfosByText("发送"); //根据文本查找控件
for (AccessibilityNodeInfo nodeInfo : listChatRecord) {
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK); //点击控件
}
- 手势
Android提供了特殊类型的触摸屏事件,如捏合,双击,滚动,长按和退缩。这些都被称为手势。
//发送一个点击事件
Path mPath=new Path();//线性的path代表手势路径,点代表按下,封闭的没用
mPath.moveTo(500, 500);
service.dispatchGesture(new GestureDescription.Builder().addStroke(new GestureDescription.StrokeDescription
(mPath, 手势开始时间比如立即开始0, 手势总时长比如100)).build(), 回调函数(可以为null), 回调的线程(null表示主线程));
扩展使用
- 使用webview通过js调用android代码
在android的方法定义中, 加上@JavaScriptInterface
注释后, 在webView.addJavascriptInterface(new JsInterface(), "Android");
然后在js中执行"Android.方法名"
附录
accessibilityEventTypes的取值
| constant | value | 描述 |
| - | - | - |
| typeAllMask|ffffffff|所有类型的事件
|typeAnnouncement|4000|一个应用产生一个通知事件
|typeAssistReadingContext|1000000|辅助用户读取当前屏幕事件
|typeContextClicked|800000|view中上下文点击事件
|typeGestureDetectionEnd|80000|监测到的手势事件完成
|typeGestureDetectionStart|40000|开始手势监测事件
|typeNotificationStateChanged|40|收到notification弹出消息事件
|typeTouchExplorationGestureEnd|400|触摸浏览事件完成
|typeTouchExplorationGestureStart|200|触摸浏览事件开始
|typeTouchInteractionEnd|200000|用户触屏事件结束
|typeTouchInteractionStart|100000|触摸屏幕事件开始
|typeViewAccessibilityFocusCleared|10000|无障碍焦点事件清除
|typeViewAccessibilityFocused|8000|获得无障碍的焦点事件
|typeViewClicked|1|点击事件
|typeViewFocused|8|view获取到焦点事件
|typeViewHoverEnter|80|一个view的悬停事件
|typeViewHoverExit|100|一个view的悬停事件结束,悬停离开该view
|typeViewLongClicked|2|view的长按事件
|typeViewScrolled|1000|view的滚动事件,adapterview、scrollview
|typeViewSelected|4|view选中,一般是具有选中属性的view,例如adapter
|typeViewTextChanged|10|edittext中文字发生改变的事件
|typeViewTextSelectionChanged|2000|edittext文字选中发生改变事件
|typeViewTextTraversedAtMovementGranularity|20000|UIanimator中在一个视图文本中进行遍历会产生这个事件,多个粒度遍历文本。一般用于语音阅读context
|typeWindowContentChanged|800|窗口的内容发生变化,或者更具体的子树根布局变化事件
|typeWindowStateChanged|20|新的弹出层导致的窗口变化(dialog、menu、popupwindow)
|typeWindowsChanged|400000|屏幕上的窗口变化事件,需要API 21+`accessibilityFeedbackType 此服务提供的反馈类型
constant | value | 描述 |
---|---|---|
feedbackAllMask | ffffffff | 取消所有的可用反馈方式 |
feedbackAudible | 4 | 可听见的(非语音反馈) |
feedbackGeneric | 10 | 通用反馈 |
feedbackHaptic | 2 | 触觉反馈(震动) |
feedbackSpoken | 1 | 语音反馈 |
feedbackVisual | 8 | 视觉反馈 |
- accessibilityFlags 辅助功能附加的标志,多个使用 ' | '分隔
constant | value | 描述 |
---|---|---|
flagDefault | 1 | 默认的配置 |
flagEnableAccessibilityVolume | 80 | 这个标志要求系统内所有的音频通道,使用由STREAM_ACCESSIBILTY音量控制USAGE_ASSISTANCE_ACCESSIBILITY |
flagIncludeNotImportantViews | 2 | 表示可获取到一些被表示为辅助功能无权获取到的view |
flagReportViewIds | 10 | 使用该flag表示可获取到view的ID |
flagRequestAccessibilityButton | 100 | 如果辅助功能可用,提供一个辅助功能按钮在系统的导航栏 API 26+ |
flagRequestEnhancedWebAccessibility | 8 | 此类扩展的目的是为WebView中呈现的内容提供更好的辅助功能支持。这种扩展的一个例子是从一个安全的来源注入JavaScript。如果至少有一个具有此标志的辅助功能服务, 则系统将使能增强的web辅助功能。因此, 清除此标志并不保证该设备不会使能增强的web辅助功能, 因为可能有另一个使能的服务在使用它。 |
flagRequestFilterKeyEvents | 20 | 能够监听到系统的物理按键 |
flagRequestFingerprintGestures | 200 | 监听系统的指纹手势 API 26+ |
flagRequestTouchExplorationMode | 4 | 系统进入触控探索模式。出现一个鼠标在用户的界面 |
flagRetrieveInteractiveWindows | 40 | 该标志知识的辅助服务要访问所有交互式窗口内容的系统,这个标志没有被设置时,服务不会收到TYPE_WINDOWS_CHANGE事件。 |