前言
谷歌提供了一種用于與app自動交互的手動無障礙服務(wù)罚勾,只要像正常的Android Service一樣寫一個繼承AccessibilityService的類即可創(chuàng)建一個無障礙服務(wù)晨缴。當(dāng)app有操作時系統(tǒng)就會通知你開啟的服務(wù),有時候遇到困難無障礙服務(wù)也是一個不得已的方案笔诵。
具體可以參照谷歌文檔:https://developer.android.com/guide/topics/ui/accessibility/service
創(chuàng)建服務(wù)類
先創(chuàng)建一個app,實現(xiàn)一個繼承AccessibilityService的Service
public class AccessibilitySampleService extends AccessibilityService {
public static String TAG = "SharkChilli";
@Override
protected void onServiceConnected() {
super.onServiceConnected();
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
// 此方法是在主線程中回調(diào)過來的屯掖,所以消息是阻塞執(zhí)行的
}
@Override
public void onInterrupt() {
}
}
onServiceConnected方法中可以來配置服務(wù)的一些設(shè)置瓢谢,比如監(jiān)控哪些app的哪些動作之類的兼呵。
onAccessibilityEvent方法就是app發(fā)送交互后會調(diào)用的方法兔辅,相關(guān)的交互信息都在AccessibilityEvent 中。
谷歌官網(wǎng)的具體解釋如下
- onServiceConnected() -(可選)當(dāng)系統(tǒng)成功連接到無障礙服務(wù)時击喂,會調(diào)用此方法维苔。使用此方法可為服務(wù)執(zhí)行任何一次性設(shè)置步驟,包括連接到用戶反饋系統(tǒng)服務(wù)懂昂,如音頻管理器或設(shè)備振動器介时。如果您要在運行時設(shè)置服務(wù)的配置或做出一次性調(diào)整,從此處調(diào)用 setServiceInfo() 非常方便凌彬。
- onAccessibilityEvent() -(必需)當(dāng)系統(tǒng)檢測到與無障礙服務(wù)指定的事件過濾參數(shù)匹配的 `AccessibilityEvent 時沸柔,會回調(diào)此方法。例如铲敛,當(dāng)用戶點擊某個按鈕或?qū)⒔裹c置于應(yīng)用中的某個界面控件上褐澎,而無障礙服務(wù)正在為其提供反饋時。出現(xiàn)這種情況時伐蒋,系統(tǒng)會調(diào)用此方法工三,并傳遞關(guān)聯(lián)的 AccessibilityEvent,服務(wù)隨后可以對其進行解讀并用其向用戶提供反饋先鱼。此方法可能會在服務(wù)的整個生命周期內(nèi)被調(diào)用多次俭正。
- onInterrupt() -(必需)當(dāng)系統(tǒng)要中斷服務(wù)正在提供的反饋(通常是為了響應(yīng)將焦點移到其他控件等用戶操作)時,會調(diào)用此方法焙畔。此方法可能會在服務(wù)的整個生命周期內(nèi)被調(diào)用多次掸读。
- onUnbind() -(可選)當(dāng)系統(tǒng)將要關(guān)閉無障礙服務(wù)時,會調(diào)用此方法。使用此方法可執(zhí)行任何一次性關(guān)閉流程寺枉,包括取消分配用戶反饋系統(tǒng)服務(wù)抑淫,如音頻管理器或設(shè)備振動器。
配置AndroidManifest.xml
和正常的Service一樣需要到AndroidManifest.xml中配置我們實現(xiàn)的Service姥闪。
<service
android:name="com.shark.service.AccessibilitySampleService"
android:exported="true"
android:label="@string/accessibility_tip"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:process=":BackgroundService">
<!--為了被視為無障礙服務(wù)始苇,您必須在清單的 application 元素中添加一個 service 元素(而非 activity 元素)。
此外筐喳,在 service 元素中催式,您還必須添加一個無障礙服務(wù) intent 過濾器。為了與 Android 4.1 及更高版本兼容避归,
清單還必須保護該服務(wù)荣月,方法是添加 BIND_ACCESSIBILITY_SERVICE 權(quán)限以確保只有系統(tǒng)可以綁定到它 -->
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<!--從 Android 4.0 開始,您可以在清單中添加一個引用配置文件的 <meta-data> 元素梳毙,這樣可讓您為無障礙服務(wù)設(shè)置所有選項-->
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibility_config" />
</service>
這里基本是固定的在meta-data信息中配置的android:resource="@xml/accessibility_config"是指定我們服務(wù)的配置信息哺窄,這個配置工作也可以在上面的onServiceConnected方法通過AccessibilityServiceInfo配置。
我們看看這兩種的配置方式
xml創(chuàng)建好相關(guān)的文件后配置如下
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackGeneric"
android:canRetrieveWindowContent="true"
android:description="@string/accessibility_desc"
android:packageNames="com.shark.nougat"
android:notificationTimeout="100" />
onServiceConnected方法中配置
protected void onServiceConnected() {
super.onServiceConnected();
AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo();
accessibilityServiceInfo.eventTypes = AccessibilityEvent.TYPE_WINDOWS_CHANGED
| AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
| AccessibilityEvent.TYPE_VIEW_CLICKED
| AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
| AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED;
accessibilityServiceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
accessibilityServiceInfo.notificationTimeout = 0;
accessibilityServiceInfo.flags = AccessibilityServiceInfo.DEFAULT;
accessibilityServiceInfo.packageNames = new String[]{"com.shark.nougat"};
setServiceInfo(accessibilityServiceInfo);
}
如需詳細了解可在無障礙服務(wù)配置文件中使用的 XML 屬性账锹,請點擊以下鏈接轉(zhuǎn)到相應(yīng)的參考文檔:
- android:description
- android:packageNames
- android:accessibilityEventTypes
- android:accessibilityFlags
- android:accessibilityFeedbackType
- android:notificationTimeout
- android:canRetrieveWindowContent
- android:settingsActivity
如需詳細了解可在運行時動態(tài)設(shè)置的配置設(shè)置萌业,請參閱 AccessibilityServiceInfo 參考文檔
開啟無障礙服務(wù)
將app安裝后,到設(shè)置中開啟服務(wù)
這個名稱就是我們前面設(shè)置的lable
AccessibilityEvent的操作
開啟后app只要有互動奸柬,系統(tǒng)就會將這次互動的相關(guān)信息封裝到AccessibilityEvent中并調(diào)用onAccessibilityEvent方法生年,這里就介紹一下基本操作
判斷操作的類型
public void onAccessibilityEvent(AccessibilityEvent event) {
// 此方法是在主線程中回調(diào)過來的,所以消息是阻塞執(zhí)行的
switch (event.getEventType()) {
/**
* AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED廓奕,
* 而會調(diào)用AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED抱婉,
* 而AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED只要內(nèi)容改變后都會調(diào)用,
* 所以一般是使用AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED來作為監(jiān)測事件的
*/
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
Log.i(TAG, "TYPE_WINDOW_STATE_CHANGED 界面改變");
break;
case AccessibilityEvent.TYPE_VIEW_CLICKED:
Log.i(TAG, "TYPE_VIEW_CLICKED view被點擊");
break;
}
}
通過AccessibilityEvent.getEventType()可以獲得這次互動的類型桌粉。更多類型可以參考官網(wǎng)文檔
獲得操作的app包名和Activity類名
String pkgName = event.getPackageName().toString();
String className = event.getClassName().toString();
AccessibilityNodeInfo操作
在無障礙服務(wù)中控件節(jié)點會包裝為一個AccessibilityNodeInfo蒸绩,通過這個類我們可以在代碼中與之交互
獲取root節(jié)點類型
private AccessibilityNodeInfo getRootNodeInfo() {
AccessibilityEvent curEvent = mAccessibilityEvent;
AccessibilityNodeInfo nodeInfo = null;
if (Build.VERSION.SDK_INT >= 16) {
// 建議使用getRootInActiveWindow,這樣不依賴當(dāng)前的事件類型
if (mAccessibilityService != null) {
nodeInfo = mAccessibilityService.getRootInActiveWindow();
Log.d(TAG, "getRootNodeInfo: " + nodeInfo);
}
// 下面這個必須依賴當(dāng)前的AccessibilityEvent
// nodeInfo = curEvent.getSource();
} else {
nodeInfo = curEvent.getSource();
}
return nodeInfo;
}
獲取了根節(jié)點就可以向下去查找其他節(jié)點了
通過ID查找節(jié)點
nodeInfo.findAccessibilityNodeInfosByViewId(viewId);
id:"com.shark.nougat:id/sample_text"
通過Text查找節(jié)點
nodeInfo.findAccessibilityNodeInfosByText(text);
點擊節(jié)點
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
修改文本
node.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
點擊全局返回铃肯、Home
performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME);
uiautomatorviewer
我們可以使用sdk中自帶的uiautomatorviewer查看app id等信息
引用
Github對AccessibilitySample封裝
android 輔助功能(無障礙) AccessibilityService 實戰(zhàn)入門詳解
Android輔助功能(Accessibility)簡介
Android輔助功能原理與基本使用詳解-AccessibilityService