分类: android自动化测试2014-03-24 15:31 3455人阅读 评论(16) 收藏 举报
 

目录(?)[+]

 

AccessibilityService为一个响应用户发送AccessibilityEvent事件的服务类,主要用作对于一些辅助功能的实现中。对于某些方面有缺陷的人群,可以通过辅助功能反馈给用户。

AccessibilityService的介绍,网络上有很多,我就不多做介绍了。我把怎么实现它跟大家分享一下,以及怎么把它跟Android自动化测试扯上关系的学习过程给大家介绍一下。

第一步  编写辅助程序

创建一个Android项目,创建一个服务类,继承AccessibilityService。

  1. import android.accessibilityservice.AccessibilityService;
  2. import android.accessibilityservice.AccessibilityServiceInfo;
  3. import android.annotation.SuppressLint;
  4. import android.util.Log;
  5. import android.view.accessibility.AccessibilityEvent;
  6. import android.view.accessibility.AccessibilityNodeInfo;
  7. @SuppressLint("NewApi")
  8. public class MyAccessibility extends AccessibilityService {
  9. private static final String TAG = "MyAccessibility";
  10. String[] PACKAGES = { "com.android.settings" };
  11. @Override
  12. protected void onServiceConnected() {
  13. Log.i(TAG, "config success!");
  14. AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo();
  15. // accessibilityServiceInfo.packageNames = PACKAGES;
  16. accessibilityServiceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
  17. accessibilityServiceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;
  18. accessibilityServiceInfo.notificationTimeout = 1000;
  19. setServiceInfo(accessibilityServiceInfo);
  20. }
  21. @SuppressLint("NewApi")
  22. @Override
  23. public void onAccessibilityEvent(AccessibilityEvent event) {
  24. // TODO Auto-generated method stub
  25. int eventType = event.getEventType();
  26. String eventText = "";
  27. Log.i(TAG, "==============Start====================");
  28. switch (eventType) {
  29. case AccessibilityEvent.TYPE_VIEW_CLICKED:
  30. eventText = "TYPE_VIEW_CLICKED";
  31. break;
  32. case AccessibilityEvent.TYPE_VIEW_FOCUSED:
  33. eventText = "TYPE_VIEW_FOCUSED";
  34. break;
  35. case AccessibilityEvent.TYPE_VIEW_LONG_CLICKED:
  36. eventText = "TYPE_VIEW_LONG_CLICKED";
  37. break;
  38. case AccessibilityEvent.TYPE_VIEW_SELECTED:
  39. eventText = "TYPE_VIEW_SELECTED";
  40. break;
  41. case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
  42. eventText = "TYPE_VIEW_TEXT_CHANGED";
  43. break;
  44. case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
  45. eventText = "TYPE_WINDOW_STATE_CHANGED";
  46. break;
  47. case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
  48. eventText = "TYPE_NOTIFICATION_STATE_CHANGED";
  49. break;
  50. case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END:
  51. eventText = "TYPE_TOUCH_EXPLORATION_GESTURE_END";
  52. break;
  53. case AccessibilityEvent.TYPE_ANNOUNCEMENT:
  54. eventText = "TYPE_ANNOUNCEMENT";
  55. break;
  56. case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START:
  57. eventText = "TYPE_TOUCH_EXPLORATION_GESTURE_START";
  58. break;
  59. case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
  60. eventText = "TYPE_VIEW_HOVER_ENTER";
  61. break;
  62. case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT:
  63. eventText = "TYPE_VIEW_HOVER_EXIT";
  64. break;
  65. case AccessibilityEvent.TYPE_VIEW_SCROLLED:
  66. eventText = "TYPE_VIEW_SCROLLED";
  67. break;
  68. case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED:
  69. eventText = "TYPE_VIEW_TEXT_SELECTION_CHANGED";
  70. break;
  71. case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
  72. eventText = "TYPE_WINDOW_CONTENT_CHANGED";
  73. break;
  74. }
  75. eventText = eventText + ":" + eventType;
  76. Log.i(TAG, eventText);
  77. Log.i(TAG, "=============END=====================");
  78. }
  79. @Override
  80. public void onInterrupt() {
  81. // TODO Auto-generated method stub
  82. }
  83. }

