
如果你一直在找一种方式让手机里的阅读App自动翻页、自动签到、自动整理书架Autojs这个安卓自动化工具几乎就是为此准备的。我最近把一个真实项目从零到一完整做了一遍用Autojs脚本控制番茄免费小说实现自动翻页阅读最后把脚本打包成独立Apk并把源码按模块重新整理好。整个过程踩了不少坑特别是Apk打包和真机坐标适配这一块今天拆开来讲清楚。适合正在学Autojs、想做一个能落地的自动化项目、或者想了解脚本如何变成安卓应用的人参考。1. 项目到底在做什么Autojs 番茄小说 Apk/源码的完整拼图1.1 一句话讲清需求这个项目不是简单的“写一段脚本点两下就完事”而是把一条完整的自动化链条走通先写Autojs脚本让番茄免费小说在指定时间自动打开、进入书架、打开某本小说、自动翻页阅读再把脚本的源码工程化组织起来封装成可维护的模块最后把它打包成一个独立的Apk装在另一台没有安装Autojs的安卓手机上也能运行。标题里出现“Apk文件和源码”其实对应两个非常实际的需求。第一脚本写好了不方便发给别人给朋友装一个Autojs再导入脚本门槛太高直接打包成Apk双击安装更符合普通用户习惯。第二脚本一旦超过两三屏代码就容易变成一团乱麻源码的模块化、版本管理和路径配置必须从一开始就规划好否则后面加一个“定时阅读”功能都要翻半天代码。所以你可以把这个项目理解成用Autojs做应用层自动化用Android应用的打包思路做交付用软件的工程化思维做源码管理。三者合在一起才是一个“实战教程”该有的完整度。1.2 为什么选择Autojs而不是其他自动化方案安卓自动化工具并不少Appium、Udacity、Tasker、无障碍脚本、ADB命令都能做类似的事情但Autojs在这个场景下几乎是首选。原因有三。第一Autojs基于JavaScript写起来没有Java或Kotlin那么重不需要Android Studio不需要Gradle构建在手机上装一个App就能开始调试。对于“我想让App自动翻页”这种需求打开Autojs写几行代码点运行几秒钟就能看到效果这种即时反馈对学习自动化非常有帮助。第二Autojs自带控件分析能力可以通过文本、ID、描述、按钮文字等属性直接找到界面上的元素。比单纯用坐标点击要稳定得多。比如番茄小说的“书架”按钮在不同手机上可能坐标不一样但文字“书架”基本不会变我们直接找text(书架)再点击就能跨机型适配。第三Autojs可以打包成Apk。这是很多脚本框架做不到的或者做起来非常繁琐。Autojs把JavaScript引擎、自动化服务、打包工具都集成在一起配置好图标和包名就能生成独立应用非常适合用来做个人工具类App。当然它也有局限性。比如无障碍服务在某些定制ROM上会被系统杀掉或者需要用户手动开启权限再比如对iOS完全无能为力。但这些都不影响它在“安卓手机自动化”这个场景里的实用地位。1.3 这个方案解决了什么痛点真正喜欢在手机上看小说的人应该都有这种体验一章读完手要抬起来点一下屏幕冬天缩在被子里尤其难受有时候看久了手指头酸眼睛也累。更别说一些需要“挂机阅读时长”的场景手动翻页非常消耗耐心。这个项目能解决的第一个痛点就是把“手动翻页”变成“脚本翻页”让手机按照设定好的时间间隔自动翻页而且可以设定总时长到点自动停止。第二个痛点是效率写一次脚本以后所有番茄小说上的书籍都能复用只需要改一下书目匹配规则。第三个痛点是工程化能力很多人写自动化脚本都是写完就丢三个月后想复用发现连自己都看不懂但按照源码管理的方式组织好之后每次打开都知道该改哪个文件。2. 动手前的环境准备工具链与测试机选择2.1 完整工具清单做这个项目之前我先列一个基础工具清单这些不是可有可无的而是每一件都会在后面的步骤里被用到。工具/资源用途备注安卓手机一台跑Autojs和番茄小说建议Android 7以上真机优先Autojs编写和运行脚本我用的是Autojs Pro 4.1.14.x开源版也可番茄免费小说App自动化操作目标建议固定版本关闭自动更新USB数据线连接电脑做日志调试部分手机需要开启USB调试VSCode或任意文本编辑器管理源码文件配合Autojs插件更好Git做源码版本管理本地仓库或远程仓库都可以打包工具生成ApkAutojs Pro内置打包或用官方离线打包如果你手头只有一台手机没有电脑也能完成大部分工作因为Autojs自带编辑器可以直接在手机上写代码。但有电脑会舒服很多至少日志查看和源码管理不会那么痛苦。最推荐的组合是一台主力机做开发一台旧手机专门跑自动化测试因为自动化过程中要反复开权限、杀后台、装包在主力机上很容易影响正常使用。2.2 Autojs版本怎么选Autojs的版本情况比较乱市面上的选择大致有三类GitHub上的开源4.x版本、商业化的Autojs Pro版本、以及各种个人修改版。开源4.x版免费满足控件操作、JavaScript语法等基本需求但打包功能比较弱很多功能需要自己配。Autojs Pro打包能力更成熟支持图标配置、包名配置、离线打包还支持把脚本加密到Apk里适合做分发。我的建议是如果你刚开始学用开源4.x版就够了如果后续要正式打包成Apk发给别人再考虑Pro版。这里的核心原则是别从不明渠道下载来路不明的修改版因为你写的脚本会申请无障碍权限和悬浮窗权限这已经是系统敏感权限如果运行在一个被植入后门的“Autojs”里风险非常大。尽量去官方渠道或者GitHub下载。2.3 测试机与番茄小说App的准备自动化最怕的就是环境变化而番茄小说是一个经常更新的App界面结构会变控件ID也会变。我的做法是装好一个稳定版本之后先去应用商店里把“自动更新”关掉。如果你对某个版本的界面特别熟悉可以保留那个版本的安装包作为测试基准。测试机建议用真机不要用模拟器。原因很简单模拟器里的屏幕分辨率和真实手机不同番茄小说对模拟器的兼容性也不稳定容易出现控件信息读取不到的情况。真机上有一类比较容易踩坑的ROM比如部分国产定制系统会默认禁止后台自启动Autojs的无障碍服务跑一会儿就被系统回收导致脚本突然失去响应。碰到这种情况需要去系统设置里把Autojs和番茄小说都设为“允许后台运行”最好还能锁定最近任务。准备工作的最后一步是安装设置好Autojs后先手动打开一次系统“无障碍”开关然后在Autojs里跑一个最简单的脚本比如toast(hello)确认脚本能正常弹窗。这个基础环境通了再继续往下写自动化逻辑。3. 核心脚本设计从打开App到自动翻页3.1 先想清楚整体状态机很多人写Autojs脚本是一股脑从上往下写先打开App然后sleep点一个按钮再sleep……这里一个sleep那里一个sleep脚本跑起来就像猜谜一样。我会建议你先画出整个流程的状态机不需要很复杂但在脑子里要清楚当前处于什么状态。这个项目的流程大致是启动脚本 - 开启无障碍 - 进入番茄小说 - 判断是否在书架页 - 打开目标书籍 - 进入阅读页 - 等待正文加载 - 执行翻页 - 翻到指定页或运行到指定时长 - 退出阅读页 - 回到桌面。每次操作之后脚本都要问自己一个问题我现在是不是真的到达了预期状态而不是盲目sleep。比如我执行click(button)之后不能马上接着翻页而应该先判断阅读页有没有出现如果没出现就等待等超过5秒还没出现就说明点击失败了需要回到上级流程。3.2 用控件查找而不是死坐标为什么我强烈推荐我最早学Autojs时也喜欢用click(x, y)因为简单看到屏幕哪里能点就点哪里。但后来发现不同手机的屏幕尺寸不一样同一个按钮坐标可能差出几十甚至上百像素到朋友手机上脚本就失灵了。Autojs比较优雅的做法是控件查找。它能把界面上的按钮、文本框、列表项解析成对象我们可以通过text()、id()、className()等条件去匹配。比如番茄小说书架页一般会有“书架”这个文本我们可以这样写auto.waitFor() launchApp(番茄免费小说) sleep(2000) var shelfTab text(书架).findOne(3000) if (shelfTab) { shelfTab.click() toast(已进入书架) } else { toast(没有找到书架tab) }注意findOne(timeout)方法是阻塞等待最多等3秒如果找到元素就返回找不到返回null。这种写法的好处是即使小说App启动慢或者页面加载有延迟脚本也能等一等再判断而不是盲目sleep。3.3 翻页逻辑的两种实现方式番茄小说阅读页的翻页方式在App设置里通常有“仿真”、“滑动”、“覆盖”等几种模式。对自动化脚本来说最稳妥的翻页方式是模拟点击阅读区的右侧或执行一次左滑手势具体要看App当前的翻页模式。如果你使用的是“仿真”或“覆盖”模式通常点一下屏幕右侧就到下一页脚本可以这样实现function nextPageByClick() { let w device.width let h device.height click(w * 0.85, h * 0.5) }如果你使用的是“滑动”模式那就需要模拟手指向左滑function nextPageBySwipe() { let w device.width let h device.height swipe(w * 0.8, h * 0.5, w * 0.2, h * 0.5, 300) }你可能会问到底应该用哪种我建议先花两分钟手动打开一本书看正文区域点一下会不会翻页。如果会就用点击法如果点一下只是弹出菜单那就用滑动法。实际项目中最好的做法是两种都写用一个配置项控制这样在不同的翻页模式下都能跑。源码里可以定义一个readerConfig对象里面放上翻页方式、翻页间隔、总阅读时长等参数。3.4 定时与防沉迷设计自动翻页虽然是方便但如果不加限制很容易让手机一直亮屏跑一整天一是费电二是不健康。我在脚本里加了一个“运行时长”和“翻页间隔”参数var config { interval: 8000, // 每8秒翻一次页 maxDuration: 30 * 60 * 1000, // 最多运行30分钟 targetBook: 斗破苍穹 // 示例书名按实际替换 }; var startTime Date.now(); while (Date.now() - startTime config.maxDuration) { if (text(目录).exists() || text(设置).exists()) { // 可能弹出菜单栏点一下正文关闭 click(device.width / 2, device.height / 2); } nextPageByClick(); sleep(config.interval); }这个设计可以用一个while循环控制到时间就退出然后回到桌面。在真实项目中我还会把到期时间通知出来比如用Autojs的notify发送一个本地通知提醒用户“今天的自动阅读已完成”。这里也顺便提醒一句自动化脚本的目的是辅助自己不是替代人去滥用某个App所以使用时要遵守番茄小说的用户协议不要用脚本去刷奖励、刷时长、恶意下载内容。我做的只是正常的“模拟翻页阅读”这是技术演示和学习范畴。3.5 异常弹窗和加载失败怎么处理自动阅读过程中最大的敌人不是代码本身而是界面上的各种不确定性。比如启动App后偶尔弹“青少年模式”阅读过程中弹“继续阅读”的引导断网时显示加载失败这些都会让脚本卡住。我的处理思路是在一个翻页循环里每次翻页前先做一个“弹窗清理”动作。用Autojs的文本选择器去查找那些常见的关闭按钮比如“知道了”、“关闭”、“取消”、“暂不”等找到了就点掉。代码可以封装成一个函数function dismissDialogs() { var closeTexts [知道了, 关闭, 取消, 暂不, 以后再说, 立即关闭]; for (var i 0; i closeTexts.length; i) { var btn text(closeTexts[i]).findOne(200); if (btn) { btn.click(); sleep(500); } } }注意findOne(200)是只等200毫秒找不到就立刻返回null避免每个弹窗文本都等很久。这个方法不能保证100%处理所有弹窗但能覆盖大多数常规提示。如果遇到一个完全没见过的弹窗脚本会在下一次翻页时因为找不到目标控件而退出所以我在主循环里还会加一个“连续失败次数”统计连续失败超过3次就停止并截图方便之后排查。3.6 源码模块化别再把所有代码堆在一个文件里当脚本功能越来越多时单文件模式就很难维护了。我最终把源码整理成这样的结构auto-read-novel/ ├── project.json ├── main.js ├── config.js ├── modules/ │ ├── app.js │ ├── reader.js │ ├── dialog.js │ └── logger.js └── res/ └── icon.pngmain.js只负责启动入口调用app模块打开应用再调用reader模块开始阅读。config.js放所有可变参数。modules/app.js封装启动App、返回桌面、判断当前应用等操作。modules/reader.js负责阅读页的定位、翻页、章节检测。modules/dialog.js专门处理各种弹窗。modules/logger.js负责输出日志到文件。这样拆开之后项目的好处就体现出来了。我想改翻页间隔只需要打开config.js想增加新的弹窗文案只需要改dialog.js想换一个阅读App只需要重写reader.js其他模块基本可以复用。这就是源码工程化的意义。4. 从源码到Apk把脚本变成独立安卓应用的完整路径4.1 为什么要打包成Apk既然脚本在Autojs里能直接跑为什么还要打包成Apk我总结下来有三个原因。第一方便分发。你写了一个自动化脚本想给家里人用但他们手机上不一定装了Autojs就算装了也不会导入脚本、不会开权限。打包成Apk之后他们只需要像安装普通App一样安装然后按提示开启无障碍服务就行。第二保护源码。Autojs的脚本文件本质上是JavaScript文本发给别人就等于公开源码。打包成Apk时可以对脚本做加密或混淆降低被直接复制阅读的门槛。第三运行更稳定。打包后的Apk不再需要Autojs主程序减少一个被系统回收的环节。4.2 Autojs打包Apk的具体步骤我以Autojs Pro为例演示一个稳定的打包流程。首先在Autojs Pro中新建一个项目把前面整理好的源码目录结构放进去。然后检查project.json这是打包配置的关键文件比想象中的还要重要。{ name: 番茄小说自动阅读助手, version: 1.0.0, packageName: com.example.autoread, mainScriptFile: main.js, icon: res/icon.png, build: { targetSdk: 30, minSdk: 21, sign: { keystore: my.keystore, alias: myalias, password: 123456 } } }配置好之后如果用Autojs Pro的图形界面直接点击“打包”按钮它会自动完成JavaScript打包、资源合并、签名生成Apk。整个过程大概几分钟。如果要用命令行Autojs也提供了打包脚本可以在电脑上用Node.js调用方便集成到CI流程里但新手阶段用图形界面就够了。4.3 打包时的权限配置和注意事项Autojs打包出来的Apk和普通App一样也会申请一系列权限。最少需要以下这几个无障碍服务这是Autojs能模拟点击、读取控件的前提悬浮窗权限有些自动化操作需要显示悬浮窗或者做日志弹窗前台服务权限避免脚本在后台运行时被系统杀掉存储权限保存日志、截图等在这些权限里最容易出问题的是“无障碍服务”。很多定制ROM在应用安装后会默认禁止无障碍服务需要用户手动去系统设置里开启。所以打包后的Apk应该在首次启动时做一个引导页告诉用户怎么找到这个开关。我自己的做法是在main.js启动时先执行auto.waitFor()如果无障碍没有开启就用alert提示用户去设置直到用户开启才进入主逻辑。另外签名文件要保管好因为在后续升级版本时必须用同一个签名文件签名否则系统会报“已安装应用签名不一致”而无法覆盖安装。很多人在打包第一次后把keystore文件丢了等到版本升级时只好卸载重装用户数据也就全没了。4.4 安装Apk时常见的几个坑第一次把打包好的Apk传到手机上安装大概率会遇到问题。我这里列几个最常见的现象原因解决办法安装时提示“解析软件包时出现问题”Apk文件损坏或者系统版本过低重新打包检查minSdk是否低于手机系统版本提示“安装失败更新包与现有应用签名不一致”之前安装了同一个包名的其他签名应用卸载旧应用后安装或者严格统一签名安装后打开就闪退脚本代码里用了系统不支持的API或没有开启无障碍时直接调用点击在启动入口加异常捕获增加权限判断图标显示为默认机器人res/icon.png路径写错检查project.json中icon路径图片必须是PNG格式我踩得最狠的一次是把targetSdk设得太高结果在Android 7的旧手机上打开直接崩溃后来改成targetSdk 30就稳定了。建议打包时尽量考虑你目标用户手机的Android版本不要追求最新SDK。5. 常见问题与排查技巧实录5.1 脚本跑一会就卡死在阅读页这是自动化脚本最常见的问题主要原因是某个元素没有出现脚本却在等待它或者不停点击同一个位置导致进入了一个无法预期的页面。排查时我会先把logger打开在关键节点都输出日志比如“进入阅读页”、“翻页完成”、“发现弹窗”这样能定位到卡在哪一步。另一个容易忽略的原因是屏幕常亮。如果手机在脚本运行过程中锁屏了控件查找会全部失效。解决方法是脚本开始时执行device.keepScreenOn()或者用Autojs的device.wakeUp()在休眠后唤醒屏幕。在打包成Apk时最好申请“唤醒锁”权限否则无法保持屏幕常亮。5.2 翻页坐标在部分机型上失效不同手机的屏幕长宽比不一样单纯用百分比坐标虽然能适配大部分情况但有些全面屏手机因为系统手势区占据了底部点击坐标会偏到导航栏上。我的经验是翻页点击点不要放在最底部尽量放在屏幕纵向中央区域比如y h * 0.5同时避免点击到左右边缘。还有一个小技巧用device.width和device.height获取到的尺寸是屏幕物理像素如果番茄小说开启了“深色模式”或“夜间模式”正文背景和控件位置不会变但弹窗文字颜色可能变化这会影响文本选择器匹配吗正常情况下不影响因为text()匹配的是控件文字内容不依赖颜色。但如果某个弹窗文字是图片形式那就无法用文本选择器定位只能借助OCR或者固定坐标这种情况就只能靠截图人眼判断了。5.3 Apk打包后无法打开番茄小说App我遇到过一种情况打包后的Apk已经申请了存储权限和悬浮窗权限但在启动番茄小说时提示“未安装”。后来发现是打包Apk时包名冲突导致系统把两个应用搞混了。解决方法是把packageName设置成一个自定义的、不和其他应用冲突的包名比如com.yourname.autoreader。还有一种情况是Android 10以后对后台启动Activity做了限制如果Autojs脚本是在后台通过定时任务触发直接调launchApp(番茄免费小说)可能被系统拦截。这个时候需要把定时触发改成前台通知或者使用app.startActivity显式指定包名和Activity但这需要确保你能拿到正确的组件信息而且不涉及绕过系统安全限制。5.4 番茄小说更新后脚本失效App更新是自动化项目最大的威胁之一。今天还能找到的“书架”按钮下次更新后可能从text(书架)变成了content-desc(书架)甚至完全换了一个控件。我的应对办法是在config.js里维护一组备选选择器匹配不到第一批就用第二批。var shelfSelectors [ function() { return text(书架).findOne(1000) }, function() { return desc(书架).findOne(1000) }, function() { return id(tab_shelf).findOne(1000) } ];从产品角度说这也是为什么要把源码管理好。因为一旦App更新你需要在半小时内更新脚本并重新打包Apk如果代码是一团乱麻根本做不到快速响应。用Git管理源码每次App更新之后还能对比之前的改动快速定位需要修改的地方。5.5 源码工程备份与Git管理源码是项目最大的财富所以我从一开始就用Git。项目根目录下添加了一个.gitignorenode_modules/ build/ dist/ *.keystore *.jks local.properties签名文件、构建产物都不应该进版本库否则容易泄露密码。每次完成一个稳定版本就打一个tag例如v1.0.0。这样以后哪个版本出了问题可以快速切回去对比。6. 项目复盘与下一步扩展6.1 我从这个项目里学到的三点经验第一自动化脚本的核心不是写代码而是“处理和预期不符的界面状态”。你永远不知道用户会在什么情况下打开你的App也永远不知道App自己的弹窗规则是什么所以脚本必须设计成“每走一步都要确认状态”。第二控件优先坐标兜底。能用text、desc、id定位元素就尽量用实在不行再用坐标。坐标只是一种快速方案不是稳定方案。第三Apk打包不是一个简单的按钮。它涉及包名、签名、targetSdk、权限配置、手机系统兼容性任何一个环节没处理好用户拿到手就是一串“安装失败”。所以一定要在自己的真机上做完整测试再分发给别人。6.2 后续还能怎么扩展这个项目的扩展空间其实很大。你可以给脚本加定时阅读功能比如每天晚上十点自动打开小说阅读半小时也可以把每天的阅读页数记录到本地SQLite里周末生成一个阅读统计还可以用Autojs的邮件或Webhook能力把阅读完成通知推送到自己的服务器。我甚至建议你试试把同一个自动阅读脚本适配到其他阅读App上比如起点、微信读书、掌阅。虽然不同App的控件结构差异很大但你已经有了一套模块化源码只需要替换reader.js里的选择器部分其他逻辑基本能复用。这个过程会是很好的学习素材也会让你对安卓自动化有更深入的理解。对我来说这个项目最大的价值不是让番茄小说“自己看书”而是把一套完整的自动化项目从脚本写到了Apk分发。它让我理解了安卓自动化真正的难点不是写代码而是适配每个不可控的界面状态。如果你也想练手我建议先别急着写大而全的功能做一个5分钟内能跑起来的翻页脚本然后在这个基础上一点一点加状态判断。等你的脚本能在不同手机上稳定运行三天你对Autojs的掌握就已经超过大多数人了。