TalkBack 源码分析之TalkBackService

「这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战

TalkBack 源码分析之TalkBackService

该篇文章比较小众,无障碍服务对大部分开发者来说是接触不到的,但如果你刚好需要,或者想要了解它,就接着往下看,这篇文章是对TalkBack的源码的初步讲解。

TalkBack 基本操作

为了防止大家打开TalkBack之后无法关闭,请先看下它的基本操作。 前面说过,当TalkBack功能开启之后,手机的操作习惯会改变。 滑动操作: 必须双指滑动 点击:双击 长按: 选中组件后,双击后按住 点按两次以激活某项内容,双指滑动以浏览屏幕。 在开启TalkBack的时候,在主页也会看到操作提示,在TalkBack设置->教程和帮助中会有详细的解释。

1.jpg

TalkBack项目入口

TalkBack的项目有多个module,我们需要找到主入口,我们知道实现无障碍服务需要在xml中去配置无障碍服务的配置信息,我们很轻松的在xml中找到了accessibilityservice,然后在AndroidManifest.xml中去找使用它的地方。

<!-- TalkBack -->
<service
android:icon="@drawable/icon"
android:name="com.google.android.marvin.talkback.TalkBackService"
>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibilityservice" />
</service>
复制代码

所以我们断定talkback.TalkBackServic就是项目的主要入口。

TalkBackService

找了项目的入口,就跟着入口服务去分析。 无障碍服务有几个关键的回调方法。
onCreate - 服务创建成功时,和普通Service的onCreate一样。
onServiceConnected - 服务开启成功,在该方法中适合做一些初始化操作
onAccessibilityEvent - 事件处理方法的回调,主要的操作的需要在该方法执行,所有的操作都会触发事件。
onConfigurationChanged - 配置发生变化时,比如探索模式的打开和关闭
getRootInActiveWindow - 获取屏幕的所有组件

  1. onCreate

服务创建成功。


@Override

public void onCreate() {

super.onCreate();

this.setTheme(R.style.BaseTheme);

instance = this; setServiceState(ServiceStateListener.SERVICE_STATE_INACTIVE);

systemUeh = Thread.getDefaultUncaughtExceptionHandler();

Thread.setDefaultUncaughtExceptionHandler(this);

}

复制代码

设置主题,设置状态,设置一个setDefaultUncaughtExceptionHandler,相当于全局的catch,用于捕获程序未捕获的异常。

  1. onServiceConnected

无障碍服务绑定成功。于OnCreate方法不同的是有些方法必须要在无障碍服务绑定成功之后才可以调用。

@Override
protected void onServiceConnected() {
性能统计...
SP 初始化...
日志初始化...
一些帮助类...
}
复制代码
  1. onAccessibilityEvent()

该回调方法是很重要的,可以监听所有的屏幕响应事件。


@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
Performance perf = Performance.getInstance();
EventId eventId = perf.onEventReceived(event);
accessibilityEventProcessor.onAccessibilityEvent(event, eventId);
perf.onHandlerDone(eventId);
}
复制代码

Performance 是事件统计的方法类。

accessibilityEventProcessor.onAccessibilityEvent

public void onAccessibilityEvent(AccessibilityEvent event, EventId eventId) {
if (testingListener != null) {
testingListener.onAccessibilityEvent(event);
}
if ((dumpEventMask & event.getEventType()) != 0) {
LogUtils.v(TAG, DUMP_EVENT_LOG_FORMAT, event);
}
if (shouldDropRefocusEvent(event)) {
return;
}
if (shouldDropEvent(event)) {
return;
}
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
lastWindowStateChanged = SystemClock.uptimeMillis();
}
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
|| event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
|| event.getEventType() == AccessibilityEvent.TYPE_WINDOWS_CHANGED) {
service.setRootDirty(true);
}

// We need to save the last focused event so that we can filter out related selected events.
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED) {
if (lastFocusedEvent != null) {
lastFocusedEvent.recycle();
}
lastFocusedEvent = AccessibilityEvent.obtain(event);
}
if (AccessibilityEventUtils.eventMatchesAnyType(event, MASK_DELAYED_EVENT_TYPES)) {
handler.postProcessEvent(event, eventId);
} else {
processEvent(event, eventId);
}
if (testingListener != null) {
testingListener.afterAccessibilityEvent(event);
}
}

复制代码

这里设置了一个缓存,如果监听到TYPE_WINDOW_CONTENT_CHANGED,TYPE_WINDOW_STATE_CHANGED,TYPE_WINDOWS_CHANGED 事件,就清楚mRootNode的缓存,下次调用获取mRootNode(getRootInActiveWindow()) 时,重新获取 mRootNode。

lastFocusedEvent 代表只记录最近的一个聚焦事件。

おすすめ

転載: juejin.im/post/7032911123477348359