☰
auto.js实战指南:从环境搭建到稳定运行Android自动化脚本
2026/10/2 9:38:53 网站建设 项目流程

“基于auto.js脚本2”——这个标题看着朴实,但懂的人一眼就知道,这是要聊 Android 自动化里最趁手的一类工具。做开发、做测试、或者平时被手机上各种重复操作烦到的人,多半都动过“把这些点来点去的事交给机器做”的念头。auto.js 就是干这个的:它基于 Android 的无障碍服务,用 JavaScript 写脚本,能模拟点击、滑动、长按、输入文本,还能读取屏幕上的界面元素,实现“让手机自己操作手机”。

这篇文章我不打算写成文档翻译,而是围绕我实际用 auto.js 踩坑、调优、做自动化任务的过程,把从环境搭建到实战脚本、再到问题排查的完整链路捋一遍。无论你是刚听说 auto.js,想拿它解决工作里的报表、打卡、群发这类琐事,还是已经写过几段脚本但总觉得不够稳,这篇文章应该都能给你一些能直接照着用的东西。

1. auto.js 到底是什么:先搞清楚原理再动手

1.1 它和普通“脚本”的本质区别

热词列表里能看到 shell 脚本、Python 脚本、PowerShell 脚本一类的东西,那些大多是跑在电脑系统里的命令行程序。auto.js 不一样,它跑在 Android 系统里,工作对象是“屏幕上看得见的界面”。

它背后的核心是 Android 的无障碍服务(AccessibilityService)。这个服务本来是给视力障碍人士辅助操作手机用的,系统允许它读取当前界面的节点信息、模拟手势。auto.js 把这套能力封装成了 JavaScript API,于是你可以这样做事:先通过文本、ID、描述这些属性找到某个按钮,再对它执行点击,整个过程就是脚本在代替你的手指。

很多初学者不理解“无障碍”和“自动化”的关系,总觉得要靠 root 权限或者什么底层协议。实际上 auto.js 走的是系统公开接口,不需要 root,这是它最大的优势之一。当然它也带来限制:无障碍服务能拿到的信息有时候不完整,比如某些 App 的界面是 Canvas 自绘的,系统看到的就是一块画布,没有具体节点,这种情况下面专门讲。

1.2 auto.js 能解决什么问题,有什么不能做

我这些年用 auto.js 做过的事情大概可以分三类,你可以对照看自己属于哪种场景:

  • 高频重复操作:比如每天到公司打开钉钉/企业微信打卡,比如每天填写固定的日报模板,比如把手机里的文件按规则批量重命名。
  • 定时自动化:配合 auto.js 的定时任务功能,让脚本在指定时间自动执行,比如早上九点自动打开某个应用签到。
  • 测试辅助:做 App 测试的同学可以用它做冒烟测试的快速回归,不用每次手动点几十步流程。

但有几点必须提前说清楚。第一,auto.js 不是万能的,如果某个界面使用自定义绘制、WebView 内嵌复杂页面,节点信息可能读不全。第二,无障碍服务在某些定制 ROM 上会被系统“省电优化”杀掉,这是日常维护里最头疼的问题。第三,不要拿它去做破坏公平性的东西,比如抢购、外挂、自动刷排名,一方面道德上讲不过去,另一方面这类操作很容易被后台风控识别,账号受限是常有的事。

2. 环境准备:从装好应用到跑通第一个脚本

2.1 手机端安装与权限配置

auto.js 的安装包虽然流传版本比较多,但使用逻辑基本一致。安装完成后第一件事不是写代码,而是把权限配好。

进入系统设置,找到“无障碍”或“辅助功能”,在服务列表里找到 Auto.js,打开开关。这里要注意,有些 ROM 把无障碍入口藏在“更多设置”或者“已下载的服务”里,找不到就让我行搜索“无障碍”。同时建议开启自动脚本需要用到的高悬浮窗权限,不然脚本运行时的悬浮控制台显示不出来,调试会很不方便。

权限配好之后,先做一个最简单的验证:直接在 App 自带的编辑页里写一行console.log("hello auto.js"),点击运行,看控制台是否输出。这一步是为了确认无障碍服务真正生效了,而不是等后面脚本跑不动才怀疑环境问题。我见过不少新手,脚本点了“运行”之后没有任何反应,查了很久发现其实是服务开关没打开。

2.2 PC 端开发环境:为什么推荐 VS Code + 插件

