zoukankan      html  css  js  c++  java
  • Android自动化测试中AccessibilityService获取控件信息(1)

    Android自动化测试中AccessibilityService获取控件信息(1)

    分类: android自动化测试
     

    目录(?)[+]

     

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

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

      第一步  编写辅助程序 

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

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

          

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

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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.   
    7.     <uses-sdk  
    8.         android:minSdkVersion="8"  
    9.         android:targetSdkVersion="17" />  
    10.   
    11.     <application  
    12.         android:allowBackup="true"  
    13.         android:icon="@drawable/ic_launcher"  
    14.         android:label="@string/app_name"  
    15.         android:theme="@style/AppTheme" >  
    16.         <service  
    17.             android:name=".MyAccessibility"  
    18.             android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" >  
    19.             <intent-filter>  
    20.                 <action android:name="android.accessibilityservice.AccessibilityService" />  
    21.             </intent-filter>  
    22.         </service>  
    23.     </application>  
    24.   
    25. </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里实现),修改后的服务类如下:

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

          

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

          

            在accessibility.xml配置一些属性:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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文件:

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
    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.   
    7.     <uses-sdk  
    8.         android:minSdkVersion="8"  
    9.         android:targetSdkVersion="17" />  
    10.   
    11.     <application  
    12.         android:allowBackup="true"  
    13.         android:icon="@drawable/ic_launcher"  
    14.         android:label="@string/app_name"  
    15.         android:theme="@style/AppTheme" >  
    16.         <service  
    17.             android:name=".MyAccessibility"  
    18.             android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" >  
    19.             <intent-filter>  
    20.                 <action android:name="android.accessibilityservice.AccessibilityService" />  
    21.             </intent-filter>  
    22.   
    23.             <meta-data  
    24.                 android:name="android.accessibilityservice"  
    25.                 android:resource="@xml/accessibility" />  
    26.         </service>  
    27.     </application>  
    28.   
    29. </manifest>  

           

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

            注意:

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

     
  • 相关阅读:
    vue-cli项目中使用vw——相比flexible更原生的移动端解决方案
    android shap画圆(空心圆、实心圆)
    Android四大组件——Activity跳转动画、淡出淡入、滑出滑入、自定义退出进入
    HDU 3980 Paint Chain (sg函数)
    HDU 3951 Coin Game (简单博弈)
    HDU 1850 Being a Good Boy in Spring Festival (Nim博弈)
    HDU 3389 Game (阶梯博弈)
    HDU 3032 Nim or not Nim? (sg函数)
    HDU 1907 John (Nim博弈)
    HDU 4638 Group (线段树 | 树状数组 + 离线处理)
  • 原文地址:https://www.cnblogs.com/duanweishi/p/4460611.html
Copyright © 2011-2022 走看看