一、本文需要解決的問題
之前本人做了一個(gè)項(xiàng)目,需要用到AccessibilityService這個(gè)系統(tǒng)提供的拓展服務(wù)。這個(gè)服務(wù)本意是作為Android系統(tǒng)的一個(gè)輔助功能,去幫助殘疾人更好地使用手機(jī)。但是由于它的一些特性,給很多項(xiàng)目的實(shí)現(xiàn)提供了一個(gè)新的思路,例如之前大名鼎鼎的微信搶紅包插件,本質(zhì)上就是使用了這個(gè)服務(wù)。我研究AccessibilityService的目的是解決以下幾個(gè)我在使用過程中所思考的問題:
- AccessibilityService這個(gè)Service跟一般的Service有什么區(qū)別?
- AccessibilityService是如何做到監(jiān)控并捕捉用戶行為的?
- AccessibilityService是如何做到查找控件,執(zhí)行點(diǎn)擊等操作的?
二、初步分析
本文基于Android 7.1的源碼對(duì)AccessibilityService進(jìn)行分析。
為了更好地理解和分析代碼,我寫了一個(gè)demo,如果想學(xué)習(xí)具體的使用方法,可以參考Google官方文檔AccessibilityService。本文不做AccessibilityService的具體使用教程。
創(chuàng)建AccessibilityService
public class MyAccessibilityService extends AccessibilityService {
private static final String TAG = "MyAccessibilityService";
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate");
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
int eventType = event.getEventType();
switch (eventType) {
case AccessibilityEvent.TYPE_VIEW_CLICKED:
// 捕獲到點(diǎn)擊事件
Log.i(TAG, "capture click event!");
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
if (nodeInfo != null) {
// 查找text為Test!的控件
List<AccessibilityNodeInfo> button = nodeInfo.findAccessibilityNodeInfosByText("Test!");
nodeInfo.recycle();
for (AccessibilityNodeInfo item : button) {
Log.i(TAG, "long-click button!");
// 執(zhí)行長(zhǎng)按操作
item.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);
}
}
break;
default:
break;
}
}
@Override
public void onInterrupt() {
Log.i(TAG, "onInterrupt");
}
}
AccessibilityService配置
res/xml/accessibility_service_config.xml
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackSpoken"
android:accessibilityFlags="flagRetrieveInteractiveWindows|flagRequestFilterKeyEvents"
android:canRequestFilterKeyEvents="true"
android:canRetrieveWindowContent="true"
android:description="@string/app_name"
android:notificationTimeout="100"
android:packageNames="com.xu.accessibilitydemo" />
在manifest中進(jìn)行注冊(cè)
<service
android:name=".MyAccessibilityService"
android:enabled="true"
android:exported="true"
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/accessibility_service_config"/>
</service>
創(chuàng)建一個(gè)text為Test!的button控件,設(shè)置監(jiān)聽方法
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.button);
button.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
Log.i(TAG, "onLongClick");
return false;
}
});
}
}
開啟AccessibilityService
AccessibilityService服務(wù)具體開啟位置在設(shè)置--無障礙中。
運(yùn)行應(yīng)用,點(diǎn)擊text為Test!的按鈕
會(huì)出現(xiàn)以下的日志:

