更新记录

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)

使用前提(重要)

  1. 无障碍服务属于系统级特殊权限,无法用代码直接开启,必须由用户在系统「设置 - 无障碍」中手动授权: 调用 openService() 跳转设置页 → 用户找到本应用并开启 → 再调用 isOpenService() 或依赖 onConnected 事件确认。
  2. 用户授权后,服务由系统拉起并常驻;应用进程被杀后重新打开,服务会自动重连(onConnected 事件补发)。
  3. 用户关闭服务或系统回收后,节点读取类接口会返回 9260002。
  4. 滑动手势接口需要 Android 7.0(API 24) 及以上;关闭服务(disableSelf)同样需要 API 24+。
  5. 请遵守目标 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 获取应用上下文失败

注意事项

  1. 无障碍服务是全局的:服务连接后,本应用自己的界面也会产生事件,调试时建议用 packageNames 过滤目标应用。
  2. getAllNodes 结果量可能很大(上限 2000 个节点),遍历耗时在几十毫秒级别,建议按需使用 getContainTextNodes / getNodesByIds 精确查找。
  3. 部分应用(尤其游戏、部分金融类 App)使用自绘控件或做了无障碍屏蔽,节点树中可能查不到目标内容。
  4. 服务配置了 flagIncludeNotImportantViews 与 flagReportViewIds,但没有启用触摸探索模式(避免影响用户正常操作)。
  5. 云打包 / 自定义基座才能验证原生服务,标准基座不包含本插件的无障碍服务声明。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

android.permission.BIND_ACCESSIBILITY_SERVICE

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件仅在本机读取当前屏幕的界面节点信息,采集到的内容不会上传至任何服务器

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无

暂无用户评论。