首页 / 资讯中心 / 文章详情

AutoJs无障碍服务从源码到初始化:原理、配置与保活避坑指南

AutoJs无障碍服务从源码到初始化:原理、配置与保活避坑指南 ★ FEATURED ARTICLE
简介面向AutoJs初学者与自动化脚本开发者这份源码演示了如何通过无障碍服务实现辅助功能自动开启适合快速上手自动点击、控件读取、界面判断等常见应用场景。包体非常精简仅包含1个js格式脚本文件压缩包大小约581B无需额外依赖在低版本AutoJs环境中也能直接运行减少配置成本。目前已有872人学习/下载虽然代码量不大却完整覆盖了无障碍服务声明、开启、回调处理等核心代码框架可帮助理解AutoJs无障碍权限的申请逻辑与脚本编写思路。借助这份源码既能快速套用到个人自动化任务中也可作为研究无障碍机制的最小参考模板。资源仅供学习交流请勿用于商业用途。1. 装了AutoJs却卡在「开启无障碍」这一步问题出在哪儿第一次跑AutoJs脚本的时候大概率会撞上一条红色提示需要开启无障碍服务。你按提示跳去设置翻了一圈没找到入口或者明明打开了脚本一跑还是报「未开启」。这个标题里的「autojs开启无障碍」说白了是把AutoJs这一系自动化脚本的根讲清楚源码里怎么声明服务、运行时脚本怎么等它就绪、系统设置里怎么授权、以及各种开了却失效的翻车点。这不只是给脚本新手看的自己拉过源码想定制或复刻类似自动化能力的开发者同样需要弄明白AccessibilityService这套机制。下面从源码层到脚本层一步步拆开。2. 从源码看AutoJs的无障碍为什么它离了AccessibilityService就跑不动2.1 AccessibilityService是什么AutoJs为什么必须依赖它AccessibilityService是Android系统提供的一套辅助功能服务由系统绑定并运行App自己不能启动它只能由用户在系统设置里手动授权。服务一旦运行可以做到三件事接收全局无障碍事件、读取当前窗口的控件信息、模拟手势操作。AutoJs这类自动化脚本恰恰需要这三样——读控件树来定位按钮注入点击和滑动来操作界面。这也是AutoJs不需要root权限的原因。相比之下root方案走的是shell命令能做的事更多但门槛和安全风险也高无障碍方案胜在免root、覆盖面广代价就是它完全依赖系统授权用户一旦关闭或系统回收服务脚本立刻瘫痪。所以AutoJs源码里最核心的权限声明不是网络权限而是这个无障碍服务。如果你拉过源码只要在工程里搜AccessibilityService这个关键词基本就能定位到它的实现类和配置文件。理解这一层后面所有「开了没生效」的问题都好排查。2.2 源码里的三重声明Manifest、Service配置与权限检查任何无障碍App都绕不开两处声明AndroidManifest.xml里注册Serviceres/xml下放服务配置文件。以常见开源自动化项目的写法为例Manifest里长这样service android:name.service.MyAccessibilityService android:exportedtrue android:labelstring/accessibility_service_label android:permissionandroid.permission.BIND_ACCESSIBILITY_SERVICE intent-filter action android:nameandroid.accessibilityservice.AccessibilityService / /intent-filter meta-data android:nameandroid.accessibilityservice android:resourcexml/accessibility_service_config / /service这里的android:permissionandroid.permission.BIND_ACCESSIBILITY_SERVICE是关键系统只允许持有该权限的应用绑定这个服务。android:exportedtrue在旧版本上可能不写也能跑但Android 12以上如果漏了系统直接忽略这个服务设置列表里就找不到你。配置文件决定了这个服务的能力边界accessibility-service xmlns:androidhttp://schemas.android.com/apk/res/android android:accessibilityEventTypestypeWindowStateChanged|typeWindowContentChanged|typeViewClicked|typeViewTextChanged android:accessibilityFeedbackTypefeedbackGeneric android:accessibilityFlagsflagDefault|flagReportViewIds|flagRetrieveInteractiveWindows android:canPerformGesturestrue android:canRetrieveWindowContenttrue android:descriptionstring/accessibility_service_description android:notificationTimeout100 /canRetrieveWindowContent表示允许服务读取窗口控件内容不打开这个脚本的text()、desc()这类控件选择器全部失效。canPerformGestures是Android 8.0以后模拟手势的开关AutoJs的click()、swipe()都依赖它。notificationTimeout是事件合并间隔单位毫秒设得太小事件量大会增加耗电设得太大又容易丢事件一般取50到200之间。2.3 无障碍拿到的数据长什么样从事件回调到控件树服务启动后系统会持续通过onAccessibilityEvent回调往里抛事件。常见的有窗口状态变化TYPE_WINDOW_STATE_CHANGED、点击TYPE_VIEW_CLICKED、文本变化TYPE_VIEW_TEXT_CHANGED、内容变更TYPE_WINDOW_CONTENT_CHANGED。AutoJs源码在收到这些事件后会刷新当前激活窗口的控件树再把树里的每个节点包装成带text、desc、bounds、clickable这些属性的对象脚本里的UiSelector就是在这棵树上做过滤。这里有个容易被忽视的点accessibilityEventTypes如果配的是typeAll服务会接收到所有类型的事件采集信息最全但事件量也最大。AutoJs的auto.setMode(fast)要生效前提就是服务能收到足够密度的手势和点击事件某些ROM上事件被系统裁剪后fast模式会明显变迟钝。所以当你发现脚本卡在选择器上时优先怀疑的不是选择器写错而是无障碍服务没有拿到完整的控件树。3. 把「开启无障碍」做成脚本第一步auto()系函数与normal/fast模式3.1 auto.waitFor()等用户手动开启而不是干等很多人在脚本第一行写auto()这个函数确实会检查无障碍服务但它的行为是如果服务没开会尝试跳转设置页然后立刻返回继续执行。问题在于用户还没操作完后续的控件查询就全扑空了。我一般不会用裸auto()做首行而是用auto.waitFor()它会阻塞脚本直到服务真正可用才往下走auto.waitFor(); log(无障碍服务已就绪); // 到这里才能安全地使用 UiSelector否则 text(xxx) 大概率拿到 nullwaitFor()内部会循环检查服务的启用状态用户从设置页开启后切回脚本函数会自动返回不需要额外写轮询。注意它没有超时参数如果用户始终不开脚本会一直挂着所以在长时间无人值守的场景里我会在外面再包一层setTimeout来兜底。常见的兜底写法是把waitFor扔进一个Promise里同时启动一个定时器去检查服务状态并做超时退出。function waitForService(timeoutMs) { return new Promise((resolve) { auto.waitFor(); let ok !!(auto.service auto.service.active); if (ok || !timeoutMs) { resolve(ok); return; } setTimeout(() { resolve(!!(auto.service auto.service.active)); }, timeoutMs); }); }这个封装的逻辑是能等到就直接返回等不到就在时间到后返回当前真实状态。脚本侧拿到false后就可以走提示逻辑不会让整个自动化流程无限期挂起。3.2 两种运行模式normal与fast什么时候用哪种AutoJs在无障碍层有两种运行模式通过auto.setMode()切换auto.setMode(normal); // 常规无障碍模式读取控件树、执行点击滑动兼容性最好 auto.setMode(fast); // 快速模式事件获取更密集某些设备上响应更快但需要系统服务配合normal模式是通用无障碍服务任何开启了服务的设备都能用。fast模式走的是无障碍服务里的一条快速事件通道对系统版本和ROM的实现有要求不支持的设备上设置后没有实际变化甚至可能导致部分回调变慢。我的建议是长按类、拖拽类操作保持normal纯点击和读文字的高速重复任务可以在fast下测试发现行为不对立刻切回。注意切换模式后最好重新检查一次auto.service状态因为部分系统在模式切换时会重建服务连接。3.3 用代码检测无障碍服务是否在运行不在就拉起设置页脚本里最可靠的判断不是看auto()回没返回而是直接取服务对象function ensureAccessibility() { if (auto.service auto.service.active) { return true; } toast(请先开启无障碍服务); app.startActivity({ action: android.settings.ACCESSIBILITY_SETTINGS }); auto.waitFor(); return auto.service auto.service.active; } ensureAccessibility();auto.service在服务未绑定时是null绑定后是一个AccessibilityService实例active字段表示它正处于激活状态。这里用action拉起系统无障碍设置页大部分原生系统都支持这个action个别国产ROM上跳转后停留在不可用的总开关页需要用户再点进具体App项打开服务属于系统差异脚本只能做到把用户送到最接近的位置。4. 开启无障碍避坑点开了却没生效、重启就失效、设备上找不到入口4.1 现象设置里明明开着脚本还是报「未开启无障碍服务」很多时候用户在设置里看到开关是绿的但脚本一跑就报错。原因一般有两个一是系统里记录的服务进程已经被回收开关还显示开启实际服务已死二是脚本在服务完全绑定前就去查控件树恰好撞上初始化窗口。解决方法是先做一次显式等待再检查auto.service的真实状态而不是相信设置页的UI。有一个血泪经验不要在两个脚本进程同时操作同一个无障碍服务有些系统上服务会被后一个进程挤掉造成「开了等于没开」的假象。4.2 现象国产ROM里服务反复被系统回收开启不到半小时就变灰这类问题几乎都在国产ROM上出现省电策略会定期清理「长时间没有界面活动」的后台进程无障碍服务正好符合被清理的特征。脚本本身写得再好也拦不住系统强杀。能做的只有几件事把App加入后台运行白名单、允许自启动、关闭对它的电池优化有的ROM还需要在最近任务里给App加锁。这些操作在不同系统上位置不一样但逻辑相同——让系统认为这个应用是「用户正在依赖的」而不是可回收的后台任务。4.3 现象Android 11以上授权弹窗消失跳转设置后找不到AutoJs新版本Android把无障碍列表改成了多级结构直接跳转ACCESSIBILITY_SETTINGS后默认展示的是「已下载应用」这一组自己编译安装的应用可能被折叠在其他分类里看起来就像消失了。另一种情况是应用列表里根本没有这一项说明Service注册有问题系统解析时直接丢弃了。这时先确认Manifest里exported为true再检查配置文件里的meta-data指向的xml是否存在于工程中最后在系统设置里用搜索功能搜应用名能搜到就说明服务没死只是入口深了。4.4 现象从源码编译自己打包改包名后无障碍直接丢了改applicationId后无障碍失效是高发问题。原因有多个改了包名导致Service路径没同步混淆规则把Service类名改了新的签名与旧安装包不一致系统里还留着旧的组件记录。常见做法是排查时依次做四件事确认Service的android:name指向的类还在且未被混淆确认meta-data的resource指向的配置文件没有写错先卸载旧包再装新包在Android 12以上设备检查exported没有被动过。这套排查顺序能覆盖九成以上的情况。如果是在模拟器或某台旧设备上测试还要确认系统没有把那个包名的残留数据带过来卸载后重启设备再安装最稳妥。4.5 现象服务开关打开瞬间又自动跳回关闭这类问题在厂商定制的系统上偶发通常不是代码错误而是系统安全策略认为该服务「没必要常驻」或与系统自带的省电模式冲突。可以先试着重启设备再开如果仍然立刻跳回就去系统设置里查找有没有「安全」相关的辅助功能拦截。这个现象没有统一的代码解只能通过调整系统设置来规避属于玄学问题里最磨人的一种。遇到这种设备别硬刚换一台系统版本相近的机器继续开发事后单独研究策略差异别让单台设备卡住整个项目进度。5. 保活与自愈让无障碍服务在长跑脚本里不掉线5.1 监听AccessibilityEvent用系统事件喂饱服务长时间运行的自动化脚本最怕的就是服务在半夜被系统回收。一些ROM会参考事件活跃度来判定服务是否还在被使用如果长时间收不到事件就把进程标记为可清理。所以长跑脚本里我会注册一个全局事件监听一方面统计事件量另一方面让服务持续处于「有消费者」的状态let eventCount 0; events.onAccessibilityEvent(function (accessibilityEvent) { eventCount; if (eventCount % 100 0) { log(已接收事件数: eventCount); } });这段代码本身不做业务只做事件计数。注意events.onAccessibilityEvent注册的是全局回调事件量大时要避免在回调里做耗时操作否则可能出现事件积压反而拖慢脚本。如果设备上事件本来就少可以试着在配置里把notificationTimeout调小到50事件密度会明显上来。这里也解释了一个常见疑问为什么服务「看起来在线」但自动化操作时的响应明显偏慢多半是事件源被系统降频了。5.2 服务被杀后自动重启定时探活 拉起设置页保活只是降低概率不能根治所以我习惯再补一层自动「探活」逻辑定时检查服务状态发现掉线就拉起设置页提醒用户重新开启setInterval(function () { if (!auto.service || !auto.service.active) { log(检测到无障碍服务已失效); app.startActivity({ action: android.settings.ACCESSIBILITY_DETAILS_SETTINGS, packageName: context.getPackageName() }); toast(无障碍服务已失效请重新开启); } }, 5 * 60 * 1000);探活间隔不宜太密太密会频繁弹跳转页面干扰正在进行的操作我一般取5到10分钟。ACCESSIBILITY_DETAILS_SETTINGS配合packageName会直接跳到指定App的无障碍详情页比通用列表少点一步。但要注意这个action并不是所有ROM都支持不支持时脚本要捕获异常并回退到ACCESSIBILITY_SETTINGS。这层逻辑的作用是「发现问题并引导恢复」不是绕过授权因为无障碍服务从设计上就不允许被应用私自拉起来。5.3 后台限制与省电白名单不是代码问题是ROM策略很多掉线问题查到最后根因是系统设置而不是代码。常见的几处设置分别是关闭电池优化、允许后台运行、关闭省电模式下的限制、允许自启动。这几个开关在不同品牌系统里的叫法不同但作用一致。还有一个习惯值得养成脚本开发完不要只在开发机上验证至少要在一部主流国产ROM和一部原生或类原生系统上各跑一遍因为同一段脚本在两种系统上的服务存活率可能差别很大这与代码好坏无关纯粹是系统策略差异。把这些策略差异整理成一份设备清单比在代码里堆各种保活黑科技靠谱得多。6. 进阶验证把自己做的「开启无障碍」升级成可观测的一键初始化6.1 用无障碍事件流做自检和回放我现在的做法是把初始化函数和事件日志绑定在一起每次脚本启动先清空旧日志然后开启事件监听跑完一轮后回查日志里有没有TYPE_WINDOW_STATE_CHANGED事件。如果一轮操作下来事件计数完全没有增长基本可以判定无障碍服务是假的在线脚本再往下跑只会更糟。这里的日志不只是给人看的我习惯把事件类型和时间戳追加到本地文件作为事后排查的依据服务掉线的时间点、恢复的时间点、事件断流的位置都能通过这份日志还原出来。6.2 把初始化封装成一个可重入函数function initAccessibility() { if (auto.service auto.service.active) { return true; } app.startActivity({ action: android.settings.ACCESSIBILITY_SETTINGS }); auto.waitFor(); return !!(auto.service auto.service.active); }这个函数被我放到所有脚本的公共模块里不管脚本执行多少次它都能做到已开启就直接返回未开启就跳设置并阻塞等待最终返回真实状态。踩过几次服务假死的坑之后我已经养成了习惯所有脚本的第一行日志必须是服务状态输出而不是直接跑业务逻辑。先确认无障碍真的在线再动手能省下大量排查时间希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站