在了解了无障碍服务基础使用之后,我们来探究一下 AccessibilityService 的事件接收方法回调的时机和它深层次的实现逻辑。
AccessibilityService 监听事件的调用逻辑
AccessibilityService
有很多用来接收外部调用事件变化的方法,这些方法封装在内部接口 Callbacks
中:
public interface Callbacks {
void onAccessibilityEvent(AccessibilityEvent event);
void onInterrupt();
void onServiceConnected();
void init(int connectionId, IBinder windowToken);
boolean onGesture(AccessibilityGestureEvent gestureInfo);
boolean onKeyEvent(KeyEvent event);
void onMagnificationChanged(int displayId, @NonNull Region region, float scale, float centerX, float centerY);
void onSoftKeyboardShowModeChanged(int showMode);
void onPerformGestureResult(int sequence, boolean completedSuccessfully);
void onFingerprintCapturingGesturesChanged(boolean active);
void onFingerprintGesture(int gesture);
void onAccessibilityButtonClicked(int displayId);
void onAccessibilityButtonAvailabilityChanged(boolean available);
void onSystemActionsChanged();
}
以最常用的 onAccessibilityEvent
为例,介绍一下调用流程。
onAccessibilityEvent
在 AccessibilityService
中,AccessibilityService
在 onBind
生命周期中,返回了一个IAccessibilityServiceClientWrapper
对象,它是一个 Binder ,所以外部实际上通过 Binder 机制跨进程调用到无障碍服务的。
外部通过 Binder 调用到 Service 具体的实现方法的调用栈是:
- frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java#IAccessibilityServiceClientWrapper#onAccessibilityEvent
- frameworks/base/core/java/com/android/internal/os/HandlerCaller.java#sendMessage
- frameworks/base/core/java/com/android/internal/os/HandlerCaller.java#Callback#executeMessage
- frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java#IAccessibilityServiceClientWrapper#executeMessage
- frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java#Callbacks#onAccessibilityEvent
- frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java#onAccessibilityEvent
首先是外部调用到 IAccessibilityServiceClientWrapper
的 onAccessibilityEvent 方法:
// IAccessibilityServiceClientWrapper
public void onAccessibilityEvent(AccessibilityEvent event, boolean serviceWantsEvent) {
Message message = mCaller.obtainMessageBO(
DO_ON_ACCESSIBILITY_EVENT, serviceWantsEvent, event);
mCaller.sendMessage(message);
}
在这个方法中通过 HandlerCaller
切换到主线程,然后发送了一个消息。
这里的 HandlerCaller
的源码是:
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;
}
...
}
从它的源码中可以看出,这是一个向主线程发消息的 Handler 。在主线程中执行它的内部类 Callback
的 executeMessage
方法。
而IAccessibilityServiceClientWrapper
实现了 AccessibilityService.Callback
接口,所以调用到了IAccessibilityServiceClientWrapper.executeMessage
方法中。IAccessibilityServiceClientWrapper
对象的创建是在 onBind
生命周期中。它接收一个 AccessibilityService.Callback
对象作为 IAccessibilityServiceClientWrapper
的构造参数:
@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);
}
...
});
IAccessibilityServiceClientWrapper
中的 executeMessage
中,根据不同的 Handler 消息调用了 AccessibilityService.Callback
的对应方法:
public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub implements HandlerCaller.Callback {
private final HandlerCaller mCaller;
public IAccessibilityServiceClientWrapper(Context context, Looper looper,
Callbacks callback) {
// ...
mCaller = new HandlerCaller(context, looper, this, true);
}
@Override
public void executeMessage(Message message) {
switch (message.what) {
case DO_ON_ACCESSIBILITY_EVENT: {
// ...
mCallback.onAccessibilityEvent(event);
return;
}
case DO_ON_INTERRUPT: {
// ...
mCallback.onInterrupt();
return;
}
default :
Log.w(LOG_TAG, "Unknown message type " + message.what);
}
}
}
而刚才传入的 AccessibilityService.Callback
方法的实现中,调用了AccessibilityService
的 onAccessibilityEvent
方法:
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
AccessibilityService.this.onAccessibilityEvent(event);
}
这样整个调用链就清晰了:
-
外部通过 Binder 机制调用到 AccessibilityService
的内部 Binder 代理实现IAccessibilityServiceClientWrapper
对象 -
IAccessibilityServiceClientWrapper
对象内部通过 Handler 机制切换到主线程执行AccessibilityService.Callback
中对应的方法。 -
AccessibilityService.Callback
中的方法调用到了AccessibilityService
对应的生命周期方法。
接下来关注一下一些重要的事件接收方法。
onAccessibilityEvent
Handler 中的 DO_ON_ACCESSIBILITY_EVENT
事件会调用到 onAccessibilityEvent
。在 executeMessage(Message message)
方法中的逻辑是:
case DO_ON_ACCESSIBILITY_EVENT: {
AccessibilityEvent event = (AccessibilityEvent) message.obj;
boolean serviceWantsEvent = message.arg1 != 0;
if (event != null) {
// Send the event to AccessibilityCache via AccessibilityInteractionClient
AccessibilityInteractionClient.getInstance(mContext).onAccessibilityEvent(
event);
if (serviceWantsEvent
&& (mConnectionId != AccessibilityInteractionClient.NO_ID)) {
// Send the event to AccessibilityService
mCallback.onAccessibilityEvent(event);
}
// Make sure the event is recycled.
try {
event.recycle();
} catch (IllegalStateException ise) {
/* ignore - best effort */
}
}
return;
}
-
取出 message.obj
转换为AccessibilityEvent
,并根据message.arg1
检查 Service 是否想要处理这个事件。 -
检查 AccessibilityEvent
对象是否为 null,为空直接 return -
将 AccessibilityEvent
对象通过AccessibilityInteractionClient
加入到 AccessibilityCache 缓存中,然后根据 service 是否要处理事件和AccessibilityInteractionClient
连接状态,决定是否要将事件发送给AccessibilityService
-
最后回收事件对象。
这里的AccessibilityInteractionClient
连接状态检查时通过 mConnectionId
属性来判断的,在IAccessibilityServiceClientWrapper
的 init 时被赋值,init 也是通过 Handler 传递来的消息切换到主线程进行的:
case DO_INIT: {
mConnectionId = message.arg1;
SomeArgs args = (SomeArgs) message.obj;
IAccessibilityServiceConnection connection =
(IAccessibilityServiceConnection) args.arg1;
IBinder windowToken = (IBinder) args.arg2;
args.recycle();
if (connection != null) {
AccessibilityInteractionClient.getInstance(mContext).addConnection(
mConnectionId, connection);
mCallback.init(mConnectionId, windowToken);
mCallback.onServiceConnected();
} else {
AccessibilityInteractionClient.getInstance(mContext).removeConnection(
mConnectionId);
mConnectionId = AccessibilityInteractionClient.NO_ID;
AccessibilityInteractionClient.getInstance(mContext).clearCache();
mCallback.init(AccessibilityInteractionClient.NO_ID, null);
}
return;
}
onIntercept
与onAccessibilityEvent
事件一样都是通过 Handler 机制进行处理的:
case DO_ON_INTERRUPT: {
if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
mCallback.onInterrupt();
}
return;
}
只检查了与AccessibilityInteractionClient
的连接状态。
AccessibilityService 事件的外部来源
经过上面的分析,我们知道外部通过 binder 机制触发了 AccessibilityService
的事件监听方法,那么它们来自哪里呢?接下来从 AccessibilityServiceInfo
开始,分析系统事件是如何传递到无障碍服务的。
AccessibilityServiceInfo
AccessibilityServiceInfo
用来描述 AccessibilityService 。系统根据这个类中的信息,将 AccessibilityEvents
通知给一个 AccessibilityService。
AccessibilityServiceInfo
中定义了一些属性,用来控制无障碍服务的一些权限和能力。我们在 AndroidManifest.xml
中为无障碍服务指定的meta-data
标签中,指定的配置文件中的配置,和AccessibilityServiceInfo
中的属性一一对应。
AccessibilityServiceInfo
的引用:
查看 AccessibilityServiceInfo
的引用栈,发现有很多地方都用到了这个类,关于无障碍的重点看 AccessibilityManagerService
和 AccessibilityManager
这一套逻辑。从名称上看,无障碍功能提供了类似 AMS 一样的系统服务,并通过一个 Manager 类来进行调用。
AccessibilityManager
AccessibilityManager 是一个系统服务管理器,用来分发 AccessibilityEvent 事件。当用户界面中发生一些值得注意的事件时,例如焦点变化和 Activity 启动等,会生成这些事件。
AccessibilityManager 内部有一个看起来与发送消息有关的方法:
public void sendAccessibilityEvent(AccessibilityEvent event) {
final IAccessibilityManager service;
final int userId;
final AccessibilityEvent dispatchedEvent;
synchronized (mLock) {
service = getServiceLocked();
if (service == null) return;
event.setEventTime(SystemClock.uptimeMillis());
if (event.getAction() == 0) {
event.setAction(mPerformingAction);
}
if (mAccessibilityPolicy != null) {
dispatchedEvent = mAccessibilityPolicy.onAccessibilityEvent(event, mIsEnabled, mRelevantEventTypes);
if (dispatchedEvent == null) return;
} else {
dispatchedEvent = event;
}
if (!isEnabled()) {
Looper myLooper = Looper.myLooper();
if (myLooper == Looper.getMainLooper()) {
throw new IllegalStateException("Accessibility off. Did you forget to check that?");
} else {
// 当不是在主线程(mainLooper)运行时,调用检查无障碍开启状态可能会异常。因此直接抛出异常
Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled");
return;
}
}
userId = mUserId;
}
try {
final long identityToken = Binder.clearCallingIdentity();
try {
service.sendAccessibilityEvent(dispatchedEvent, userId);
} finally {
Binder.restoreCallingIdentity(identityToken);
}
if (DEBUG) {
Log.i(LOG_TAG, dispatchedEvent + " sent");
}
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error during sending " + dispatchedEvent + " ", re);
} finally {
if (event != dispatchedEvent) {
event.recycle();
}
dispatchedEvent.recycle();
}
}
这个方法是用来发送一个 AccessibilityEvent
事件的,简化里面的逻辑:
- 加锁
- getServiceLocked() 获取 service 对象,service 获取不到直接 return
- event 设置一个时间,然后设置 action
- 检查 AccessibilityPolicy 对象是否为 null
- 不为空,dispatchedEvent 根据 AccessibilityPolicy 的 onAccessibilityEvent(event) 赋值,赋值后仍为空直接 return
- 为空, dispatchedEvent = event
- 检查系统是否开启无障碍功能
- 解锁
- try
- try
- service.sendAccessibilityEvent(dispatchedEvent, userId); 通过 AccessibilityManagerService 的 sendAccessibilityEvent 发送事件
- finally
- Binder.restoreCallingIdentity(identityToken);
- finally
- 回收 event 和 dispatchedEvent 对象
这里 getServiceLocked()
内部调用了 tryConnectToServiceLocked
方法:
private void tryConnectToServiceLocked(IAccessibilityManager service) {
if (service == null) {
IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
if (iBinder == null) {
return;
}
service = IAccessibilityManager.Stub.asInterface(iBinder);
}
// ...
}
而 AccessibilityManagerService
实现了 IAccessibilityManager.Stub
,所以这里的 service 是 AccessibilityManagerService
。这里调用了 service.sendAccessibilityEvent(dispatchedEvent, userId);
通过 Binder 机制,调用到的是 AccessibilityManagerService
里的 sendAccessibilityEvent
方法。
AccessibilityManager.sendAccessibilityEvent
的调用位置有很多,其中比较显眼的是在 ViewRootImpl 中的,因为 ViewRootImpl 是 View 添加到 Window 的重要实现类。sendAccessibilityEvent
在 ViewRootImpl 的内部类中存在调用:
@Override
public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
// ...
final int eventType = event.getEventType();
final View source = getSourceForAccessibilityEvent(event);
switch (eventType) {
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
if (source != null) {
AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider();
if (provider != null) {
final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(event.getSourceNodeId());
final AccessibilityNodeInfo node;
node = provider.createAccessibilityNodeInfo(virtualNodeId);
setAccessibilityFocus(source, node);
}
}
} break;
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: {
if (source != null && source.getAccessibilityNodeProvider() != null) {
setAccessibilityFocus(null, null);
}
} break;
case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
handleWindowContentChangedEvent(event);
} break;
}
mAccessibilityManager.sendAccessibilityEvent(event);
return true;
}
这是一个 override 方法,它的定义在接口 ViewParent
中。这个方法的调用栈很多:
可以跟到 View 中存在的同名方法 :
public void sendAccessibilityEvent(int eventType) {
if (mAccessibilityDelegate != null) {
mAccessibilityDelegate.sendAccessibilityEvent(this, eventType);
} else {
sendAccessibilityEventInternal(eventType);
}
}
它在 View 中的调用:
可以看出,常见的 View 的事件,包括:点击、长按、焦点变化等,都会调用 sendAccessibilityEvent
方法。
在 ViewRootImpl
中, AccessibilityManager
也存在很多处调用逻辑:
可以看出在 Android 在 View 体系中,提供了很多对无障碍能力的支持。所有的 View 的事件都会被系统的无障碍服务捕获到。
回到调用逻辑,AccessibilityManager
内部调用到的是 AccessibilityManagerService
里的 sendAccessibilityEvent
方法。下面介绍 AccessibilityManagerService
里面的流程。
AccessibilityManagerService
sendAccessibilityEvent
伪代码逻辑:
synchronized {
1. 解析配置文件中的属性,并对其进行配置
2. 设置配置的包名范围
}
if (dispatchEvent) {
3. 确保接收此事件的 client 能够获取 window 当前的状态,因为 Window Manager 可能会出于性能原因延迟计算,通过配置 shouldComputeWindows = true/false
if (shouldComputeWindows) {
4. 获取 WindowManagerInternal wm
5. wm.computeWindowsForAccessibility(displayId);
}
synchoronized {
notifyAccessibilityServicesDelayedLocked(event, false)
notifyAccessibilityServicesDelayedLocked(event, true)
mUiAutomationManager.sendAccessibilityEventLocked(event);
}
}
...
最后的关键三行代码中,调用了两个方法:
notifyAccessibilityServicesDelayedLocked :
private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event, boolean isDefault) {
try {
AccessibilityUserState state = getCurrentUserStateLocked();
for (int i = 0, count = state.mBoundServices.size(); i < count; i++) {
AccessibilityServiceConnection service = state.mBoundServices.get(i);
if (service.mIsDefault == isDefault) {
service.notifyAccessibilityEvent(event);
}
}
} catch (IndexOutOfBoundsException oobe) {}
}
AccessibilityManagerService
从这个方法中,调用 AccessibilityServiceConnection
的同名方法notifyAccessibilityEvent
。
这个意思是,先通知 service.mIsDefault = false
的无障碍服务连接发送事件,然后再通知 等于 true 的无障碍服务连接发送事件。
**mUiAutomationManager.sendAccessibilityEventLocked(event)**:
mUiAutomationManager
的类型是 UiAutomationManager
,它的 sendAccessibilityEventLocked
方法实现是:
void sendAccessibilityEventLocked(AccessibilityEvent event) {
if (mUiAutomationService != null) {
mUiAutomationService.notifyAccessibilityEvent(event);
}
}
mUiAutomationService
的类型是 UiAutomationService
,它是 UiAutomationManager
的内部类,继承自 AbstractAccessibilityServiceConnection
,内部操作都是切换到主线程进行的,notifyAccessibilityEvent
方法的实现在父类中,后续会和上面的 AccessibilityServiceConnection
一起说明。
AccessibilityServiceConnection
此类用来表示一个无障碍服务。它存储着服务管理所需的所有每个服务数据,提供用于启动/停止服务的 API,并负责在服务管理的数据结构中添加/删除服务。该类还公开了配置接口,该接口在绑定后立即传递给它所代表的服务。它还用作服务的连接。
AccessibilityServiceConnection
与 UiAutomationService
一样,继承自 AbstractAccessibilityServiceConnection
。
它们的 notifyAccessibilityEvent
方法,在 AbstractAccessibilityServiceConnection
中:
public void notifyAccessibilityEvent(AccessibilityEvent event) {
synchronized (mLock) {
...
// copy 一个副本,因为在调度期间,如果接收的服务没有访问窗口内容的权限,则可能会修改并删除事件。
AccessibilityEvent newEvent = AccessibilityEvent.obtain(event);
Message message;
if ((mNotificationTimeout > 0)
&& (eventType != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)) {
// 最多允许一个待处理事件
final AccessibilityEvent oldEvent = mPendingEvents.get(eventType);
mPendingEvents.put(eventType, newEvent);
if (oldEvent != null) {
mEventDispatchHandler.removeMessages(eventType);
oldEvent.recycle();
}
message = mEventDispatchHandler.obtainMessage(eventType);
} else {
// 发送所有消息,绕过 mPendingEvents
message = mEventDispatchHandler.obtainMessage(eventType, newEvent);
}
message.arg1 = serviceWantsEvent ? 1 : 0;
mEventDispatchHandler.sendMessageDelayed(message, mNotificationTimeout);
}
}
这个方法中,通过一个 Handler 来处理和发送消息。
mEventDispatchHandler = new Handler(mainHandler.getLooper()) {
@Override
public void handleMessage(Message message) {
final int eventType = message.what;
AccessibilityEvent event = (AccessibilityEvent) message.obj;
boolean serviceWantsEvent = message.arg1 != 0;
notifyAccessibilityEventInternal(eventType, event, serviceWantsEvent);
}
};
内部调用 notifyAccessibilityEventInternal
:
private void notifyAccessibilityEventInternal(int eventType, AccessibilityEvent event, boolean serviceWantsEvent) {
IAccessibilityServiceClient listener;
synchronized (mLock) {
listener = mServiceInterface;
// 如果在消息分发无障碍事件时 service die 或 关闭,listener 可能为空
if (listener == null) return;
// 我们有两种通知事件的方式,节流和非节流。 如果我们不进行节流,那么消息会随事件一起出现,我们会毫不费力地处理这些事件。
if (event == null) {
// 我们正在限制事件,所以只要它为空,我们就会在 mPendingEvents 中发送这种类型的事件。 由于竞争条件,它只能为空:
// 1) 一个 binder 线程调用 notifyAccessibilityServiceDelayedLocked,它发布一条用于调度事件的消息并将该事件存储在 mPendingEvents 中。
// 2) 消息由服务线程上的处理程序从队列中拉出,此方法即将获取锁。
// 3) 另一个 binder 线程在 notifyAccessibilityEvent 中获取锁
// 4) notifyAccessibilityEvent 回收该方法即将处理的事件,替换为新的,并发布第二条消息
// 5) 此方法抓取新事件,对其进行处理,然后将其从 mPendingEvents 中删除
// 6) (4) 中发送的第二条消息到达,但事件已在 (5) 中删除。
event = mPendingEvents.get(eventType);
if (event == null) {
return;
}
mPendingEvents.remove(eventType);
}
if (mSecurityPolicy.canRetrieveWindowContentLocked(this)) {
event.setConnectionId(mId);
} else {
event.setSource((View) null);
}
event.setSealed(true);
}
try {
listener.onAccessibilityEvent(event, serviceWantsEvent);
} catch (RemoteException re) {} finally {
event.recycle();
}
}
备注中说明了消息处理和分发逻辑,但我们这里只需要关注最后的:
listener.onAccessibilityEvent(event, serviceWantsEvent);
这里的 listener 是一个 IAccessibilityServiceClient
,是个 AIDL 文件。这个 AIDL 在我们的 AccessibilityService 中有实现类!
调用到这个 IAccessibilityServiceClient
, 就能调用到 AccessibilityService
中的代码了。
至此无障碍服务的调用,从系统的 View 和其他事件位置,经过AccessibilityManager.notifyAccessibilityEvent
用 Binder 机制调用AccessibilityManagerService.notifyAccessibilityEvent
;然后AccessibilityManagerService
内部通过调用 AccessibilityServiceConnection.notifyAccessibilityEvent
来调用我们可以实现的 AccessibilityService
中接收事件。
原文始发于微信公众号(八千里路山与海):Android AccessibilityService 事件分发原理
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/60181.html