溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android自動化測試中AccessibilityService獲取控件信息

發(fā)布時間:2020-06-21 15:20:28 來源:網(wǎng)絡 閱讀:3061 作者:zhukev 欄目:移動開發(fā)

原著出處(這里順便對先驅(qū)表示感謝,省得我多走彎路,所以這里直接轉載過來,我們沒有必要做重新造輪子的事情,既然先驅(qū)們已經(jīng)幫我們把輪子造好了):http://blog.csdn.net/itfootball/article/details/21953763


AccessibilityService為一個響應用戶發(fā)送AccessibilityEvent事件的服務類,主要用作對于一些輔助功能的實現(xiàn)中。對于某些方面有缺陷的人群,可以通過輔助功能反饋給用戶。

        AccessibilityService的介紹,網(wǎng)絡上有很多,我就不多做介紹了。我把怎么實現(xiàn)它跟大家分享一下,以及怎么把它跟Android自動化測試扯上關系的學習過程給大家介紹一下。

 

  第一步  編寫輔助程序 

 

        創(chuàng)建一個Android項目,創(chuàng)建一個服務類,繼承AccessibilityService。

 

[java] view plaincopyAndroid自動化測試中AccessibilityService獲取控件信息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.   
  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的事件,在用戶操作的過程中,系統(tǒng)不斷的發(fā)送sendAccessibiltyEvent(AccessibilityEvent event);然后通過onAccessibilityEvent()可以捕捉到該事件,然后分析。

        3.public void onInterrupt():打斷獲取事件的過程,本例中暫不適用。

        在onServiceConnected()我們做了一些初始化的工作,通過AccessibilityServiceInfo設置了AccessibilityService的一些參數(shù)

       //ccessibilityServiceInfo.packageNames = PACKAGES:響應某個應用的時間,包名為應用的包名;可以用String[]對象傳入多包。如果不設置,默認響應所有應用的事件。

        ssibilityServiceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK:響應時間的類型,事件分很多種:單擊、長按、滑動等,需要指定,我設置了所有事件都響應:TYPES_ALL_MASK。

        cessibilityServiceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN:設置回饋給用戶的方式,是語音播出還是振動。這個我們稍后嘗試配置一些TTS引擎,讓它發(fā)音。

        cessibilityServiceInfo.notificationTimeout = 1000:看意思就能明白,響應時間的設置。

        接下來我們要在配置文件AndroidManifest.xml配置該服務:

 

[java] view plaincopyAndroid自動化測試中AccessibilityService獲取控件信息Android自動化測試中AccessibilityService獲取控件信息
  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都沒寫。因為它是通過該配置讓系統(tǒng)自動識別的,識別成功后,就會在我們手機里面看到該服務,然后需要手動開啟該服務(所以千萬別粗心寫錯,我就是因為少寫了一個單詞,然后安裝上app,一直沒反應,它也不報錯,害我找錯找了半天,后來發(fā)現(xiàn)是自己的低級錯誤。)。

        現(xiàn)在我們就可以將應用部署到手機上,部署成功后我們點:設置->輔助功能。找到我們的服務,如圖:

 

 

Android自動化測試中AccessibilityService獲取控件信息

       

        看到我們的應用會顯示在其中,默認是關閉的;如果你在此沒看見你的服務,那么你應該核對一下你的配置文件,確保配置正確;點進去,打開該服務。

然后我們就可以隨意在你的手機設備上操作,看看log輸出:

 

Android自動化測試中AccessibilityService獲取控件信息

       

        我們就會看到我們點擊的事件,被服務在后臺捕捉到了。接下來,我們嘗試著從我們捕捉到的時間中獲得我們有用的時間,AccessibilityEvent API 中有一個getSource()方法。得到的是AccessibilityNodeInfo,試著獲得這個對象。我們看看是什么東西。

 

   第二步 獲得控件信息

 

        在調(diào)用這個getSource()方法時,需要事先配置一下,也就是需要android:canRetrieveWindowContent="true",這個屬性是API14(android4.0)給出的,它的配置方法受限于需要采用XML初始化的方式配置AccessibilityService,也就是我們現(xiàn)在不在onServiceConnected()里配置初始化信息,改在xml里實現(xiàn)。那么我們現(xiàn)在就要將上面的程序修改一下。首先,刪除onServiceConnected()的配置(因為getSource()不是所有的操作都有該方法,所以得在特定的方法中實現(xiàn)該方法,我們選擇在TYPE_VIEW_CLICKED里實現(xiàn)),修改后的服務類如下:

 