该服务类继承了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配置该服务:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.spreadtrum.accessibilityservice"
  4. android:versionCode="1"
  5. android:versionName="1.0" >
  6. <uses-sdk
  7. android:minSdkVersion="8"
  8. android:targetSdkVersion="17" />
  9. <application
  10. android:allowBackup="true"
  11. android:icon="@drawable/ic_launcher"
  12. android:label="@string/app_name"
  13. android:theme="@style/AppTheme" >
  14. <service
  15. android:name=".MyAccessibility"
  16. android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" >
  17. <intent-filter>
  18. <action android:name="android.accessibilityservice.AccessibilityService" />
  19. </intent-filter>
  20. </service>
  21. </application>
  22. </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里实现),修改后的服务类如下:

  1. package com.spreadtrum.accessibilityservice;
  2. import android.accessibilityservice.AccessibilityService;
  3. import android.accessibilityservice.AccessibilityServiceInfo;
  4. import android.annotation.SuppressLint;
  5. import android.util.Log;
  6. import android.view.accessibility.AccessibilityEvent;
  7. import android.view.accessibility.AccessibilityNodeInfo;
  8. @SuppressLint("NewApi")
  9. public class MyAccessibility extends AccessibilityService {
  10. private static final String TAG = "MyAccessibility";
  11. String[] PACKAGES = { "com.android.settings" };
  12. @Override
  13. protected void onServiceConnected() {
  14. Log.i(TAG, "config success!");
  15. }
  16. @SuppressLint("NewApi")
  17. @Override
  18. public void onAccessibilityEvent(AccessibilityEvent event) {
  19. // TODO Auto-generated method stub
  20. int eventType = event.getEventType();
  21. String eventText = "";
  22. switch (eventType) {
  23. case AccessibilityEvent.TYPE_VIEW_CLICKED:
  24. Log.i(TAG, "==============Start====================");
  25. eventText = "TYPE_VIEW_CLICKED";
  26. AccessibilityNodeInfo noteInfo = event.getSource();
  27. Log.i(TAG, noteInfo.toString());
  28. Log.i(TAG, "=============END=====================");
  29. break;
  30. case AccessibilityEvent.TYPE_VIEW_FOCUSED:
  31. eventText = "TYPE_VIEW_FOCUSED";
  32. break;
  33. case AccessibilityEvent.TYPE_VIEW_LONG_CLICKED:
  34. eventText = "TYPE_VIEW_LONG_CLICKED";
  35. break;
  36. case AccessibilityEvent.TYPE_VIEW_SELECTED:
  37. eventText = "TYPE_VIEW_SELECTED";
  38. break;
  39. case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
  40. eventText = "TYPE_VIEW_TEXT_CHANGED";
  41. break;
  42. case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
  43. eventText = "TYPE_WINDOW_STATE_CHANGED";
  44. break;
  45. case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
  46. eventText = "TYPE_NOTIFICATION_STATE_CHANGED";
  47. break;
  48. case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END:
  49. eventText = "TYPE_TOUCH_EXPLORATION_GESTURE_END";
  50. break;
  51. case AccessibilityEvent.TYPE_ANNOUNCEMENT:
  52. eventText = "TYPE_ANNOUNCEMENT";
  53. break;
  54. case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START:
  55. eventText = "TYPE_TOUCH_EXPLORATION_GESTURE_START";
  56. break;
  57. case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
  58. eventText = "TYPE_VIEW_HOVER_ENTER";
  59. break;
  60. case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT:
  61. eventText = "TYPE_VIEW_HOVER_EXIT";
  62. break;
  63. case AccessibilityEvent.TYPE_VIEW_SCROLLED:
  64. eventText = "TYPE_VIEW_SCROLLED";
  65. break;
  66. case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED:
  67. eventText = "TYPE_VIEW_TEXT_SELECTION_CHANGED";
  68. break;
  69. case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
  70. eventText = "TYPE_WINDOW_CONTENT_CHANGED";
  71. break;
  72. }
  73. }
  74. @Override
  75. public void onInterrupt() {
  76. // TODO Auto-generated method stub
  77. }
  78. }

然后我们要在res文件写创建一个XML文件夹,里面新建accessibility.xml配置文件。

在accessibility.xml配置一些属性:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
  4. android:accessibilityFeedbackType="feedbackSpoken"
  5. android:notificationTimeout="100"
  6. android:canRetrieveWindowContent="true"
  7. />

重点就是标红的这一句,这样我们就能在程序中得到AccessibilityNodeInfo对象。然后在AndroidManifest.xml配置文件里引用该XML文件:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.spreadtrum.accessibilityservice"
  4. android:versionCode="1"
  5. android:versionName="1.0" >
  6. <uses-sdk
  7. android:minSdkVersion="8"
  8. android:targetSdkVersion="17" />
  9. <application
  10. android:allowBackup="true"
  11. android:icon="@drawable/ic_launcher"
  12. android:label="@string/app_name"
  13. android:theme="@style/AppTheme" >
  14. <service
  15. android:name=".MyAccessibility"
  16. android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" >
  17. <intent-filter>
  18. <action android:name="android.accessibilityservice.AccessibilityService" />
  19. </intent-filter>
  20. <meta-data
  21. android:name="android.accessibilityservice"
  22. android:resource="@xml/accessibility" />
  23. </service>
  24. </application>
  25. </manifest>

然后我们部署应用到手机设备,看输出情况:

        注意:

在你配置成功后,有可能会报空指针错误,但有可能你已经配置正确,只是你点的对象有问题。因为在API文档中有一句话:如果发起的窗口事件仍然是活动的窗口,该调用将会返回一个对象,否则,会返回null!所以首先确保你刚开始点击的那个控件所在的窗口要是活动的状态,不然也会报null。例如:我刚开始点击HOME界面的所有应用按钮进入到应用的列表,这时候就会报错,如果你是点击通讯录触发服务的,就没问题;原理我也不太清楚,所以不敢大放厥词,反正在一个应用中来回点是没问题的;这个随着深入的了解后,相信会有答案的。

 