在手机上敲代码确实难受,所以团队开发基本都会用电脑。推荐的组合是 VS Code 加 Auto.js-VSCode 插件,手机和电脑连同一个局域网,电脑端写好代码直接推送到手机运行,还能看日志。

配置流程不复杂:电脑装 VS Code,扩展市场搜 Auto.js 装好;手机和电脑连同一个 Wi-Fi;手机端打开 Auto.js 的“连接电脑”服务界面,抄下显示的 IP 和端口;在 VS Code 里按 Ctrl+Shift+P,输入Auto.js相关命令,填上手机 IP 和端口,连接成功后在编辑器里右键就能“运行”当前脚本。

这个环境搭建环节值得花时间弄好,因为后面调试脚本的效率高很多。很多从手机直接“保存-运行”的人会反复改一行代码就要在手机上操作半天,其实完全可以交给 VS Code 统一管理。补充一句,新版本有些同学会自己编译,如果只是用现成功能,直接装打包好的版本就行,没必要上来就折腾源码。

3. 核心 API 与实操:用代码把“点屏幕”这件事说清楚

3.1 选择器是怎么工作的:定位元素的正确姿势

auto.js 最核心的概念是 UiSelector,也就是“选择器”。它的作用是从当前屏幕的界面节点树里找到你想操作的那个元素。最常用的匹配方式有这几种:

  • text("打卡"):按文本内容精确匹配。
  • textContains("打卡"):包含“打卡”的文本。
  • desc("按钮描述"):按内容描述匹配,很多图片按钮没有文本,但会有 desc。
  • id("xxx"):按资源 ID 匹配,这个通常最稳定。
  • className("android.widget.Button"):按控件类型匹配。

我个人的使用经验是:优先用 id,其次用 desc,最后才用 text。原因很简单,文本经常变,今天“打卡”明天“签到”,而且同一屏可能有多个相同文本;ID 是开发在布局里写死的,只要 App 不大版本重构,基本稳定。

选择器拿到的是一组候选节点,怎么从候选里挑出真正要的那个?可以用findOnce()拿第一个,或者用find()拿到数组后自己再加条件过滤。这里有个常见误操作:新手喜欢直接text("确定").click(),当屏幕上有多个“确定”时会出问题。更稳的写法是:

