
前言
最近開發的一款設備使用到了Accessibility 功能,Android 提供了Accessibility功能和服務幫助這些用戶更加簡單地操作設備,
需要實作AccessibilityService,AccessibilityService是一個系統服務,它運行在后臺,并且能夠收到由系統發出的一些事件,比如通知狀態、view 的一些相關事件,指紋,touch 等,
界面中產生的任何變化都會由系統通知給 AccessibilityService,大家熟知的搶紅包軟體,Talkback 都是使用AccessibilityService 實作的,自動化測驗等等,都是基于 Accessibility,
后面幾篇文章,將慢慢揭開Accessibility的神秘的面紗,請大家多多關注,
AccessibilityService 是 Service 嗎
簡單例子這里面就不講了,大家可以網上搜索下,
AndroidManifest.xml 配置如下
<service
android:name="XXX.Service"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessible_service_config" />//對AccessibilityService 的組態檔
</service>
</application>
AccessibilityService 寫法就是 Service ,android:permission ,intent-filter 都是必要配置,
貌似還是 Service 那一套,但又不僅限于此,后面的內容可以發現它是不一般的 Servcie,
AccessibilityService 如何啟動的
雖作為 Servcie,App 本身并沒有啟動和停止它,完全由系統調度,這是第一個不一樣的地方,
在 AOSP 中類似的由系統調度的 App Service 有很多,比如 JobService、AutoFillService、NotificationAssistantService 等
下面將將具體解答這兩個問題:
- 誰啟動和停止 AccessibilityService ?
- 設備重啟了,AccessbilityService 也會啟動嗎,不會被kill嗎?
1. 誰啟動了 AccessibilityService
安裝AccessibilityService 的應用,會出現在設定-無障礙-應用串列中,選擇打開,應用中的AccessibilityService 就啟動,
代碼上,設定應用 會將AccessibilityService 的 ComponentName 資訊 存入 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
AccessibilityManagerService 系統服務將監聽該 Map 的變化,啟動或者關閉相應 ComponentName 的 AccessibilityService,
流程如下:
- 監聽
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES - 更新
AccessibilityUserState - 遍歷
mEnableServices,如果沒有bind 則呼叫bindService啟動它

2、重啟會AccessbilityService 會啟動嗎,不會被kill?
AccessibilityManagerService 是 SystemService 行程,開機自啟動,所以 AccessbilityService 的開機啟動就簡單了,原始碼是監聽 unlock 廣播,
bindService 則使用 Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE FLAG 來啟動前臺級別的 service,所以不會被Kill,
frameworks/baseservices/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
public void bindLocked() {
AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState == null) return;
final long identity = Binder.clearCallingIdentity();
try {
int flags = Context.BIND_AUTO_CREATE
| Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE //前臺service,所以不會被Kill
| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
| Context.BIND_INCLUDE_CAPABILITIES;
if (userState.getBindInstantServiceAllowedLocked()) {
flags |= Context.BIND_ALLOW_INSTANT;
}
if (mService == null && mContext.bindServiceAsUser(
mIntent, this, flags, new UserHandle(userState.mUserId))) {
userState.getBindingServicesLocked().add(mComponentName);
}
} finally {
Binder.restoreCallingIdentity(identity);
}
mActivityTaskManagerService.setAllowAppSwitches(mComponentName.flattenToString(),
mAccessibilityServiceInfo.getResolveInfo().serviceInfo.applicationInfo.uid,
userState.mUserId);
}
注意:開發中遇到過一種情況 AccessibilityService 不會啟動,設定 App 中會顯示無法運行,點按可查看相關資訊的提示,這題由于 AccessibilityService 所在的 App 發生了 crash,而 crash 資訊會記錄在AccessibilityUserState中,不再自動 bind,
以上流程AccessibilityManagerService 與 應用中AccessibilityService通信,
AccessibilityService 和 AccessibilityManagerService 的關系
分析完以上流程,可以看到
- AccessibilityManagerService 充當了
client - AccessibilityService 則是
service
所以呼叫程序的本質上是AccessibilityManagerService -> AccessibilityService,
可能有朋友會有疑惑:AccessibilityService 雖然本質上是 Service ,但分明又提供了 onServiceConnected 的方法,那它到除錯于 client 還是 service?
從以上時序圖可以看出,AccessibilityManagerService 系結 AccessibilityService 成功后,會立即呼叫 IAccessibilityServiceClient#init(),
init() 的目的很簡單:將 AccessibilityManagerService 側的 IAccessibilityServiceConnection 介面回呼回來,可以方便AccessibilityService 反過來呼叫 AccessibilityManagerService,回呼過來的介面正常的話會呼叫上述提供的 onServiceConnected(),
簡言之,這個 onServiceConnected() 指的是 AccessibilityManagerService 的 IAccessibilityServiceConnection 服務準備好了,
public abstract class AccessibilityService extends Service {
...
public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
implements HandlerCaller.Callback {
...
public void init(IAccessibilityServiceConnection connection, int connectionId,
IBinder windowToken) {
// Binder 執行緒切換到 Main 執行緒
// 傳遞 id 和 AccessibilityManagerService 回傳的 connection 介面
Message message = mCaller.obtainMessageIOO(DO_INIT, connectionId,
connection, windowToken);
mCaller.sendMessage(message);
}
...
@Override
public void executeMessage(Message message) {
switch (message.what) {
...
case DO_INIT: {
mConnectionId = message.arg1;
SomeArgs args = (SomeArgs) message.obj;
IAccessibilityServiceConnection connection =
(IAccessibilityServiceConnection) args.arg1;
IBinder windowToken = (IBinder) args.arg2;
args.recycle();
// 如果系統回傳的 connection 沒有問題
// 回呼 AccessibilityService 自己的初始化
// 并回呼提供的 onServiceConnected()
if (connection != null) {
AccessibilityInteractionClient.getInstance().addConnection(mConnectionId,
connection);
mCallback.init(mConnectionId, windowToken);
mCallback.onServiceConnected();
} else {
AccessibilityInteractionClient.getInstance().removeConnection(
mConnectionId);
mConnectionId = AccessibilityInteractionClient.NO_ID;
AccessibilityInteractionClient.getInstance().clearCache();
mCallback.init(AccessibilityInteractionClient.NO_ID, null);
}
return;
...
}
}
}
}
}
所以說:AccessibilityService 這種特殊的 Service,既是供 AccessibilityManagerService 傳遞無障礙事件的 service,同時又是會反向呼叫 AccessibilityManagerService 的 client,
那 AccessibilityService 什么時候需要反向呼叫 AccessibilityManagerService 呢?
其實這種的場景很多,這構成了 AccessibilityService 功能的重要部分,包括:
- 動態更新 Accessibility 的配置(serServiceInfo())
- 發出具體的手勢(dispatchGesture())
- 發出截圖的請求(takeScreenshot())
- 發出螢屏縮放的請求(setMagnificationScaleAndCenter())
- 等等
下面提及的 Accessibility 配置的動態更新正是這個場景之一!
Accessibility 配置的加載
AccessibilityService 支持很多配置,但是很多配置在實際開發中都是用不到的,配置的方式有靜態和動態兩種,
-
靜態配置(更為推薦)
就像文章開頭在meta-data里配置資訊 -
動態配置
運行中呼叫 serServiceInfo() 根據需要動態更新配置