[java] view plaincopyAndroid自動化測試中AccessibilityService獲取控件信息Android自動化測試中AccessibilityService獲取控件信息
  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文件寫創(chuàng)建一個XML文件夾,里面新建accessibility.xml配置文件。

 

Android自動化測試中AccessibilityService獲取控件信息

      

        在accessibility.xml配置一些屬性:

 

[java] view plaincopyAndroid自動化測試中AccessibilityService獲取控件信息Android自動化測試中AccessibilityService獲取控件信息
  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 plaincopyAndroid自動化測試中AccessibilityService獲取控件信息Android自動化測試中AccessibilityService獲取控件信息
  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>  

       

         然后我們部署應用到手機設備,看輸出情況:

 

Android自動化測試中AccessibilityService獲取控件信息

 

        注意:

        在你配置成功后,有可能會報空指針錯誤,但有可能你已經(jīng)配置正確,只是你點的對象有問題。因為在API文檔中有一句話:如果發(fā)起的窗口事件仍然是活動的窗口,該調(diào)用將會返回一個對象,否則,會返回null!所以首先確保你剛開始點擊的那個控件所在的窗口要是活動的狀態(tài),不然也會報null。例如:我剛開始點擊HOME界面的所有應用按鈕進入到應用的列表,這時候就會報錯,如果你是點擊通訊錄觸發(fā)服務的,就沒問題;原理我也不太清楚,所以不敢大放厥詞,反正在一個應用中來回點是沒問題的;這個隨著深入的了解后,相信會有答案的。


第三步 獲得所有窗體控件

已經(jīng)做到了獲得控件信息,但是AccessibilityEvent.getSource()得到的是被點擊的單體對象。我們需要獲得是整個窗口的對象,在API16中AccessibilityService新引入的方法getRootInActiveWindow()可以滿足我們的要求,所以我們用這個方法得到整個窗口,然后遍歷得到所有子節(jié)點。

[java] view plaincopyAndroid自動化測試中AccessibilityService獲取控件信息Android自動化測試中AccessibilityService獲取控件信息
  1. AccessibilityNodeInfo rowNode = getRootInActiveWindow();  
  2.   if (rowNode == null) {  
  3.    Log.i(TAG, "noteInfo is null");  
  4.    return;  
  5.   } else {  
  6.    recycle(rowNode);  
  7.   }  
  8.   Log.i(TAG, "==============================================");  


其中循環(huán)的方法recycle():

[java] view plaincopyAndroid自動化測試中AccessibilityService獲取控件信息Android自動化測試中AccessibilityService獲取控件信息
  1. public void recycle(AccessibilityNodeInfo info) {  
  2.         if (info.getChildCount() == 0) {  
  3.             Log.i(TAG, "child widget----------------------------" + info.getClassName());  
  4.             Log.i(TAG, "showDialog:" + info.canOpenPopup());  
  5.             Log.i(TAG, "Text:" + info.getText());  
  6.             Log.i(TAG, "windowId:" + info.getWindowId());  
  7.         } else {  
  8.             for (int i = 0; i < info.getChildCount(); i++) {  
  9.                 if(info.getChild(i)!=null){  
  10.                     recycle(info.getChild(i));  
  11.                 }  
  12.             }  
  13.         }  
  14.     }  

打印輸出的信息有:控件名、是否點擊彈出對話框、窗口ID;你還可以查看API里的方法,獲得你想要的信息。

部署到手機上,測試結果如下:

Android自動化測試中AccessibilityService獲取控件信息


總結

 

三種方式學習完了,對比一下各種方式的優(yōu)缺點!

 

Android自動化測試中AccessibilityService獲取控件信息


向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI