Android辅助功能原理与基本使用详解-AccessibilityService
辅助功能原理与基本使用详解
本文主要介绍辅助功能的使用
- 辅助功能基本原理
- 辅助功能基本配置和框架搭建
- 辅助功能实战解析
一、辅助功能基本原理
辅助功能(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
Android辅助功能原理与基本使用详解-AccessibilityService的更多相关文章
- 深入了解View实现原理以及自定义View详解
下面几篇文章对View的原理讲的非常详细. Android LayoutInflater原理分析,带你一步步深入了解View(一) Android视图绘制流程完全解析,带你一步步深入了解View(二) ...
- Android EventBus 3.0 实例使用详解
EventBus的使用和原理在网上有很多的博客了,其中泓洋大哥和启舰写的非常非常棒,我也是跟着他们的博客学会的EventBus,因为是第一次接触并使用EventBus,所以我写的更多是如何使用,源码解 ...
- Android 多线程之IntentService 完全详解
关联文章: Android 多线程之HandlerThread 完全详解 Android 多线程之IntentService 完全详解 android多线程-AsyncTask之工作原理深入解析(上) ...
- Android 多线程之HandlerThread 完全详解
关联文章: Android 多线程之HandlerThread 完全详解 Android 多线程之IntentService 完全详解 android多线程-AsyncTask之工作原理深入解析(上) ...
- [Spark内核] 第40课:CacheManager彻底解密:CacheManager运行原理流程图和源码详解
本课主题 CacheManager 运行原理图 CacheManager 源码解析 CacheManager 运行原理图 [下图是CacheManager的运行原理图] 首先 RDD 是通过 iter ...
- Android开发:文本控件详解——TextView(一)基本属性
一.简单实例: 新建的Android项目初始自带的Hello World!其实就是一个TextView. 在activity_main.xml中可以新建TextView,从左侧组件里拖拽到右侧预览界面 ...
- [置顶]
xamarin android toolbar(踩坑完全入门详解)
网上关于toolbar的教程有很多,很多新手,在使用toolbar的时候踩坑实在太多了,不好好总结一下,实在浪费.如果你想学习toolbar,你肯定会去去搜索androd toolbar,既然你能看到 ...
- Nginx 反向代理工作原理简介与配置详解
Nginx反向代理工作原理简介与配置详解 by:授客 QQ:1033553122 测试环境 CentOS 6.5-x86_64 nginx-1.10.0 下载地址:http://nginx. ...
- Android 应用程序之间内容分享详解(二)
转载请注明出处:http://blog.csdn.net/xiaanming/article/details/9428613 Android 应用程序之间内容分享详解(一) 之前给大家分享了你开发的应 ...
随机推荐
- super 与 this 同时使用问题
大家都知道this 和 super 调用构造函数时都必须放在第一句,今天同学问我的一个问题有点意思. 那么:我怎么在子类中 显式的用 super 初始化父类同时用 this 初始化子类? ------ ...
- String类的方法
String str = "hello"; /* * 1.String当中跟char[]有关系的方法 */ char[] array = str.toCharArr ...
- 微信小程序框架探究和解析
何为框架 你对微信小程序的技术框架了解多少? 对wepy 框架进行一系列的深入了解 微信小程序框架解析和探究 小程序组件化框架WePY 在性能调优上做出的探究 开发者培训班上海专场PPT分享:小程序框 ...
- Vue2源码分析-逻辑梳理
很久之前就看完vue1,但是太懒就一直没写博客,这次打算抽下懒筋先把自己看过了记录下来,否则等全部看完,估计又没下文了 看源码总需要抱着一个目的,否则就很难坚持下去,我并没做过vue的项目,我几乎很少 ...
- 为什么是Spring Boot
原文:https://dzone.com/articles/why-springboot 作者:Siva Prasad Reddy Katamreddy 译者:Oopsguy 本文介绍将各种Sprin ...
- JS实现添加至购物车功能
效果图展示: 当将书拖拽至购物车一览时: 首先将页面的基本结构写出来: <!DOCTYPE html> <html lang="en"> <head& ...
- C语言_第一讲_C语言入门
一.C语言的简介 1.C语言是一个标准,而执行标准的时候产生的自动化程序则是编译器2.了解:1983年美国国家标准化歇会(ANSI)制定了C语言标准.C语言的特点:3.代码的可移植性(理想状态是代码可 ...
- 原理Lambda表达式
http://www.kingreatwill.com/ (1)源起 .net的设计者发现在使用匿名方法时, 仍旧有一些多余的字母或单词的编码工作 比如delegate关键字 于是进一步简化了匿名方法 ...
- 邻接表(C++)
adj_list_network_edge.h // 邻接表网边数据类模板 template <class WeightType> class AdjListNetworkEdge { p ...
- yhTriangle_LinkQueue(队列实现杨辉三角)
#include"LinkQueue.h" void yhTriangle(int n) { LinkQueue<int> A; int s,t; A.Inqueue( ...