var confirmBtn = text("确定").findOnce(); if (confirmBtn) { confirm(); // 点击 } else { log("没找到确定按钮"); }

3.2 点击、滑动、输入:三个最常用的动作

点击看起来简单,但实现方式有讲究。直接用click(x, y)是“坐标点击”,属于物理层操作,不用关心屏幕上有啥;用text("确定").click()是“节点点击”,会先找到按钮再点它的中心点。两者各有利弊:节点点击更智能,界面如果滚动了位置也能点到;坐标点击在节点信息拿不到的时候是兜底方案。

滑动的 API 是swipe(x1, y1, x2, y2, duration),五个参数分别是起点、终点坐标和滑动耗时。注意它用的是屏幕坐标,不是节点坐标。如果想滑动某个控件,可以先拿到节点的 bounds(),再计算中心点,这样写出来的脚本对不同屏幕尺寸的适配性更好。

输入的 API 有两类:setText(index, text)和input(text)。setText 是对当前聚焦的输入框直接设置文本,速度很快但部分输入法下会不触发某些监听;input 是模拟键盘逐个字符输入,更接近真人操作,但速度慢一些。我建议优先 setText,如果遇到 App 对输入做了特殊校验(比如金额输入框不能直接设置过长数字),再换 input 试试。

来看一个完整的小例子,这个场景很常见:打开某个需要登录的应用,自动填入账号密码并点击登录按钮。

// 等待“账号输入框”出现,最多等10秒 var accountEdit = className("android.widget.EditText").findOnce(); if (accountEdit) { accountEdit.click(); setText(0, "my_account"); } // 切到密码输入框,一般EditText会多出几个 var editList = className("android.widget.EditText").find(); if (editList.length > 1) { editList[1].click(); setText(1, "my_password"); } // 点击登录 text("登录").click();

这段代码里没有写死坐标,所以换不同分辨率的手机,理论上也能跑。这就是“基于节点操作”相比“坐标脚本”的最大优势。

3.3 不要硬点:等待与轮询的思维

新手最常见的脚本问题是:界面还没加载完就急着去点,结果找不到元素直接报错。解决办法是“等待条件满足再操作”。auto.js 里简单粗暴的做法是sleep(2000),但固定延时不可控,网络稍微慢一点就白等了。

更合理的是轮询:写个 while 循环,不停检查目标是否存在,超时后自动退出。

function waitFor(selector, timeout) { var t = timeout || 5000; var start = new Date().getTime(); while (new Date().getTime() - start < t) { var target = selector.findOnce(); if (target) return target; sleep(500); } return null; } var btn = waitFor(text("立即打卡"), 10000); if (btn) { btn.click(); } else { log("10秒内没等到打卡按钮,退出"); }

这段代码的核心价值是把“等”和“操作”分离了。往后所有脚本里,涉及界面跳转、网络请求后的操作,都应该走这种等元素再点击的模式,而不是无脑 sleep。

4. 实战案例拆解:自动打卡与批量处理这样写才稳

4.1 案例一:每天早上自动打开应用打卡

需求很简单:打开 App,点击底部“工作台”,进入考勤页面,点“打卡”按钮,最后截图确认。难点在于每一步的定位。

我第一次写的时候直接照抄别人文章的text("打卡").click(),结果跑了三天后失效了。后来发现,App 更新之后按钮的文本由“打卡”变成了“立即打卡”,而且页面上有两个类似的入口。正确做法是先手动 dump 当前界面的节点信息,确认目标按钮的独特属性再写选择器。

完整脚本可以长这样:

// 1. 拉起App launch("com.example.kaoqin"); // 2. 等待工作台Tab出现并点击 var tabWork = waitFor(text("工作台"), 10000); if (tabWork) tabWork.click(); // 3. 等待考勤入口出现 var attendance = waitFor(desc("考勤打卡"), 10000); if (attendance) attendance.click(); // 4. 在考勤页内等待按钮可以点击 var punchBtn = waitFor(text("立即打卡"), 10000); if (punchBtn) { punchBtn.click(); log("打卡操作完成"); } else { log("没有找到打卡按钮,可能不在考勤时间或已经打过卡"); }

这里我特意把每个等待都做了超时处理,目的是脚本失败时能知道具体卡在哪一步。实际运行中,如果某一天页面弹了个广告,第一个等待可能一直不满足,10秒后自动退出,日志会提示第一步就失败了。这种定位问题的速度,比脚本直接挂掉强非常多。

4.2 案例二:批量处理通知或重复弹窗

另一个高频场景是处理弹窗。很多工具类应用每天第一次打开都会弹“今日推荐”“隐私协议”“更新提示”,这些弹窗关掉之后界面才能正常操作。

我写过一个小函数,用它统一处理“关闭按钮”:

function closePopup() { var closeBtns = [text("关闭"), desc("关闭"), id("close_btn"), text("知道了"), text("以后再说")]; closeBtns.forEach(function (s) { try { var btn = s.findOnce(); if (btn) { btn.click(); log("已关闭弹窗: " + s.toString()); sleep(800); } } catch (e) { // 节点不存在时不用慌,继续下一个 } }); }

这个函数的思路是把所有可能出现的弹窗关闭文案都试一遍,遇到哪个点哪个。不要小看这种“粗鲁”的写法,在节点解析不完整的场景下,枚举比精确匹配更实际。

4.3 稳定性优化:日志、异常与线程

脚本写多了以后,你会发现真正花时间的不是“实现功能”,而是“让它稳定跑一个月”。我踩过的几个坑,希望你不要再踩。

第一个是崩溃问题。节点不存在时直接.click()会抛异常,所以所有可能不存在的节点操作都要 try-catch 或者先判空。我不建议每个方法都包一层 try-catch,但整体脚本会放在一个大的异常捕获里,出错时把错误信息写到文件:

try { main(); } catch (e) { var msg = e.toString() + "\n" + e.stack; files.write("/sdcard/auto_error.log", msg); log("脚本异常: " + msg); }

第二个是线程问题。auto.js 支持threads模块创建新线程,配合setInterval可以做一些并行任务,但新手很容易写出多个线程同时点击,导致界面混乱。我建议初期保持单线程思维,所有操作线性执行。真要用多线程,至少给要操作的界面元素加锁,确保同一时间只有一个线程在动屏幕。

第三个是日志。跑自动化脚本最怕“跑了,但不知道跑没跑对”。我在关键步骤后都会加log(),并且把日志同时写入文件。这样第二天对账时能快速看到脚本执行到哪一步,是不是某一次弹窗卡住了。

5. 常见问题与排查技巧实录

5.1 无障碍服务自动失效:定制 ROM 的宿命

这大概是所有 auto.js 使用者反馈最多的问题。明明脚本昨天还好好的,今天一点运行就提示无障碍服务未开启。去设置里一看,服务开关确实被关了。

原因大多不是 auto.js 自己崩了,而是系统省电策略把它“优化”了。解决方案是到系统的“电池优化”里把 Auto.js 设为“不优化”,在“自启动管理”里允许它后台运行,锁屏清理设置中把它加入“白名单”。不同 ROM 的设置路径差异很大,建议直接用系统设置的搜索功能搜“自启动”“电池优化”这些关键词,逐个排查。还有一点,尽量不要在脚本运行过程中手动重启手机,重启后服务重新注册的几秒钟内,脚本如果被调起来可能也会出问题。

5.2 选择器匹配不准,或者根本匹配不到

如果你确定节点存在但脚本找不到,大概率是下面几种情况。

一是界面是 Flutter、Unity 这类自绘引擎渲染的,系统 UI 节点信息很少,很可能只有一个根节点,内部子元素全都不可见。这种场景下别死磕节点,改用坐标:通过截屏分析、或者直接观察真实设备,人工获取关键位置保存成配置。二是节点在屏幕外,比如列表底部的内容,要先 scrollDown() 滚动后再找。三是 WebView 里的界面,用text()直接匹配不一定有效,可以尝试className("android.view.View")一级一级往下遍历,但不建议在 WebView 里做太复杂的定位,成本太高。

5.3 脚本运行到一半卡住

卡住的本质是某个等待条件永远不满足。排查思路其实很简单:在每一步关键操作前打印日志,运行一遍看最后一条日志是什么,就知道卡在哪个函数里。然后针对那一步单独做超时控制。我习惯把 5.3 节里的waitFor封装成基础函数,后面所有脚本都复用它,这样就不会出现无限等待。

另外,如果脚本里用了while(true),注意加退出条件。我见过有同学写“直到成功”的循环,结果某天界面状态异常,这个循环就变成死循环,把手机电量耗到关机。最安全的写法是给循环加最大尝试次数,比如 20 次之后仍然失败就主动退出。

5.4 排查速查表

平时帮人看脚本问题,我总结了一张速查表,新手遇到问题先照这个排查:

现象先检查什么可能的解决办法
点击无反应无障碍服务是否开启重新开启服务,进设置检查自启动和电池策略
提示元素找不到目标界面是否已加载完成加等待轮询,检查选择器属性和文本大小写
部分机型点击偏移是否用了坐标点击改用节点点击,或按屏幕宽高比例换算坐标
定时任务不执行应用是否被杀或锁屏被清理加白名单,关闭省电策略,检查定时表达式
脚本偶尔崩溃是否存在空指针调用所有 findOnce() 结果判空,外层加 try-catch
界面无法读取App 是否使用自绘引擎放弃节点方案,改用坐标和 OCR 辅助

这张表我建议直接截图存到手机里,排查问题的时候对照着看,比自己瞎猜快得多。

6. 关于 auto.js 脚本的一些个人体会

如果让我总结一句实操心得,那就是:auto.js 脚本的重心不在“怎么写代码”,而在“怎么描述操作”。很多人一上来就抠语法,结果写着写着自己先晕了。我习惯先把要自动化操作的步骤用文字列出来,比如“打开App -> 等首页加载 -> 点击我的 -> 点击设置 -> 关闭消息推送”,每一步都想清楚“界面状态是怎样的、怎么确认这步成功了”,然后再翻译成 API 调用。这样出错的概率会低很多,而且后面接手的人也能看懂。

还有一个小技巧:无论脚本多简单,都建议把日志落盘,并保留最近几天的日志文件。自动化任务最怕的是“它动了,但你不知道它动了什么”。有日志,第二天就能快速复盘;没日志,一切都只能靠猜,排查效率天差地别。

最后再说一点可扩展的方向。如果你已经能把界面操作脚本写得比较稳,可以把它跟 HTTP 请求结合,比如从远端拉取当天的工作安排,根据安排自动执行不同流程;或者把 OCR 能力引进来,解决那些拿不到节点信息的自绘界面。auto.js 本身的能力边界很清晰,但把这些边界以外的能力组合起来,你能做的自动化会远远超出“点两下屏幕”的范畴。

希望这篇文章对你有点用。如果你也在折腾 auto.js 自动化,回去按上面的框架整理一下你已有的脚本,把等待和异常处理补上,再用日志武装一遍,体验会好非常多的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询