更新记录
1.0.0(2026-11-11)
初始版本
- 安卓无障碍(AccessibilityService)能力完整封装,原生层为自研 Kotlin 无障碍服务,不依赖任何三方库
- 服务控制:监听无障碍事件、取消监听、打开系统无障碍设置页、关闭无障碍服务、查询开启状态
- 节点读取:获取全部节点、按文字查找节点、按 viewId 查找节点、获取全部可输入节点
- 节点操作:点击按文字命中的节点、点击按 viewId 命中的节点、粘贴文本到输入框
- 手势模拟:自定义滑动、横向滑动、纵向滑动(Android 7.0 / API 24 及以上)
平台兼容性
uni-app(4.72)
| Vue2 |
Vue2插件版本 |
Vue3 |
Vue3插件版本 |
Chrome |
Safari |
app-vue |
app-vue插件版本 |
app-nvue |
app-nvue插件版本 |
Android |
Android插件版本 |
iOS |
鸿蒙 |
| √ |
1.0.0 |
√ |
1.0.0 |
× |
× |
√ |
1.0.0 |
√ |
1.0.0 |
5.0 |
1.0.0 |
× |
× |
| 微信小程序 |
支付宝小程序 |
抖音小程序 |
百度小程序 |
快手小程序 |
京东小程序 |
鸿蒙元服务 |
QQ小程序 |
飞书小程序 |
小红书小程序 |
快应用-华为 |
快应用-联盟 |
| × |
× |
× |
× |
× |
× |
× |
× |
× |
× |
× |
× |
uni-app x(4.72)
| Chrome |
Safari |
Android |
Android插件版本 |
iOS |
鸿蒙 |
微信小程序 |
| × |
× |
5.0 |
1.0.0 |
× |
× |
× |
插件介绍
安卓无障碍(AccessibilityService)操作 UTS 原生插件。一个插件覆盖无障碍事件监听、服务开关、界面节点读取、按文字 / viewId 定位并点击节点、写入文本到输入框、模拟滑动手势,不依赖任何三方库(原生层为自研 Kotlin 无障碍服务)。
支持 uni-app 与 uni-app x。
使用场景
- 跨应用自动化:监听指定 App 的事件,识别界面文字后自动点击(如自动确认、批量操作)
- 无障碍辅助工具:读取屏幕节点树并语音播报 / 提示
- 表单填写:定位目标 App 的输入框并直接写入内容
- 手势模拟:翻页、上下滑动、左右滑动(无障碍服务免 root)
使用前提(重要)
- 无障碍服务属于系统级特殊权限,无法用代码直接开启,必须由用户在系统「设置 - 无障碍」中手动授权:
调用
openService() 跳转设置页 → 用户找到本应用并开启 → 再调用 isOpenService() 或依赖 onConnected 事件确认。
- 用户授权后,服务由系统拉起并常驻;应用进程被杀后重新打开,服务会自动重连(
onConnected 事件补发)。
- 用户关闭服务或系统回收后,节点读取类接口会返回
9260002。
- 滑动手势接口需要 Android 7.0(API 24) 及以上;关闭服务(disableSelf)同样需要 API 24+。
- 请遵守目标 App 的用户协议与相关法律法规,不要用于抢购、刷量、外挂等违规场景。
用法
import {
setListener,
removeListener,
openService,
stopService,
isOpenService,
getAllNodes,
getContainTextNodes,
clickContainTextNodes,
getNodesByIds,
clickNodesByIds,
getAllEditableNodes,
pasteTextToEditor,
slide,
slideHorizontal,
slideVertical
} from '@/uni_modules/zy-accessibility'
1. 监听 / 取消监听
// 只监听微信与 QQ 的事件;不传 packageNames 表示监听全部应用(事件量大,建议过滤)
setListener({
packageNames: ['com.tencent.mm', 'com.tencent.mobileqq'],
onEvent: (event) => {
if (event.type === 'onConnected') {
console.log('无障碍服务已连接,可以开始操作了')
} else if (event.type === 'onDisconnected') {
console.log('无障碍服务已断开')
} else if (event.type === 'onEvent') {
// event.eventType:viewClick / viewTextChanged / windowStateChanged 等
console.log(event.eventType + ' ' + event.packageName + ' ' + event.text)
}
},
success: (res) => console.log('监听已注册: ' + res.errMsg),
fail: (err) => console.log('注册失败 ' + err.errCode + ' ' + err.errMsg)
})
// 取消监听(只清回调与包名过滤,不会关闭系统无障碍服务)
removeListener()
2. 开启 / 关闭 / 查询服务
// 跳转系统无障碍设置页(用户手动开启;Android 8.0+ 优先直达本服务详情页)
openService({
success: (res) => console.log(res.errMsg)
})
// 关闭无障碍服务(内部 disableSelf,需 API 24+)
stopService({
success: () => console.log('已关闭'),
fail: (err) => console.log('关闭失败 ' + err.errCode + ' ' + err.errMsg)
})
// 查询状态
isOpenService({
success: (res) => {
// res.result:系统已授权且服务已连接
// res.enabled:系统设置中已授权
// res.connected:服务已连接(可执行节点操作)
console.log('可用:' + res.result + ' 已授权:' + res.enabled + ' 已连接:' + res.connected)
}
})
3. 读取节点
// 当前窗口全部节点(前序遍历,上限 2000 个)
getAllNodes({
success: (res) => {
res.list.forEach((node) => {
console.log(node.index + ' ' + node.className + ' ' + node.text + ' ' + node.id)
})
},
fail: (err) => console.log(err.errCode + ' ' + err.errMsg)
})
// 包含指定文字的节点(多个关键词,结果去重)
getContainTextNodes({
list: ['红包', '恭喜发财'],
success: (res) => console.log('命中 ' + res.list.length + ' 个节点')
})
// 根据 viewId 集合查找节点(需目标控件上报 viewId,Android 8.0+)
getNodesByIds({
list: ['com.tencent.mm:id/a3u'],
success: (res) => console.log('命中 ' + res.list.length + ' 个节点')
})
// 全部可输入节点(索引供 pasteTextToEditor 使用)
getAllEditableNodes({
success: (res) => console.log('可输入节点:' + res.list.length)
})
4. 操作节点
// 点击包含文字的节点,index 为 getContainTextNodes 结果中的下标
// 节点未标记可点击时会沿父节点链回溯到第一个可点击节点
clickContainTextNodes({
list: ['发送'],
index: 0,
success: () => console.log('点击成功'),
fail: (err) => console.log('点击失败 ' + err.errCode + ' ' + err.errMsg)
})
// 点击指定 viewId 的节点
clickNodesByIds({
list: ['com.tencent.mm:id/a3u'],
index: 0
})
// 粘贴(写入)文本到第 0 个输入框
pasteTextToEditor({
text: '这是一条测试数据',
index: 0,
success: () => console.log('写入成功')
})
5. 手势滑动(API 24+)
// 自定义滑动:屏幕绝对坐标(像素),可从节点的 left / top / right / bottom 计算
slide({
startX: 540, startY: 1600,
endX: 540, endY: 600,
duration: 300
})
// 横向滑动:按屏幕宽高百分比,yPercent 为纵向位置
slideHorizontal({
xStartPercent: 0.8,
yPercent: 0.5,
xEndPercent: 0.2,
duration: 300
})
// 纵向滑动:按屏幕宽高百分比,xPercent 为横向位置(上滑翻页)
slideVertical({
xPercent: 0.5,
yStartPercent: 0.8,
yEndPercent: 0.3,
duration: 300
})
手势接口在手势真正完成(系统回调 onCompleted)后才回调 success,被系统取消或分发失败回调 fail。
数据结构
节点 ZyAccessibilityNode
| 字段 |
类型 |
说明 |
| index |
number |
在结果数组中的下标,可直接作为 clickXxx 的 index |
| className |
string |
控件类名,如 android.widget.Button |
| text |
string |
节点文本 |
| contentDescription |
string |
内容描述(contentDescription) |
| id |
string |
视图资源 id,如 com.xxx:id/btn(Android 8.0+ 可取到) |
| windowId |
number |
所属窗口 id |
| childCount |
number |
子节点数量 |
| isClickable / isScrollable / isEditable / isEnabled / isChecked / isSelected |
boolean |
控件状态 |
| left / top / right / bottom |
number |
屏幕坐标(像素) |
| bounds |
string |
屏幕坐标串,如 "120,80,540,200" |
| parentClassName / parentText / parentContentDescription / parentId / parentClickable |
string / boolean |
父节点摘要 |
事件 ZyAccessibilityEvent
| 字段 |
说明 |
| type |
onConnected 服务已连接 / onDisconnected 服务已断开 / onEvent 无障碍事件 / onError 内部错误 |
| eventType |
事件细分类型:viewClick、viewLongClicked、viewTextChanged、windowStateChanged、windowContentChanged、viewScrolled 等 |
| eventTypeValue |
事件原始数值(AccessibilityEvent.getEventType()) |
| packageName / className |
事件来源包名与类名 |
| text |
事件文本(多条用 \n 连接) |
| contentDescription / beforeText |
内容描述 / 变化前文本 |
| eventTime |
事件时间(毫秒时间戳) |
| itemCount / currentItemIndex |
列表项总数 / 当前项下标 |
| scrollX / scrollY / maxScrollX / maxScrollY |
滚动偏移与最大偏移 |
| windowId |
窗口 id |
| errCode / errMsg |
错误码与描述(type 为 onError / onDisconnected 时) |
错误码
| 错误码 |
说明 |
| 9260001 |
参数错误(如 list 为空) |
| 9260002 |
无障碍服务未开启 |
| 9260003 |
未获取到当前窗口的根节点(目标应用不在前台) |
| 9260004 |
未找到目标节点(或 index 超出范围) |
| 9260005 |
节点不可点击,且父节点链上也没有可点击节点 |
| 9260006 |
当前系统版本不支持该功能(需要 API 24 及以上) |
| 9260007 |
操作失败 |
| 9260008 |
获取应用上下文失败 |
注意事项
- 无障碍服务是全局的:服务连接后,本应用自己的界面也会产生事件,调试时建议用
packageNames 过滤目标应用。
getAllNodes 结果量可能很大(上限 2000 个节点),遍历耗时在几十毫秒级别,建议按需使用 getContainTextNodes / getNodesByIds 精确查找。
- 部分应用(尤其游戏、部分金融类 App)使用自绘控件或做了无障碍屏蔽,节点树中可能查不到目标内容。
- 服务配置了
flagIncludeNotImportantViews 与 flagReportViewIds,但没有启用触摸探索模式(避免影响用户正常操作)。
- 云打包 / 自定义基座才能验证原生服务,标准基座不包含本插件的无障碍服务声明。