AccessibilityService是一個輔助類,可以監聽我們手機的焦點,窗口變化,按鈕點擊等等。實現它的服務需要在手機設置里面->輔助功能在這里面找到你自己實現的輔助類,然后打開它就可以進行我們一系列的監聽,這個功能google的出發點是給那些肢體上有障礙的人使用的,比如手指不健全的用戶,怎么才能滑動屏幕,然后打開一個應用呢?那么輔助功能就是干這些事,他的功能其實就是可以概括兩句話:
第一、尋找到我們想要的View節點
第二、然后模擬點擊,實現特定功能
我們知道Android中的View體系是一個樹形結構,那么每一個View就是一個節點。所以我們可以查找到指定的節點,那么我們該如何查找到我們想要的節點呢?這里我們先看一下輔助功能(AccessibilityService)的用法。
第一步、我們需要集成AccessibilityService類
我們需要自定一個Service然后繼承AccessibilityService,當然還需要在AndroidManifest.xml中聲明這個服務:
<application> <service android:name=".MyAccessibilityService" android:label="@string/accessibility_service_label"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> <mate-data android:name="android.accessibilityservice" anroid:resource="@xml/accessibility"/> </service> <uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" /> </application>
在<application>標簽下添加指定了AccessibilityService的子類MyAccessibilityService,同時加入相應的權限。
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
當然還要一個meta-data的聲明,這個聲明是對這個AccessibilityService的配置。我們看一下配置文件內容:
(注:從Android4.0開始,開發者可以通過在AndroidManifest里添加<meta-data>標簽,在標簽里指出配置文件的位置)
<?xml version="1.0" encoding="utf-8"?> <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged" android:accessibilityFeedbackType="feedbackGeneric" android:accessibilityFlags="flagDefault" android:canRetrieveWindowContent="true" android:description="@string/desc" android:notificationTimeout="100" android:packageNames="com.tencent.mm" />
這里我們看到有很多選項,我們看一下常用的幾個屬性:
1、android:accessibilityEventTypes="typeAllMask"
看屬性名也差不多可以明白,這個是用來設置響應事件的類型,typeAllMask當然就是響應所有類型的事件了。當然還有單擊、長按、滑動等。
2、android:accessibilityFeedbackType="feedbackSpoken"
設置回饋給用戶的方式,有語音播出和振動。可以配置一些TTS引擎,讓它實現發音。
3、android:notificationTimeout="100"
響應時間的設置就不用多說了
4、android:packageNames="com.example.android.apis"
可以指定響應某個應用的事件,這里因為要響應所有應用的事件,所以不填,默認就是響應所有應用的事件。比如我們寫一個微信搶紅包的輔助程序,就可以在這里填寫微信的包名,便可以監聽微信產生的事件了。我們這些配置信息除了在xml中定義,同樣也可以在代碼中定義,我們一般都是在onServiceConnected()方法里進行
@Override protected void onServiceConnected() { AccessibilityServiceInfo info = getServiceInfo(); info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK; info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN; info.notificationTimeout = 100; setServiceInfo(info); info.packageNames = new String[]{"xxx.xxx.xxx", "yyy.yyy.yyy","...."}; setServiceInfo(info); super.onServiceConnected(); }
在子類MyAccessibilityService里實現幾個重要的重載方法:
onServiceConnected() - 可選。系統會在成功連接上你的服務的時候調用這個方法,在這個方法里你可以做一下初始化工作,例如設備的聲音震動管理,也可以調用setServiceInfo()進行配置工作。
onAccessibilityEvent() - 必須。通過這個函數可以接收系統發送來的AccessibilityEvent,接收來的AccessibilityEvent是經過過濾的,過濾是在配置工作時設置的。
onInterrupt() - 必須。這個在系統想要中斷AccessibilityService返給的響應時會調用。在整個生命周期里會被調用多次。
onUnbind() - 可選。在系統將要關閉這個AccessibilityService會被調用。在這個方法中進行一些釋放資源的工作。
2、這里我們一般都會在這里寫上我們需要監聽的應用的包名,但是有時候我們需要監聽多個應用,那么這時候我們該怎么辦呢?
第一種:我們在代碼中注冊多個應用的包名,從而可以監聽多個應用:
@Override protected void onServiceConnected() { AccessibilityServiceInfo info = getServiceInfo(); //這里可以設置多個包名,監聽多個應用 info.packageNames = new String[]{"xxx.xxx.xxx", "yyy.yyy.yyy","...."}; setServiceInfo(info); super.onServiceConnected(); }
第二種:我們在onAccessibilityEvent事件監聽的方法中做包名的過濾(這種方式最常用)
@Override public void onAccessibilityEvent(AccessibilityEvent event) { String pkgName = event.getPackageName().toString(); if("xxx.xxx.xxx".equals(pkgName)){ }else if("yyy.yyy.yyy".equals(pkgName)){ }else if("....".equals(pkgName)){ } }
第三步、在onAccessibilityEvent方法中監聽指定的事件
這里面最重要的部分就是onAccessibilityEvent這個回調函數,當我們注冊了監聽事件的時候,當有事件發生就會通知我們這個函數,但是一定要注意這個函數通知是異步的,當然很多朋友就會問這個是怎么通知到這里來的呢?他是通過AccessibilityDelegate這個代理類,發送出來的,這個類有個方法sendAccessibilityEvent可以發送事件。那這個類又怎么和我們的窗口聯系呢?這里舉個例子。比如我們的View類里面有個setAccessibilityDelegate這個方法,是不是這下一切都明了了呢?然后就是調用我們的find函數去當前節點里面找到我們需要的節點信息。
@Override public void onAccessibilityEvent(AccessibilityEvent event) { int eventType = event.getEventType(); switch (eventType) { case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED: //....... } }
這個事件類型很多的,我們可以查看AccessibilityEvent類的源碼
第四步、查找到我們想要處理的節點View
這里系統提供了兩個方法讓我們來進行查找想要的節點View
第一種是通過節點View的Text內容來查找
findAccessibilityNodeInfosByText("查找內容")
這種方式查找,就是像TextView,Button等View有文本內容的,可以使用這種方式快速的找到。
第二種是通過節點View在xml布局中的id名稱
findAccessibilityNodeInfosByViewId("@id/xxx")
這個一般很難知道,但是我們在查找系統控件的時候還是可以做的,因為系統的控件的id是可以知道的,而且是統一的。
(關於這兩個方法我們在寫網頁爬蟲程序的時候可能知道,在html中通過tag/name/id等信息可以找到一個節點,原理都類似)
第五步、模擬點擊指定事件
我們找到我們想要的View節點,調用方法模擬事件:
performAction(AccessibilityNodeInfo.ACTION_CLICK)
調用這個方法即可,當然這里的參數就是指定事件的名稱,這個和AccessibilityEvent中監聽的那些事件是一一對應的,這里是模擬點擊事件,我們當然可以模擬View的滾動事件,長按事件等。