具體解釋:
點(diǎn)擊按鈕即產(chǎn)生TYPE_VIEW_CLICKED事件 --> 被AcceesibilityService捕獲 --> 捕獲后執(zhí)行長(zhǎng)按按鈕操作 --> 執(zhí)行長(zhǎng)按回調(diào)方法。
為什么AcceesibilityService能捕獲并執(zhí)行其他操作呢,接下來我將對(duì)源碼進(jìn)行解析~
三、源碼解析
AccessibilityService內(nèi)部邏輯
AccessibilityService.java
public abstract class AccessibilityService extends Service {
// 省略代碼
public abstract void onAccessibilityEvent(AccessibilityEvent event);
public abstract void onInterrupt();
@Override
public final IBinder onBind(Intent intent) {
return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() {
@Override
public void onServiceConnected() {
AccessibilityService.this.dispatchServiceConnected();
}
@Override
public void onInterrupt() {
AccessibilityService.this.onInterrupt();
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
AccessibilityService.this.onAccessibilityEvent(event);
}
@Override
public void init(int connectionId, IBinder windowToken) {
mConnectionId = connectionId;
mWindowToken = windowToken;
// The client may have already obtained the window manager, so
// update the default token on whatever manager we gave them.
final WindowManagerImpl wm = (WindowManagerImpl) getSystemService(WINDOW_SERVICE);
wm.setDefaultToken(windowToken);
}
@Override
public boolean onGesture(int gestureId) {
return AccessibilityService.this.onGesture(gestureId);
}
@Override
public boolean onKeyEvent(KeyEvent event) {
return AccessibilityService.this.onKeyEvent(event);
}
@Override
public void onMagnificationChanged(@NonNull Region region,
float scale, float centerX, float centerY) {
AccessibilityService.this.onMagnificationChanged(region, scale, centerX, centerY);
}
@Override
public void onSoftKeyboardShowModeChanged(int showMode) {
AccessibilityService.this.onSoftKeyboardShowModeChanged(showMode);
}
@Override
public void onPerformGestureResult(int sequence, boolean completedSuccessfully) {
AccessibilityService.this.onPerformGestureResult(sequence, completedSuccessfully);
}
});
}
}
分析:
- AccessibilityService是一個(gè)抽象類,繼承于Service,提供兩個(gè)抽象方法 onAccessibilityEvent() 和 onInterrupt();
- 雖然是抽象類,但是實(shí)現(xiàn)了最重要的 onBind() 方法,在其中創(chuàng)建了一個(gè)IAccessibilityServiceClientWrapper對(duì)象,實(shí)現(xiàn)Callbacks接口中的抽象方法。
IAccessibilityServiceClientWrapper
// 以分析onAccessibilityEvent為例,省略部分代碼
public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
implements HandlerCaller.Callback {
private final HandlerCaller mCaller;
private final Callbacks mCallback;
private int mConnectionId;
public IAccessibilityServiceClientWrapper(Context context, Looper looper,
Callbacks callback) {
mCallback = callback;
mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/);
}
public void init(IAccessibilityServiceConnection connection, int connectionId,
IBinder windowToken) {
Message message = mCaller.obtainMessageIOO(DO_INIT, connectionId,
connection, windowToken);
mCaller.sendMessage(message);
}
// 省略部分代碼
public void onAccessibilityEvent(AccessibilityEvent event) {
Message message = mCaller.obtainMessageO(DO_ON_ACCESSIBILITY_EVENT, event);
mCaller.sendMessage(message);
}
@Override
public void executeMessage(Message message) {
switch (message.what) {
case DO_ON_ACCESSIBILITY_EVENT: {
AccessibilityEvent event = (AccessibilityEvent) message.obj;
if (event != null) {
AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event);
mCallback.onAccessibilityEvent(event);
// Make sure the event is recycled.
try {
event.recycle();
} catch (IllegalStateException ise) {
/* ignore - best effort */
}
}
} return;
// ...
}
}
}
分析:
- IAccessibilityServiceClientWrapper繼承于IAccessibilityServiceClient類,它是一個(gè)aidl接口,同時(shí)注意到它是繼承于IAccessibilityServiceClient.Stub類,可以大概猜測(cè)到,AccessibilityService為一個(gè)遠(yuǎn)程Service,使用到跨進(jìn)程通信技術(shù),后面我還會(huì)繼續(xù)分析這個(gè);
- IAccessibilityServiceClientWrapper的類構(gòu)造方法中,有兩個(gè)比較重要的參數(shù),一個(gè)是looper,另一個(gè)是Callbacks callback。Looper不用說,而Callbacks接口定義了很多方法,代碼如下:
public interface Callbacks {
public void onAccessibilityEvent(AccessibilityEvent event);
public void onInterrupt();
public void onServiceConnected();
public void init(int connectionId, IBinder windowToken);
public boolean onGesture(int gestureId);
public boolean onKeyEvent(KeyEvent event);
public void onMagnificationChanged(@NonNull Region region,
float scale, float centerX, float centerY);
public void onSoftKeyboardShowModeChanged(int showMode);
public void onPerformGestureResult(int sequence, boolean completedSuccessfully);
}
- IAccessibilityServiceClientWrapper同時(shí)也實(shí)現(xiàn)了HandlerCaller.Callback接口,HandlerCaller類通過命名也可以知道,它內(nèi)部含有一個(gè)Handler實(shí)例,所以可以把它當(dāng)做一個(gè)Handler,而處理信息的方法就是HandlerCaller.Callback#executeMessage(msg)方法
- 代碼有點(diǎn)繞,故簡(jiǎn)單總結(jié)一下流程:
AccessibilityEvent產(chǎn)生
? -> Binder驅(qū)動(dòng)
?? -> IAccessibilityServiceClientWrapper#onAccessibilityEvent(AccessibilityEvent)
??? -> HandlerCaller#sendMessage(message); // message中包括AccessibilityEvent
???? -> IAccessibilityServiceClientWrapper#executeMessage();
????? -> Callbacks#onAccessibilityEvent(event);
?????? -> AccessibilityService.this.onAccessibilityEvent(event);
到這里解決了我們的第一個(gè)問題:AccessibilityService同樣繼承于Service類,它屬于遠(yuǎn)程服務(wù)類,是Android系統(tǒng)提供的一種服務(wù),可以綁定此服務(wù),用于捕捉界面的一些特定事件。
AccessibilityService外部邏輯
前面分析了接收到AccessibilityEvent之后的代碼邏輯,那么,這些AccessibilityEvent是怎樣產(chǎn)生的呢,而且,在回調(diào)執(zhí)行之后是怎么做到點(diǎn)擊等操作的(如demo所示)?我們接下來繼續(xù)分析相關(guān)的源碼~
我們從demo作為例子開始入手,首先我們知道,一個(gè)點(diǎn)擊事件的產(chǎn)生,實(shí)際代碼邏輯是在View#onTouchEvent() -> View#performClick()中:
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
// !??!
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
這里找到一個(gè)重點(diǎn)方法sendAccessibilityEvent(),繼續(xù)跟進(jìn)去,最后走到View#sendAccessibilityEventUncheckedInternal()方法:
public void sendAccessibilityEventUncheckedInternal(AccessibilityEvent event) {
if (!isShown()) {
return;
}
onInitializeAccessibilityEvent(event);
// Only a subset of accessibility events populates text content.
if ((event.getEventType() & POPULATING_ACCESSIBILITY_EVENT_TYPES) != 0) {
dispatchPopulateAccessibilityEvent(event);
}
// In the beginning we called #isShown(), so we know that getParent() is not null.
getParent().requestSendAccessibilityEvent(this, event);
}
這里的getParent()會(huì)返回一個(gè)實(shí)現(xiàn)ViewParent接口的對(duì)象。
我們可以簡(jiǎn)單理解為,它會(huì)讓View的父類執(zhí)行requestSendAccessibilityEvent()方法,而View的父類一般為ViewGroup,我們查看ViewGroup#requestSendAccessibilityEvent()方法
@Override
public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
ViewParent parent = mParent;
if (parent == null) {
return false;
}
final boolean propagate = onRequestSendAccessibilityEvent(child, event);
if (!propagate) {
return false;
}
return parent.requestSendAccessibilityEvent(this, event);
}
這里涉及到一個(gè)變量mParent,我們要找到這個(gè)mParent變量是在哪里被賦值的。
首先我們?cè)赩iew類中找到一個(gè)相關(guān)的方法View#assignParent():
void assignParent(ViewParent parent) {
if (mParent == null) {
mParent = parent;
} else if (parent == null) {
mParent = null;
} else {
throw new RuntimeException("view " + this + " being added, but" + " it already has a parent");
}
}
但是View類中并沒有調(diào)用此方法,猜測(cè)是View的父類進(jìn)行調(diào)用。
通過對(duì)源碼進(jìn)行搜索,發(fā)現(xiàn)最后是在ViewRootImpl#setView()中進(jìn)行調(diào)用,賦值的是this即ViewRootImpl本身。
直接跳到ViewRootImpl#requestSendAccessibilityEvent()方法:
@Override
public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
if (mView == null || mStopped || mPausedForTransition) {
return false;
}
// Intercept accessibility focus events fired by virtual nodes to keep
// track of accessibility focus position in such nodes.
final int eventType = event.getEventType();
switch (eventType) {
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED:
{
final long sourceNodeId = event.getSourceNodeId();
final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(sourceNodeId);
View source = mView.findViewByAccessibilityId(accessibilityViewId);
if (source != null) {
AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider();
if (provider != null) {
final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(sourceNodeId);
final AccessibilityNodeInfo node;
if (virtualNodeId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
node = provider.createAccessibilityNodeInfo(AccessibilityNodeProvider.HOST_VIEW_ID);
} else {
node = provider.createAccessibilityNodeInfo(virtualNodeId);
}
setAccessibilityFocus(source, node);
}
}
}
break;
// 省略部分代碼
}
// !?。? mAccessibilityManager.sendAccessibilityEvent(event);
return true;
}
重點(diǎn):AccessibilityManager#sendAccessibilityEvent(event)
public void sendAccessibilityEvent(AccessibilityEvent event) {
final IAccessibilityManager service;
final int userId;
synchronized(mLock) {
service = getServiceLocked();
if (service == null) {
return;
}
if (!mIsEnabled) {
Looper myLooper = Looper.myLooper();
if (myLooper == Looper.getMainLooper()) {
throw new IllegalStateException("Accessibility off. Did you forget to check that?");
} else {
// If we're not running on the thread with the main looper, it's possible for
// the state of accessibility to change between checking isEnabled and
// calling this method. So just log the error rather than throwing the
// exception.
Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled");
return;
}
}
userId = mUserId;
}
boolean doRecycle = false;
try {
event.setEventTime(SystemClock.uptimeMillis());
// it is possible that this manager is in the same process as the service but
// client using it is called through Binder from another process. Example: MMS
// app adds a SMS notification and the NotificationManagerService calls this method
long identityToken = Binder.clearCallingIdentity();
// ?。?!
doRecycle = service.sendAccessibilityEvent(event, userId);
Binder.restoreCallingIdentity(identityToken);
if (DEBUG) {
Log.i(LOG_TAG, event + " sent");
}
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error during sending " + event + " ", re);
} finally {
if (doRecycle) {
event.recycle();
}
}
}
private IAccessibilityManager getServiceLocked() {
if (mService == null) {
tryConnectToServiceLocked(null);
}
return mService;
}
private void tryConnectToServiceLocked(IAccessibilityManager service) {
if (service == null) {
IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
if (iBinder == null) {
return;
}
service = IAccessibilityManager.Stub.asInterface(iBinder);
}
try {
final int stateFlags = service.addClient(mClient, mUserId);
setStateLocked(stateFlags);
mService = service;
} catch (RemoteException re) {
Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
}
}
這里有使用到Android Binder機(jī)制,重點(diǎn)為IAccessibilityManager#sendAccessibilityEvent()方法,這里調(diào)用的是代理方法,實(shí)際代碼邏輯在AccessibilityManagerService#sendAccessibilityEvent():
@Override
public boolean sendAccessibilityEvent(AccessibilityEvent event, int userId) {
synchronized(mLock) {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
// performs the current profile parent resolution..
final int resolvedUserId = mSecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(userId);
// This method does nothing for a background user.
if (resolvedUserId != mCurrentUserId) {
return true; // yes, recycle the event
}
if (mSecurityPolicy.canDispatchAccessibilityEventLocked(event)) {
mSecurityPolicy.updateActiveAndAccessibilityFocusedWindowLocked(event.getWindowId(), event.getSourceNodeId(), event.getEventType(), event.getAction());
mSecurityPolicy.updateEventSourceLocked(event);
// ?。?!
notifyAccessibilityServicesDelayedLocked(event, false);
notifyAccessibilityServicesDelayedLocked(event, true);
}
if (mHasInputFilter && mInputFilter != null) {
mMainHandler.obtainMessage(MainHandler.MSG_SEND_ACCESSIBILITY_EVENT_TO_INPUT_FILTER, AccessibilityEvent.obtain(event)).sendToTarget();
}
event.recycle();
}
return (OWN_PROCESS_ID != Binder.getCallingPid());
}
private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event, boolean isDefault) {
try {
UserState state = getCurrentUserStateLocked();
for (int i = 0, count = state.mBoundServices.size(); i < count; i++) {
Service service = state.mBoundServices.get(i);
if (service.mIsDefault == isDefault) {
if (canDispatchEventToServiceLocked(service, event)) {
service.notifyAccessibilityEvent(event);
}
}
}
} catch (IndexOutOfBoundsException oobe) {
// An out of bounds exception can happen if services are going away
// as the for loop is running. If that happens, just bail because
// there are no more services to notify.
}
}
- 在方法中,最后會(huì)調(diào)用notifyAccessibilityServicesDelayedLocked()方法,然后將event進(jìn)行回收;
- 在notifyAccessibilityServicesDelayedLocked()方法中,會(huì)獲得所有Bound即綁定的Service,執(zhí)行notifyAccessibilityEvent()方法,通過跟蹤代碼邏輯,最后會(huì)調(diào)用綁定Service的onAccessibilityEvent()方法。綁定的Service是指我們自己實(shí)現(xiàn)的繼承于AccessibilityService的Service類,當(dāng)你在設(shè)置-無障礙中開啟服務(wù)之后即將服務(wù)綁定到AccessibilityManagerService中。
這樣我們解決了第二個(gè)問題:
AccessibilityService是如何做到監(jiān)控捕捉用戶行為的:(以點(diǎn)擊事件為例)
AccessibilityEvent產(chǎn)生:
View#performClick()
? -> View#sendAccessibilityEventUncheckedInternal()
?? -> ViewGroup#requestSendAccessibilityEvent()
??? -> ViewRootImpl#requestSendAccessibilityEvent()
???? -> AccessibilityManager#sendAccessibilityEvent(event)
????? -> AccessibilityManagerService#sendAccessibilityEvent()
?????? -> AccessibilityManagerService#notifyAccessibilityServicesDelayedLocked()
??????? -> Service#notifyAccessibilityEvent(event)
AccessibilityEvent處理:
AccessibilityEvent
? -> Binder驅(qū)動(dòng)
?? -> IAccessibilityServiceClientWrapper#onAccessibilityEvent(AccessibilityEvent)
??? -> HandlerCaller#sendMessage(message); // message中包括AccessibilityEvent
???? -> IAccessibilityServiceClientWrapper#executeMessage();
????? -> Callbacks#onAccessibilityEvent(event);
?????? -> AccessibilityService.this.onAccessibilityEvent(event);
AccessibilityService交互之查找控件
在demo中,我們?cè)贛yAccessibilityService中調(diào)用了getRootInActiveWindow()方法獲取被監(jiān)控的View的所有結(jié)點(diǎn),這些結(jié)點(diǎn)都封裝成一個(gè)AccessibilityNodeInfo對(duì)象中。同時(shí)也調(diào)用AccessibilityNodeInfo#findAccessibilityNodeInfosByText()方法查找相應(yīng)的控件。
這些方法的本質(zhì)是調(diào)用了AccessibilityInteractionClient類的對(duì)應(yīng)方法。
以AccessibilityInteractionClient#findAccessibilityNodeInfosByText()為例:
public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(int connectionId, int accessibilityWindowId, long accessibilityNodeId, String text) {
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
final long identityToken = Binder.clearCallingIdentity();
final boolean success = connection.findAccessibilityNodeInfosByText(accessibilityWindowId, accessibilityNodeId, text, interactionId, this, Thread.currentThread().getId());
Binder.restoreCallingIdentity(identityToken);
if (success) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(interactionId);
if (infos != null) {
finalizeAndCacheAccessibilityNodeInfos(infos, connectionId);
return infos;
}
}
} else {
if (DEBUG) {
Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
}
}
} catch (RemoteException re) {
Log.w(LOG_TAG, "Error while calling remote" + " findAccessibilityNodeInfosByViewText", re);
}
return Collections.emptyList();
}
代碼邏輯比較簡(jiǎn)單,就是直接調(diào)用IAccessibilityServiceConnection#findAccessibilityNodeInfosByText()方法。
IAccessibilityServiceConnection是一個(gè)aidl接口,從注釋看,它是AccessibilitySerivce和AccessibilityManagerService之間溝通的橋梁。
猜想代碼真正的實(shí)現(xiàn)在AccessibilityManagerService中。
AccessibilityManagerService.Service#findAccessibilityNodeInfosByText():
@Override
public boolean findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId, String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
Region partialInteractiveRegion = Region.obtain();
synchronized(mLock) {
if (!isCalledForCurrentUserLocked()) {
return false;
}
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted = mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
if (!permissionGranted) {
return false;
} else {
connection = getConnectionLocked(resolvedWindowId);
if (connection == null) {
return false;
}
}
if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(resolvedWindowId, partialInteractiveRegion)) {
partialInteractiveRegion.recycle();
partialInteractiveRegion = null;
}
}
final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
try {
connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text, partialInteractiveRegion, interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid, spec);
return true;
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfosByText()");
}
} finally {
Binder.restoreCallingIdentity(identityToken);
// Recycle if passed to another process.
if (partialInteractiveRegion != null && Binder.isProxy(connection)) {
partialInteractiveRegion.recycle();
}
}
return false;
}
- 此方法在AccessibilityManagerService的內(nèi)部類Service中實(shí)現(xiàn),這個(gè)Service繼承于IAccessibilityServiceConnection.Stub,驗(yàn)證了我上面的猜想是正確的;
- 代碼重點(diǎn)是調(diào)用connection.findAccessibilityNodeInfosByText(),這里的connection實(shí)例與上面不同,它隸屬于IAccessibilityInteractionConnection類。這個(gè)類同樣是一個(gè)aidl接口,從注釋上看,它又是AccessibilityManagerService與指定窗口的ViewRoot之間溝通的橋梁。
再次猜想,真正的代碼邏輯在ViewRootImpl中。
查看ViewRootImpl.AccessibilityInteractionConnection#findAccessibilityNodeInfosByText():
@Override
public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController().findAccessibilityNodeInfosByTextClientThread(accessibilityNodeId, text, interactiveRegion, interactionId, callback, flags, interrogatingPid, interrogatingTid, spec);
} else {
// We cannot make the call and notify the caller so it does not wait.
try {
callback.setFindAccessibilityNodeInfosResult(null, interactionId);
} catch (RemoteException re) {
/* best effort - ignore */
}
}
}
- 同樣的,此方法在ViewRootImpl的內(nèi)部類AccessibilityInteractionConnection中實(shí)現(xiàn),這個(gè)內(nèi)部類繼承于IAccessibilityServiceConnection.Stub,驗(yàn)證了我的猜想;
- 查找控件等操作,ViewRootImpl并不是直接處理,而是交給AccessibilityInteractionController類去查找,查找到的結(jié)果會(huì)保存到一個(gè)callback中,這個(gè)callback為IAccessibilityInteractionConnectionCallback類型,它也是一個(gè)aidl接口,而AccessibilityInteractionClient類繼承了IAccessibilityInteractionConnectionCallback.Stub,即最后查詢后的結(jié)果會(huì)回調(diào)到AccessibilityInteractionClient類中,如上面AccessibilityInteractionClient#findAccessibilityNodeInfosByText()方法,最后會(huì)調(diào)用getFindAccessibilityNodeInfosResultAndClear()方法獲取結(jié)果。具體如何尋找指定控件則不再分析代碼。
AccessibilityService交互之執(zhí)行控件操作
類似的,與上面的流程基本相同,只是回調(diào)的時(shí)候,返回的是執(zhí)行操作的返回值(True or False)。
到這里,我們解決了最后一個(gè)問題:
AccessibilityService是如何做到查找控件,執(zhí)行點(diǎn)擊等操作的?
總結(jié):
尋找指定控件/執(zhí)行操作
? -> 交給AccessibilityInteractionClient類處理
? ? -> Binder
? ? ? -> AccessibilityManagerService類進(jìn)行查找/執(zhí)行操作
? ? ? ? -> Binder
? ? ? ? ? -> 指定窗口的ViewRoot(ViewRootImpl)進(jìn)行查找/執(zhí)行操作
? ? ? ? <- Binder
? ? <- 結(jié)果回調(diào)到AccessibilityInteractionClient類
四、有用代碼記錄
- HandlerCaller類:結(jié)合Handler類和自定義的接口類(Caller.java),利用Handler的消息循環(huán)機(jī)制來分發(fā)消息,將最終的處理函數(shù)交給Caller#executeMessage():
// HandlerCaller.java
public class HandlerCaller {
final Looper mMainLooper;
final Handler mH;
final Callback mCallback;
class MyHandler extends Handler {
MyHandler(Looper looper, boolean async) {
super(looper, null, async);
}
@Override
public void handleMessage(Message msg) {
mCallback.executeMessage(msg);
}
}
public interface Callback {
public void executeMessage(Message msg);
}
public HandlerCaller(Context context, Looper looper, Callback callback,
boolean asyncHandler) {
mMainLooper = looper != null ? looper : context.getMainLooper();
mH = new MyHandler(mMainLooper, asyncHandler);
mCallback = callback;
}
public Handler getHandler() {
return mH;
}
public void executeOrSendMessage(Message msg) {
// If we are calling this from the main thread, then we can call
// right through. Otherwise, we need to send the message to the
// main thread.
if (Looper.myLooper() == mMainLooper) {
mCallback.executeMessage(msg);
msg.recycle();
return;
}
mH.sendMessage(msg);
}
public void sendMessageDelayed(Message msg, long delayMillis) {
mH.sendMessageDelayed(msg, delayMillis);
}
public boolean hasMessages(int what) {
return mH.hasMessages(what);
}
public void removeMessages(int what) {
mH.removeMessages(what);
}
public void removeMessages(int what, Object obj) {
mH.removeMessages(what, obj);
}
public void sendMessage(Message msg) {
mH.sendMessage(msg);
}
public SomeArgs sendMessageAndWait(Message msg) {
if (Looper.myLooper() == mH.getLooper()) {
throw new IllegalStateException("Can't wait on same thread as looper");
}
SomeArgs args = (SomeArgs)msg.obj;
args.mWaitState = SomeArgs.WAIT_WAITING;
mH.sendMessage(msg);
synchronized (args) {
while (args.mWaitState == SomeArgs.WAIT_WAITING) {
try {
args.wait();
} catch (InterruptedException e) {
return null;
}
}
}
args.mWaitState = SomeArgs.WAIT_NONE;
return args;
}
public Message obtainMessage(int what) {
return mH.obtainMessage(what);
}
// 省略部分代碼
}
- HandlerCaller#sendMessageAndWait():
public SomeArgs sendMessageAndWait(Message msg) {
if (Looper.myLooper() == mH.getLooper()) {
throw new IllegalStateException("Can't wait on same thread as looper");
}
SomeArgs args = (SomeArgs) msg.obj;
args.mWaitState = SomeArgs.WAIT_WAITING;
mH.sendMessage(msg);
synchronized(args) {
while (args.mWaitState == SomeArgs.WAIT_WAITING) {
try {
args.wait();
} catch (InterruptedException e) {
return null;
}
}
}
args.mWaitState = SomeArgs.WAIT_NONE;
return args;
}
這篇文章會(huì)同步到我的個(gè)人日志,如有問題,請(qǐng)大家踴躍提出,謝謝大家!