從上圖可以看出,
-
靜態配置:AccessibilityManagerService 通過
PackageManager獲取xml的配置資訊,就轉化為AccessibilityServiceInfo,AccessibilityServiceInfo 保存在System Server行程 -
動態配置:app 使用
IAccessbilityServiceConnection介面作為橋梁,去呼叫 System Server ,設定 AccessibilityServiceInfo
具體配置的內容,這里就不多講了,整個Accessibility 的內容是很龐大的,所以相關配置也比較多,后面文章會結合功能跟大家分享,這樣也更容易懂,這里就跟大家賣個關子吧,
AccessibilityService 的除錯
上面說過開啟的 AccessibilityService 會存在 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES 中,通過相應的 adb 命令可以實時列印這些資訊,高效除錯,
adb shell settings get secure enabled_accessibility_services
可以讀當然也可以寫,免去了到 Settings App 里手動打開:
adb shell settings put secure enabled_accessibility_services <com.example.xxx/.xxxService>
注意:引數是所有打開的 Service 串列,新寫入的 Service 名稱要在已有的 Service 后面追加,用:隔開,不然會清除其他 開啟的Servcie,
adb shell settings put secure enabled_accessibility_services <xxx/.xxxService:xxx/.xxxService:xxx/.xxxService>
除了查看開啟,同時還有個命令可以觀察 AccessibilityService 的 bound 、unbound 和 crash 等詳細資訊,
adb shell dumpsys accessibility
總結
本次主要分享了AccessibilityService 是如何啟動, AccessibilityService 如何與SystemServer 中 AccessibilityManagerService 如何相互呼叫,
大概關系如圖所示:

總結如下:
- AccessibityManagerService 創建和管理
AccessibilityServiceConnection - AccessibilityServiceConnection 與 App 中的
AccessibilityService一 一對應 - AccessibilityService 將
IAccessibilityServiceClient介面暴露給 AccessibityManagerService 調度 - AccessibityManagerService 將
IAccessibilityServiceConnection介面傳遞給 AccessibilityService 回呼
再次梳理下 AccessibilityService 的特點:
- 由
AccessibityManagerServicebind 和 unbind - 設備重啟會
自行啟動 - 擁有
前臺 Service的 Flag,優先級高不會被 kill 掉 - 接受 AccessibityManagerService 的調度,同時會反向呼叫,既是
service又是client
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/301561.html
標籤:其他
上一篇:關于華為手機無線除錯
