輔助功能(AccessibilityService)其實是一個Android系統提供給的一種服務,本身是繼承Service類的。這個服務提供了增強的用戶界面,旨在幫助殘障人士或者可能暫時無法與設備充分交互的人們。從開發者的角度看,其實就是提供兩種功能:查找界面元素,實現模擬點擊。實現一個輔助功能服務... ...
輔助功能原理與基本使用詳解
本文主要介紹輔助功能的使用
- 輔助功能基本原理
- 輔助功能基本配置和框架搭建
- 輔助功能實戰解析
一、輔助功能基本原理
輔助功能(AccessibilityService)其實是一個Android系統提供給的一種服務,本身是繼承Service類的。這個服務提供了增強的用戶界面,旨在幫助殘障人士或者可能暫時無法與設備充分交互的人們。
從開發者的角度看,其實就是提供兩種功能:查找界面元素,實現模擬點擊。實現一個輔助功能服務要求繼承AccessibilityService類並實現它的抽象方法。自定義一個服務類AccessibilitySampleService(這個命名可以隨意),繼承系統的AccessibilityService並覆寫onAccessibilityEvent和onInterrupt方法。編寫好服務類之後,在系統配置文件(AndroidManifest.xml)中註冊服務。完成前面兩個步驟就完成了基本發輔助功能服務註冊與配置,具體的功能實現需要在onAccessibilityEvent中完成,根據onAccessibilityEvent回調方法傳遞過來的AccessibilityEvent對象可以對事件進行過濾,結合AccessibilitySampleService本身提供的查找節點與模擬點擊相關的介面即可實現許可權節點的查找與點擊。
二、輔助功能基本配置和框架搭建
創建自定義輔助功能服務類
import android.accessibilityservice.AccessibilityService;
import android.view.accessibility.AccessibilityEvent;
import com.accessibility.utils.AccessibilityLog;
public class AccessibilitySampleService extends AccessibilityService {
@Override
protected void onServiceConnected() {
super.onServiceConnected();
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
// 此方法是在主線程中回調過來的,所以消息是阻塞執行的
// 獲取包名
String pkgName = event.getPackageName().toString();
int eventType = event.getEventType();
// AccessibilityOperator封裝了輔助功能的界面查找與模擬點擊事件等操作
AccessibilityOperator.getInstance().updateEvent(this, event);
AccessibilityLog.printLog("eventType: " + eventType + " pkgName: " + pkgName);
switch (eventType) {
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
break;
}
}
@Override
public void onInterrupt() {
}
}
註冊輔助功能服務
// 註冊輔助功能服務
<service android:name=".AccessibilitySampleService"
android:label="@string/accessibility_tip"
android:exported="true"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:process=":BackgroundService">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
// 通過xml文件完成輔助功能相關配置,也可以在onServiceConnected中動態配置
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibility_config"/>
</service>
上面android:label="@string/accessibility_tip"是配置此輔助功能服務在系統輔助功能頁面裡面顯示的名字。
accessibility_config文件內容如下:
<?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:notificationTimeout="100" />
跳轉到系統輔助功能頁面,開啟輔助功能服務
完成上面配置之後,輔助功能服務就註冊成功了,在系統輔助功能頁面就能找到這個服務,但是預設是關閉的,也就是說,這個服務要開始為我們服務,還需要去系統界面開啟那個開關。下麵是跳轉到輔助功能頁面的代碼,跳轉過去之後,手動點擊開關按鈕。開關打開之後,這個輔助功能服務就開始工作了,系統開始回調onAccessibilityEvent方法。我們可以在onAccessibilityEvent方法中處理查找節點與點擊操作。
public class OpenAccessibilitySettingHelper {
private static final String ACTION = "action";
private static final String ACTION_START_ACCESSIBILITY_SETTING = "action_start_accessibility_setting";
public static void jumpToSettingPage(Context context) {
try {
Intent intent = new Intent(context, AccessibilityOpenHelperActivity.class);
intent.putExtra(ACTION, ACTION_START_ACCESSIBILITY_SETTING);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
} catch (Exception ignore) {}
}
}
下圖是小米手機開啟輔助功能的界面
三、輔助功能實戰解析
實現界面自動點擊操作,動畫有點模糊,將就看吧
界面節點查找與模擬點擊
AccessibilityOperator封裝了輔助功能的界面查找與模擬點擊事件等操作,下麵介紹幾個關鍵的技術點。
界面節點查找操作
AccessibilityNodeInfo提供兩種查找View節點的方法
1. 根據View的ID精確查找,但是要求SDK_INT >= 18才能用
/**
* 根據View的ID搜索符合條件的節點,精確搜索方式;
* 這個只適用於自己寫的界面,因為ID可能重覆
* api要求18及以上
* @param viewId
*/
public List<AccessibilityNodeInfo> findNodesById(String viewId) {
AccessibilityNodeInfo nodeInfo = getRootNodeInfo();
if (nodeInfo != null) {
if (Build.VERSION.SDK_INT >= 18) {
return nodeInfo.findAccessibilityNodeInfosByViewId(viewId);
}
}
return null;
}
2. 根據View的Text文本進行模糊查找
/**
* 根據Text搜索所有符合條件的節點, 模糊搜索方式
*/
public List<AccessibilityNodeInfo> findNodesByText(String text) {
AccessibilityNodeInfo nodeInfo = getRootNodeInfo();
if (nodeInfo != null) {
return nodeInfo.findAccessibilityNodeInfosByText(text);
}
return null;
}
模擬界面操作
1. 普通的View事件模擬(ACTION_CLICK)
private boolean performClick(List<AccessibilityNodeInfo> nodeInfos) {
if (nodeInfos != null && !nodeInfos.isEmpty()) {
AccessibilityNodeInfo node;
for (int i = 0; i < nodeInfos.size(); i++) {
node = nodeInfos.get(i);
// 獲得點擊View的類型
AccessibilityLog.printLog("View類型:" + node.getClassName());
// 進行模擬點擊
if (node.isEnabled()) {
return node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
}
}
return false;
}
2. 全局事件模擬(返回鍵:AccessibilityService.GLOBAL_ACTION_BACK)
public boolean clickBackKey() {
return performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
}
private boolean performGlobalAction(int action) {
return mAccessibilityService.performGlobalAction(action);
}
源碼地址
https://github.com/PopFisher/AccessibilitySample