Android自动化测试中AccessibilityService获取控件信息(1)
AccessibilityService为一个响应用户发送AccessibilityEvent事件的服务类,主要用作对于一些辅助功能的实现中。对于某些方面有缺陷的人群,可以通过辅助功能反馈给用户。
AccessibilityService的介绍,网络上有很多,我就不多做介绍了。我把怎么实现它跟大家分享一下,以及怎么把它跟Android自动化测试扯上关系的学习过程给大家介绍一下。
第一步 编写辅助程序
创建一个Android项目,创建一个服务类,继承AccessibilityService。
- import android.accessibilityservice.AccessibilityService;
- import android.accessibilityservice.AccessibilityServiceInfo;
- import android.annotation.SuppressLint;
- import android.util.Log;
- import android.view.accessibility.AccessibilityEvent;
- import android.view.accessibility.AccessibilityNodeInfo;
- @SuppressLint("NewApi")
- public class MyAccessibility extends AccessibilityService {
- private static final String TAG = "MyAccessibility";
- String[] PACKAGES = { "com.android.settings" };
- @Override
- protected void onServiceConnected() {
- Log.i(TAG, "config success!");
- AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo();
- // accessibilityServiceInfo.packageNames = PACKAGES;
- accessibilityServiceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
- accessibilityServiceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;
- accessibilityServiceInfo.notificationTimeout = 1000;
- setServiceInfo(accessibilityServiceInfo);
- }
- @SuppressLint("NewApi")
- @Override
- public void onAccessibilityEvent(AccessibilityEvent event) {
- // TODO Auto-generated method stub
- int eventType = event.getEventType();
- String eventText = "";
- Log.i(TAG, "==============Start====================");
- switch (eventType) {
- case AccessibilityEvent.TYPE_VIEW_CLICKED:
- eventText = "TYPE_VIEW_CLICKED";
- break;
- case AccessibilityEvent.TYPE_VIEW_FOCUSED:
- eventText = "TYPE_VIEW_FOCUSED";
- break;
- case AccessibilityEvent.TYPE_VIEW_LONG_CLICKED:
- eventText = "TYPE_VIEW_LONG_CLICKED";
- break;
- case AccessibilityEvent.TYPE_VIEW_SELECTED:
- eventText = "TYPE_VIEW_SELECTED";
- break;
- case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
- eventText = "TYPE_VIEW_TEXT_CHANGED";
- break;
- case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
- eventText = "TYPE_WINDOW_STATE_CHANGED";
- break;
- case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
- eventText = "TYPE_NOTIFICATION_STATE_CHANGED";
- break;
- case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END:
- eventText = "TYPE_TOUCH_EXPLORATION_GESTURE_END";
- break;
- case AccessibilityEvent.TYPE_ANNOUNCEMENT:
- eventText = "TYPE_ANNOUNCEMENT";
- break;
- case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START:
- eventText = "TYPE_TOUCH_EXPLORATION_GESTURE_START";
- break;
- case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
- eventText = "TYPE_VIEW_HOVER_ENTER";
- break;
- case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT:
- eventText = "TYPE_VIEW_HOVER_EXIT";
- break;
- case AccessibilityEvent.TYPE_VIEW_SCROLLED:
- eventText = "TYPE_VIEW_SCROLLED";
- break;
- case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED:
- eventText = "TYPE_VIEW_TEXT_SELECTION_CHANGED";
- break;
- case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
- eventText = "TYPE_WINDOW_CONTENT_CHANGED";
- break;
- }
- eventText = eventText + ":" + eventType;
- Log.i(TAG, eventText);
- Log.i(TAG, "=============END=====================");
- }
- @Override
- public void onInterrupt() {
- // TODO Auto-generated method stub
- }
- }
该服务类继承了3个方法:
1.onServiceConnected():绑定服务所用方法,一些初始化的操作放在这里面。
2.onAccessibilityEvent(AccessibilityEvent event):响应AccessibilityEvent的事件,在用户操作的过程中,系统不断的发送sendAccessibiltyEvent(AccessibilityEvent event);然后通过onAccessibilityEvent()可以捕捉到该事件,然后分析。
3.public void onInterrupt():打断获取事件的过程,本例中暂不适用。
在onServiceConnected()我们做了一些初始化的工作,通过AccessibilityServiceInfo设置了AccessibilityService的一些参数
//ccessibilityServiceInfo.packageNames = PACKAGES:响应某个应用的时间,包名为应用的包名;可以用String[]对象传入多包。如果不设置,默认响应所有应用的事件。
ssibilityServiceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK:响应时间的类型,事件分很多种:单击、长按、滑动等,需要指定,我设置了所有事件都响应:TYPES_ALL_MASK。
cessibilityServiceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN:设置回馈给用户的方式,是语音播出还是振动。这个我们稍后尝试配置一些TTS引擎,让它发音。
cessibilityServiceInfo.notificationTimeout = 1000:看意思就能明白,响应时间的设置。
接下来我们要在配置文件AndroidManifest.xml配置该服务:
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.spreadtrum.accessibilityservice"
- android:versionCode="1"
- android:versionName="1.0" >
- <uses-sdk
- android:minSdkVersion="8"
- android:targetSdkVersion="17" />
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@style/AppTheme" >
- <service
- android:name=".MyAccessibility"
- android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" >
- <intent-filter>
- <action android:name="android.accessibilityservice.AccessibilityService" />
- </intent-filter>
- </service>
- </application>
- </manifest>
这些设置很重要,因为AccessibilityService服务不是用户去开启的。我们无法用startService()方法启动它,所以我连activity都没写。因为它是通过该配置让系统自动识别的,识别成功后,就会在我们手机里面看到该服务,然后需要手动开启该服务(所以千万别粗心写错,我就是因为少写了一个单词,然后安装上app,一直没反应,它也不报错,害我找错找了半天,后来发现是自己的低级错误。)。
现在我们就可以将应用部署到手机上,部署成功后我们点:设置->辅助功能。找到我们的服务,如图:
看到我们的应用会显示在其中,默认是关闭的;如果你在此没看见你的服务,那么你应该核对一下你的配置文件,确保配置正确;点进去,打开该服务。
然后我们就可以随意在你的手机设备上操作,看看log输出:
我们就会看到我们点击的事件,被服务在后台捕捉到了。接下来,我们尝试着从我们捕捉到的时间中获得我们有用的时间,AccessibilityEvent API 中有一个getSource()方法。得到的是AccessibilityNodeInfo,试着获得这个对象。我们看看是什么东西。
第二步 获得控件信息
在调用这个getSource()方法时,需要事先配置一下,也就是需要android:canRetrieveWindowContent="true",这个属性是API14(android4.0)给出的,它的配置方法受限于需要采用XML初始化的方式配置AccessibilityService,也就是我们现在不在onServiceConnected()里配置初始化信息,改在xml里实现。那么我们现在就要将上面的程序修改一下。首先,删除onServiceConnected()的配置(因为getSource()不是所有的操作都有该方法,所以得在特定的方法中实现该方法,我们选择在TYPE_VIEW_CLICKED里实现),修改后的服务类如下:
- package com.spreadtrum.accessibilityservice;
- import android.accessibilityservice.AccessibilityService;
- import android.accessibilityservice.AccessibilityServiceInfo;
- import android.annotation.SuppressLint;
- import android.util.Log;
- import android.view.accessibility.AccessibilityEvent;
- import android.view.accessibility.AccessibilityNodeInfo;
- @SuppressLint("NewApi")
- public class MyAccessibility extends AccessibilityService {
- private static final String TAG = "MyAccessibility";
- String[] PACKAGES = { "com.android.settings" };
- @Override
- protected void onServiceConnected() {
- Log.i(TAG, "config success!");
- }
- @SuppressLint("NewApi")
- @Override
- public void onAccessibilityEvent(AccessibilityEvent event) {
- // TODO Auto-generated method stub
- int eventType = event.getEventType();
- String eventText = "";
- switch (eventType) {
- case AccessibilityEvent.TYPE_VIEW_CLICKED:
- Log.i(TAG, "==============Start====================");
- eventText = "TYPE_VIEW_CLICKED";
- AccessibilityNodeInfo noteInfo = event.getSource();
- Log.i(TAG, noteInfo.toString());
- Log.i(TAG, "=============END=====================");
- break;
- case AccessibilityEvent.TYPE_VIEW_FOCUSED:
- eventText = "TYPE_VIEW_FOCUSED";
- break;
- case AccessibilityEvent.TYPE_VIEW_LONG_CLICKED:
- eventText = "TYPE_VIEW_LONG_CLICKED";
- break;
- case AccessibilityEvent.TYPE_VIEW_SELECTED:
- eventText = "TYPE_VIEW_SELECTED";
- break;
- case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
- eventText = "TYPE_VIEW_TEXT_CHANGED";
- break;
- case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
- eventText = "TYPE_WINDOW_STATE_CHANGED";
- break;
- case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
- eventText = "TYPE_NOTIFICATION_STATE_CHANGED";
- break;
- case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END:
- eventText = "TYPE_TOUCH_EXPLORATION_GESTURE_END";
- break;
- case AccessibilityEvent.TYPE_ANNOUNCEMENT:
- eventText = "TYPE_ANNOUNCEMENT";
- break;
- case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START:
- eventText = "TYPE_TOUCH_EXPLORATION_GESTURE_START";
- break;
- case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
- eventText = "TYPE_VIEW_HOVER_ENTER";
- break;
- case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT:
- eventText = "TYPE_VIEW_HOVER_EXIT";
- break;
- case AccessibilityEvent.TYPE_VIEW_SCROLLED:
- eventText = "TYPE_VIEW_SCROLLED";
- break;
- case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED:
- eventText = "TYPE_VIEW_TEXT_SELECTION_CHANGED";
- break;
- case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
- eventText = "TYPE_WINDOW_CONTENT_CHANGED";
- break;
- }
- }
- @Override
- public void onInterrupt() {
- // TODO Auto-generated method stub
- }
- }
然后我们要在res文件写创建一个XML文件夹,里面新建accessibility.xml配置文件。
在accessibility.xml配置一些属性:
- <?xml version="1.0" encoding="UTF-8"?>
- <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
- android:accessibilityFeedbackType="feedbackSpoken"
- android:notificationTimeout="100"
- android:canRetrieveWindowContent="true"
- />
重点就是标红的这一句,这样我们就能在程序中得到AccessibilityNodeInfo对象。然后在AndroidManifest.xml配置文件里引用该XML文件:
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.spreadtrum.accessibilityservice"
- android:versionCode="1"
- android:versionName="1.0" >
- <uses-sdk
- android:minSdkVersion="8"
- android:targetSdkVersion="17" />
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@style/AppTheme" >
- <service
- android:name=".MyAccessibility"
- 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>
- </application>
- </manifest>
然后我们部署应用到手机设备,看输出情况:
注意:
在你配置成功后,有可能会报空指针错误,但有可能你已经配置正确,只是你点的对象有问题。因为在API文档中有一句话:如果发起的窗口事件仍然是活动的窗口,该调用将会返回一个对象,否则,会返回null!所以首先确保你刚开始点击的那个控件所在的窗口要是活动的状态,不然也会报null。例如:我刚开始点击HOME界面的所有应用按钮进入到应用的列表,这时候就会报错,如果你是点击通讯录触发服务的,就没问题;原理我也不太清楚,所以不敢大放厥词,反正在一个应用中来回点是没问题的;这个随着深入的了解后,相信会有答案的。
Android自动化测试中AccessibilityService获取控件信息(1)的更多相关文章
- Android自动化测试中AccessibilityService获取控件信息(2)-三种方式对比
Android自动化测试中AccessibilityService获取控件信息(2)-三种方式对比 上一篇文章: Android自动化测试中AccessibilityService获取控件信息(1 ...
- UiAutomator源码分析之获取控件信息
根据上一篇文章<UiAutomator源码分析之注入事件>开始时提到的计划,这一篇文章我们要分析的是第二点: 如何获取控件信息 我们在测试脚本中初始化一个UiObject的时候通常是像以下 ...
- UiAutomator源代码分析之获取控件信息
依据上一篇文章<UiAutomator源代码分析之注入事件>開始时提到的计划,这一篇文章我们要分析的是第二点: 怎样获取控件信息 我们在測试脚本中初始化一个UiObject的时候一般是像下 ...
- Android开发中目前流行控件和知识点总结
Android开发中目前流行控件和知识点总结 1.SlidingMenu 滑动菜单 应用案例:Facebook . Path 2.0 .人人.网易新闻 下载地址: https://github.c ...
- Android Studio中Button等控件的Text中字符串默认大写的解决方法
初学Android的时候,在Android Studio中xml里面添加一个Button.EditText等控件后,它的Text总是会显示大写,即使你输入的字符串是小写也不行,控制字符串大小写的属性是 ...
- Android 布局中 如何使控件居中
首先要分两种不同情况,在两种不同的布局方式下:LinearLayout 和RelativeLayout 1. LinearLayout a). android:layout_gravity=" ...
- android自动化测试中hierarchyviewer和uiautomatorviewer获取控件信息的方式比对
http://blog.csdn.net/itfootball/article/details/21777835 http://blog.csdn.net/chenbang110/article/de ...
- 获取控件id
普通状态中JS获取控件IDdocument.getElementById('controlID'); JS获取父窗口控件IDwindow.parent.document.getElementById( ...
- android 获取屏幕的高度和宽度、获取控件在屏幕中的位置、获取屏幕中控件的高度和宽度
(一)获取屏幕的高度和宽度 有两种方法: 方法1: WindowManager wm = (WindowManager) getContext().getSystemService(Context.W ...
随机推荐
- mysql日常运维
一.Linux内核和发行版本 uname -a cat /etc/issue 二.glibc的版本 /lib/libc.so.6 ---没有man函数据的动态链接库 三.MySQL的版 ...
- Winform中用comboBox来选择显示Dataset中表格数据
这是一次偷懒的尝试,因为每次都必须打开代码,调试才能看见数据,发现问题.也是借鉴了调试中查看dataset数据的模式,查看不同表格.经历一番研究,总算实现了想要的效果了,故作此一笔记.与人共享. 界面 ...
- (C/C++学习笔记) 十四. 动态分配
十四. 动态分配 ● C语言实现动态数组 C语言实现动态数组,克服静态数组大小固定的缺陷 C语言中,数组长度必须在创建数组时指定,并且只能是一个常数,不能是变量.一旦定义了一个数组,系统将为它分配一个 ...
- DevExpress v18.1新版亮点——WinForms篇(四)
用户界面套包DevExpress v18.1日前终于正式发布,本站将以连载的形式为大家介绍各版本新增内容.本文将介绍了DevExpress WinForms v18.1 的新功能,快来下载试用新版本! ...
- 201621123010《Java程序设计》第9周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 2. 书面作业 本次作业题集集合 1. List中指定元素的删除(题集题目) 1.1 实验总结.并回答:列举至 ...
- UIWebView和UIWebViewDelegate的基本用法 (转)
一.UIWebView主要有三种方法实现页面的装载,分别是: 1. (void)loadRequest:(NSURLRequest *)request; (直接装载URL) 2. (void)loa ...
- ssh key建立后不能clone问题
在建立了ssh密钥对之后,要将私钥添加,公钥key添加到gitlab的ssh keys里. 添加成功后,这个时候,你可以clone了! 总结:如果是遇到重复输入密码的情况,可能是ssh-key,的私钥 ...
- Swift中WebView的应用
WebView控件是做网络应用开发中使用最多的控件,直接在WebView内部指定一个网页地址就可以访问网页了,同时也可以实现UIWebViewDelegate协议实现相应的方法去控制内容的加载和处理. ...
- 配置阿里云ECS支持IPv6,解决苹果app审核失败问题
前几天iOS的App提交给苹果审核没通过,给出的原因是:该应用在 IPv6 的环境下无法使用.检查发现:阿里云优化过的系统没有启用IPv6协议,需要配置启用一下,但是只单独启用IPv6也是无法直接提供 ...
- spark 与 Hadoop 融合后启动 slf4j提示Class path contains multiple SLF4J bindings
相关参考文献: https://www.oschina.net/question/93435_174549 警告信息如下: 看起来明明就是一个文件,怎么还提示multiple bindings呢,sl ...