简介:AutoJs源码-autojs开启无障碍是一份面向AutoJs自动化脚本学习者与实战开发者的可直接运行项目模板,核心作用是帮助用户在Android设备上快速开启无障碍服务权限,解决脚本执行前常见的手动授权与权限状态检测问题。整包采用7z压缩格式,文件总数仅1个,主要文件类型为js脚本,压缩包大小581B,属于轻量级单文件模板;安装好AutoJs后直接运行该脚本即可触发开启无障碍的完整流程,并且兼容低版本AutoJs运行环境,降低了新手的使用门槛。已有872人浏览学习,适合刚接触AutoJs、希望从无障碍授权切入点理解自动化机制的新手,也可作为已有工程中授权模块的轻量参考实现。通过阅读这份js源码,用户能够了解开启无障碍服务时涉及的服务状态判断与系统设置页跳转逻辑,掌握可直接复用的轻量代码结构,并可将对应代码片段迁移至自己的自动化项目中,省去从零编写授权逻辑的时间。资源仅供学习与参考,请勿用于商业用途,使用前请确认AutoJs基础操作。
1. 无障碍权限是AutoJs源码的入口:能跑起来和跑得稳是两回事
AutoJs源码项目的核心价值不在那几十个示例脚本,而在最底层那套无障碍服务的对接逻辑。没有它,text()、click()、swipe() 这些 API 等于空操作;有了它,又会出现权限明明开着但脚本提示未启用、节点树空白、点击位置偏移这类连锁问题。很多第一次接触 AutoJs 的开发者,一个晚上全耗在“服务开了为什么还不行”的黑匣子上,最后发现不是代码逻辑错,而是系统把后台进程给冻结了。这篇笔记会把无障碍权限从开启路径、节点操作、排错方案到压力验证整个链路拆一遍,适合刚接手 AutoJs 源码、想用它做自动化辅助工具的开发者,也适合团队里负责脚本稳定性的同学。权限问题先立住,后面写业务逻辑才不用反复推倒重来。
2. 开启无障碍权限的三种路径:手动、ADB与代码引导
AutoJs 的自动化能力建立在无障碍服务之上。这个服务属于系统级辅助功能,用户授权一次,系统以 ComponentName 的形式写入全局设置,应用本身并没有权限绕过用户直接激活。所以“开启无障碍”这个动作,严格来说不是在应用内部完成的,而是通过设置页、ADB 命令或系统意图把用户引导到对应入口。理解这一点之后,你就能明白为什么不同手机上“开启后马上又失效”的情况区别这么大:不同的系统会在后台对无障碍服务做不同的限制策略,有些是杀进程,有些是关闭后台启动,有些是休眠后冻结。
2.1 为什么AutoJs必须依赖无障碍服务:事件注入与节点读取的逻辑
Android 的普通应用无法直接读取另一个应用窗口里的控件内容,也无法直接向系统注入点击手势。无障碍服务是系统提供给辅助工具的一条合法通道,它可以在界面变化时收到回调,拿到当前窗口的 AccessibilityNodeInfo 节点树,也能通过 dispatchGesture 等方式向系统注入点击、滑动、长按等手势。AutoJs 的选择器 API,本质上就是在遍历这棵节点树,查找匹配 text、desc、id 等属性的节点;click 和 swipe 底层走的也是无障碍服务里的手势注入接口。
这里有一个特别容易忽略的点:无障碍服务从用户点击开关到真正绑定 UI 事件回调,中间有几百毫秒到几秒的延迟,甚至可能因为系统桌面还没刷新完导致服务绑定失败。所以脚本开头不能直接假设权限一定就绪,必须先等待。
// 阻塞等待无障碍服务可用 auto.waitFor(); log("无障碍服务已就绪");auto.waitFor()是 AutoJs 初始化入口里最常见的启动方式,内部会反复检查服务状态,默认最长等待 60 秒,超时后抛出异常。如果脚本跑在无人值守的设备上,建议把这段逻辑放在主入口最前面,并且配合 toast 或日志输出,方便远程排查。
2.2 手动开启流程与界面参数:稳定但不适合批量设备
手动开启是最稳妥的方式,路径通常是:系统设置 → 无障碍 → 已安装的辅助功能 → 找到目标应用 → 开启服务 → 弹窗确认。每个系统在这个流程里还会藏几个额外开关,比如“快捷方式”“使用情况访问”“悬浮窗权限”,它们不是无障碍服务本身,但会影响脚本运行。手动开启的好处是系统会做好服务绑定和状态同步,坏处是没法批量部署。如果只管一台测试机,我一般直接手动开,省去环境变量调试的麻烦。
2.3 ADB方式写入权限:批量设备上车的正解
要给一批测试机批量配置,手动点就太慢了。最直接的思路是把无障碍服务开关直接写入系统设置数据库。命令如下:
adb shell settings put secure enabled_accessibility_services com.example.autotask/com.example.autotask.TaskAccessibilityService adb shell settings put secure accessibility_enabled 1第一条命令里的值必须是“包名/服务类名”的完整形式,多个服务用冒号分隔,例如com.example.autotask/com.example.autotask.TaskAccessibilityService:com.another.app/.AccessibilityService。第二条命令是总开关。执行完之后用下面的命令确认写入结果:
adb shell settings get secure enabled_accessibility_services adb shell settings get secure accessibility_enabled这种方式的缺点是部分系统不会即时响应数据库变化,需要重启目标应用或重启设备才能完成绑定。我在批量部署时会在 ADB 命令之后加一条adb shell am force-stop com.example.autotask,再通过adb shell am start拉起应用,让无障碍服务被重新注册。如果设备系统限制较强,可能还需要配合设备管理器或辅助功能“快捷方式”才能维持长期开启。
2.4 应用内跳转引导:给普通用户做兜底方案
当脚本要分发给非技术用户时,不能要求别人敲 ADB 命令。需要在应用里加一个“检查权限”的按钮,点了以后跳到系统无障碍设置页,再通过循环检测等服务真正就绪。
function gotoAccessibilitySettings() { app.startActivity({ action: "android.settings.ACCESSIBILITY_SETTINGS" }); } function ensureAccessibility() { if (!auto.isServiceEnabled()) { gotoAccessibilitySettings(); auto.waitFor(); } } ensureAccessibility();这段代码的关键在auto.waitFor(),它会阻塞当前线程,用户手动打开服务后脚本继续往下走。注意auto.isServiceEnabled()在不同 AutoJs 版本的实现细节略有差异,但语义都是“当前应用的无障碍服务是否在系统已启用列表里”。如果返回值不准确,可以改用轮询方式,每秒检查一次,最多循环 20 次。
3. 无障碍服务开启后的核心代码:节点读取与控制操作的实战写法
权限通了之后,真正的业务逻辑才开始。AutoJs 源码里最常用到的就是节点选择器和手势模拟。这一章我按“读节点 → 做操作 → 等结果”的顺序写,每一步都给出可直接替换的代码片段,以及参数怎么调、失败时看什么。
3.1 读取节点:text、desc与bounds的优先级
无障碍节点树里,每个控件可能同时有 android:text、android:content-desc、resource-id 等属性。AutoJs 的 text() 和 desc() 选择器分别对应这两个字段。我的使用习惯是:优先 text,因为文案区域对用户可见,便于调试;其次 desc,因为很多图标按钮没有 text;最后再用 id。如果三者都匹配不到,再考虑 bounds 坐标。
// 等待登录按钮出现,最多等 5 秒 var loginBtn = text("登录").findOne(5000); if (loginBtn != null) { log("找到登录按钮,坐标范围: " + loginBtn.bounds()); loginBtn.click(); } else { log("text 匹配失败,尝试 desc 匹配"); var loginByDesc = desc("登录").findOne(2000); if (loginByDesc != null) { loginByDesc.click(); } }findOne(5000)里的 5000 是超时时间,单位毫秒。findOne 在等待期间会持续监听界面变化,一旦出现匹配节点就立即返回,而不是等满 5 秒。如果 5 秒内没有出现,返回 null。bounds()返回控件的矩形坐标,格式就是普通的四个边界值,调试时打印出来非常有用,尤其是后续要退化到坐标点击的时候。
3.2 点击、长按与滑动手势的参数设定
拿到节点之后,玄学问题一般都出在“节点有了,但点不中”。原因是某些控件可点击区域和 bounds 不一致,尤其是多层嵌套的列表项。这时直接用 bounds 的中心点做全局坐标点击更可靠。
// 用节点坐标点击 var target = desc("更多选项").findOne(3000); if (target != null) { var b = target.bounds(); click(b.centerX(), b.centerY()); } // 快速滑动,从 (100, 900) 滑到 (100, 300),耗时 300ms swipe(100, 900, 100, 300, 300); // 长按 800ms press(500, 600, 800);click(x, y)用的是物理像素坐标,不是 dp。大多数手机屏幕上 1080 和 1440 的设备之间,同一个控件的坐标差距很大,所以坐标方案要放在节点匹配之后。swipe最后一个参数是手势持续时间,单位毫秒。太短(小于 100ms)会被系统判定为 Fling 快速滑动,太长(超过 800ms)会触发拖拽模式,滚动场景需要反复试参数。
3.3 等待与轮询:防止脚本跑空
很多时候界面加载是异步的,按钮要等网络返回后才出现。在 AutoJs 里最直接的写法是阻塞式 findOne,但也有血泪教训:某些页面控件会被重复创建,或者节点树刷新时出现短暂的空窗期,一次性 findOne 容易取到旧节点。我一般会写轮询代替阻塞等待。
var clicked = false; for (var i = 0; i < 20; i++) { if (text("下一步").exists()) { text("下一步").findOne().click(); clicked = true; break; } sleep(500); } if (!clicked) { log("20 次轮询仍未找到下一步按钮"); }这段代码的逻辑很简单:每 500ms 检查一次节点是否存在,存在就点击并退出循环,最多循环 20 次,也就是最多等 10 秒。exists()方法不阻塞,也不会抛异常,比直接 findOne 更适合做条件判断。轮询的好处是遇到页面卡顿不会把整个脚本卡死,超时后可以走失败分支。
4. 避坑指南:无障碍权限失效的四个典型场景与解决
这一章写的是实际调试里最常见的四个坑。每一个都是现象 → 原因 → 解决的完整链路,照着排查基本能定位问题。
4.1 现象:服务已开启,脚本仍提示“未启用无障碍服务”
设置页里开关明明是绿色,但auto.isServiceEnabled()返回 false,或者日志里直接报“服务未连接”。最初遇到这个问题时,我以为是源码 API 的问题,后来才发现是系统里启用了多个无障碍服务时,AutoJs 检查的是自己包名对应的服务是否在已启用列表里,而系统可能在应用层缓存了旧的列表状态。
解决方法是先确认当前系统的真实状态:
adb shell settings get secure enabled_accessibility_services如果输出里确实有目标服务,但脚本依然提示未启用,就强制重启应用进程:
adb shell am force-stop com.example.autotask adb shell am start -n com.example.autotask/.MainActivity再不行就把应用加入“不优化电池”白名单和“自启动”白名单。部分系统还会拦截后台弹出界面,这个权限也需要放行。
4.2 现象:无障碍服务自动关闭,脚本跑到一半失效
脚本前几分钟正常,后面突然点击没有反应,检查设置页发现服务已经被关闭。这类问题在国产系统上尤其常见,系统省电策略会定时清理后台进程,而无障碍服务一旦进程被回收,就没有线程去维持服务绑定。
解决方法是双管齐下。第一,在系统设置里把目标应用锁定在最近任务列表,并放到“允许后台活动”名单;第二,在 AutoJs 入口代码里加一个心跳检查,每隔一段时间检查服务状态,发现被关闭就尝试重新初始化。
setInterval(function () { if (!auto.isServiceEnabled()) { log("服务掉线,尝试重新等待"); auto.waitFor(); } }, 30000);注意心跳间隔不要设太短,10 秒一次会比较频繁,建议 30 秒到 1 分钟。这种方式只能兜底,系统强杀服务时 auto.waitFor 可能也会等待超时,最终还是靠系统白名单解决。
4.3 现象:节点树一片空白,text和desc都匹配不到
手动开启服务后,打开应用界面,auto.dump()输出的节点树只有根节点和几个外层容器,业务控件完全看不到。这通常不是无障碍权限的问题,而是界面技术栈的问题。WebView、Flutter、游戏引擎、部分自定义 View 不会向无障碍服务暴露细粒度节点,系统拿到的只是一个大矩形。
// 输出当前界面节点树,检查是否有可读文本 auto.dump();发现节点空白后,唯一可靠的做法是退化到坐标点击。先通过截图或auto.screenshot()定位目标区域,再计算点击坐标。另一种思路是集成 OCR 插件识别截图文字,但 OCR 会增加包体积,而且识别速度慢,不建议默认启用。
4.4 现象:设备重启后无障碍权限丢失
很多脚本在设备重启后需要手动重新开启无障碍服务,不是系统缺陷,而是 Android 11 之后对无障碍服务的持久化策略变严格了,部分设备只在开机时做一次绑定,失败后不会自动重试。
解决方法是每次开机后主动检查一次。AutoJs 源码里可以监听 BOOT_COMPLETED 广播,但由于厂商限制,这个方案并不稳定。我常用的做法是在主 Activity 启动时调用一次 ensureAccessibility,也可以通过定时任务每小时检查一次。如果设备完全无人值守,需要同时关掉锁屏密码,并在应用里设置开机自启。
5. 从脚本到成品:封装无障碍能力时容易忽略的四个细节
脚本能跑通只是第一步,要交付给别人长期用,还需要处理分辨率适配、重复开启、异常兜底和系统兼容问题。这一章讲的都是工程细节,代码可以直接抄。
5.1 屏幕分辨率适配:坐标系与自动缩放
无节点可用的界面只能靠坐标点击,而不同设备的物理分辨率差异很大。我维护脚本时会先定一个标准分辨率,比如 1080 x 2400,然后把所有的坐标点都按比例换算,不让硬编码坐标直接散落在业务逻辑里。
// 获取当前设备真实分辨率 var screenW = device.width; var screenH = device.height; // 标准分辨率,按需修改 var baseW = 1080; var baseH = 2400; function cx(x) { return Math.round(x * screenW / baseW); } function cy(y) { return Math.round(y * screenH / baseH); } // 用法:在标准屏上的 (540, 1200) 位置点击 click(cx(540), cy(1200));这套方案的前提是页面元素在大多数手机上按比例缩放。如果遇到曲面屏或刘海屏,还需要考虑安全区域偏移,那是另一个层的适配问题。使用坐标方案时,建议把坐标点集中放在一个配置对象里,方便后期调整。
5.2 悬浮窗与状态管理:防止重复开启与多脚本冲突
AutoJs 源码里最常见的“脚本冲突”就是两个脚本同时运行,目标应用界面被抢来抢去,点击手势全部混乱。与其写复杂的锁机制,不如做一个简单的运行标记,启动脚本前先检查一次,如果有标记就直接拒绝启动。
var store = storages.create("script_state"); var runningKey = "current_running_script"; function tryEnter() { if (store.get(runningKey)) { toast("已有脚本在运行,请先停止"); return false; } store.put(runningKey, true); return true; } function safeExit() { store.remove(runningKey); log("脚本结束,释放运行标记"); }这个标记位只对正常流程有效,系统强杀应用不会执行 safeExit,所以下次冷启动时如果发现上次运行的 Session 异常,要做一次重置。可以给标记增加时间戳,超过 10 分钟的残留标记直接删除。
5.3 日志与异常兜底:把黑匣子变成可排查记录
无障碍脚本最怕的就是无人值守时静默挂掉。代码里没有异常捕获,节点查找失败会把整个脚本直接打断。我在关键动作外面包一层安全函数,记录错误信息到日志队列。
var errorLogs = []; function safeExec(tag, fn) { try { fn(); } catch (e) { var msg = "[" + tag + "] " + e.toString(); errorLogs.push({ time: new Date(), msg: msg }); if (errorLogs.length > 30) { errorLogs.shift(); } log(msg); } } safeExec("点击登录", function () { text("登录").findOne(3000).click(); });错误日志队列是我排查问题的第一入口。更完整的做法是定时把 errorLogs 写入本地文件,文件名带上日期,方便事后回溯。注意日志内容不要涉及敏感信息,节点匹配不上时只记录选择器字段,不记录整个页面截图。
5.4 多系统兼容:注意辅助功能快捷方式和后台限制
同一个应用在不同厂商系统里的无障碍服务表现完全不同。我见过某台设备打开无障碍后,只要切换应用就立刻被停用;也见过另一台设备需要同时开启“使用情况访问”权限,否则节点树不刷新。遇到这种问题,先别急着改代码,打开系统设置里的电池优化、后台弹窗、关联启动这三个入口查看限制状态。备一台常用测试机,把四个主流系统的兼容问题做成清单,比硬写兼容代码更省事。
6. 最后一步:用压力测试验证无障碍脚本的稳定性
脚本写完不代表能交付,我会把“重复跑 N 次都成功”当成最低门槛。具体做法是写一个简单的压测入口,循环执行核心动作,并在循环里检查无障碍服务状态和节点匹配情况,连续跑 10 次以上,观察是否有中断、超时、坐标漂移。
var passedCount = 0; var totalCount = 10; for (var i = 0; i < totalCount; i++) { if (!auto.isServiceEnabled()) { log("第 " + i + " 次检查发现服务不可用"); break; } // 模拟一次完整操作:等待节点、点击、短停留 var btn = text("提交").findOne(3000); if (btn != null) { btn.click(); sleep(1000); passedCount++; } else { log("第 " + i + " 次未找到提交按钮"); } } log("通过次数: " + passedCount + " / " + totalCount);压测时不要盯着屏幕看,运行几分钟后回头看日志。只要出现一次服务掉线或节点超时,就要回到第 4 章的排查链路里确认原因,而不是简单重跑一次碰运气。自从那次某个脚本连续跑 8 小时后在凌晨三点掉线,我再也没有跳过压测。每次交付前都会强制把所有流程执行三遍:一遍调试模式、一遍正常模式、一遍在锁屏状态下用辅助功能自动拉起。希望帮到你。
本文还有配套的精品资源,点击获取