Android自动化测试中AccessibilityService获取控件信息(1)的更多相关文章

  1. Android自动化测试中AccessibilityService获取控件信息(2)-三种方式对比

    Android自动化测试中AccessibilityService获取控件信息(2)-三种方式对比   上一篇文章: Android自动化测试中AccessibilityService获取控件信息(1 ...

  2. UiAutomator源码分析之获取控件信息

    根据上一篇文章<UiAutomator源码分析之注入事件>开始时提到的计划,这一篇文章我们要分析的是第二点: 如何获取控件信息 我们在测试脚本中初始化一个UiObject的时候通常是像以下 ...

  3. UiAutomator源代码分析之获取控件信息

    依据上一篇文章<UiAutomator源代码分析之注入事件>開始时提到的计划,这一篇文章我们要分析的是第二点: 怎样获取控件信息 我们在測试脚本中初始化一个UiObject的时候一般是像下 ...

  4. Android开发中目前流行控件和知识点总结

    Android开发中目前流行控件和知识点总结   1.SlidingMenu 滑动菜单 应用案例:Facebook . Path 2.0 .人人.网易新闻 下载地址: https://github.c ...

  5. Android Studio中Button等控件的Text中字符串默认大写的解决方法

    初学Android的时候,在Android Studio中xml里面添加一个Button.EditText等控件后,它的Text总是会显示大写,即使你输入的字符串是小写也不行,控制字符串大小写的属性是 ...

  6. Android 布局中 如何使控件居中

    首先要分两种不同情况,在两种不同的布局方式下:LinearLayout 和RelativeLayout 1. LinearLayout a). android:layout_gravity=" ...

  7. android自动化测试中hierarchyviewer和uiautomatorviewer获取控件信息的方式比对

    http://blog.csdn.net/itfootball/article/details/21777835 http://blog.csdn.net/chenbang110/article/de ...

  8. 获取控件id

    普通状态中JS获取控件IDdocument.getElementById('controlID'); JS获取父窗口控件IDwindow.parent.document.getElementById( ...

  9. android 获取屏幕的高度和宽度、获取控件在屏幕中的位置、获取屏幕中控件的高度和宽度

    (一)获取屏幕的高度和宽度 有两种方法: 方法1: WindowManager wm = (WindowManager) getContext().getSystemService(Context.W ...

随机推荐

  1. httpclient cookie保持会话

    COOKIE保持回话 httpclient4.x自带维护回话的功能,只要使用同一个httpclient且未关闭连接,就可以使用相同的回话来访问其他要求登陆验证的服务. 如果需要使用HttpClient ...

  2. 微信小程序-隐藏和显示自定义的导航

    微信小程序中不能直接操作window对象,document文档,跟html的树结构不相同. 实现类似导航的隐藏显示,如图效果: 点击网络显示或隐藏网络中包含的内容.其他类似. 如果是jquery很方便 ...

  3. 什么是REST API?

    REST指一组架构约束条件和原则,满足约束条件和原则的应用程序设计.架构,软件体系结构分为三部分:构建,用于描述计算机:连接器,用于描述构建的链接部分:配置将构建和连接器组成有机整体.web基本技术: ...

  4. 数据库schema的简介

    [参考]自百度百科 数据库中的Schema,为数据库对象的集合,一个用户一般对应一个schema. 官方定义如下: A schema is a collection of database objec ...

  5. JSON 数组的创建方式

    procedure TFormDZMD.Button1Click(Sender: TObject); var ja: ISuperObject; begin ja := SA([]); ja.AsAr ...

  6. 如何修改tomcat端口以及tomcat热部署

    一.修改tomcat端口 1.首先我们需要知道,http的默认端口是80,tomcat的默认端口是8080,也就是说,如果我们将tomcat的默认端口号修改为80,输入网址的时候就可以不用输入端口了, ...

  7. apache的<directory> 语句以及属性的含义

    在整完apache和tomcat的之后我觉得有必要把<directory>和它下面的属性捋顺一下 如何访问根目录下的目录http://192.168.1.12/test/ 第一.缺省apa ...

  8. python3:jsonpath-rw处理Json对象

    前提:接口自动化测试中,存在依赖情况:test_02的某个请求参数的值,需要依赖test_01返回结果中某个字段的数据,所以就先需要拿到返回数据中特定字段的值.这里使用到python中jsonpath ...

  9. 【机器学习算法】Boostrapping算法

    参考 1.AdaBoost从原理到实现: 完

  10. Softmax回归介绍

    把输入值当成幂指数求值,再正则化这些结果值.这个幂运算表示,更大的证据对应更大的假设模型(hypothesis)里面的乘数权重值.反之,拥有更少的证据意味着在假设模型里面拥有更小的乘数系数.假设模型里 ...