1. UiSelector 找不到控件到底卡在哪
AutoJs 里用 UiSelector 选控件,最常见的翻车现场就是findOne()返回null,然后脚本在.click()那一行直接抛异常,日志里一片红。你盯着屏幕看控件明明就在那儿,代码却死活找不到,这种时候最容易怀疑人生。其实问题基本都出在「选择器条件写得不够精确」或者「条件之间互相打架」上。UiSelector 支持 text、desc、className、id、depth、indexInParent 等一堆属性,你可以单独用,也可以链式组合,但组合的时候如果某个属性在当前界面层级里对不上,整个选择器就会返回空。比如你写id("jha").className("android.widget.TextView").depth(22),只要 depth 差一层,前面 id 和 className 匹配得再准也没用。这篇就按排障视角,把 AutoJs 的 UiSelector 报错拆开讲,同时把 Claude Code 的 Base URL 改到 TaoToken 通道,让模型帮你逐条对照控件属性和层级,把修正后的选择器条件拿回 AutoJs 跑通。
2. 先把 Claude Code 接到 TaoToken 通道
排障这件事本身不复杂,复杂的是你得有个能稳定对话、能贴代码、能看报错的模型入口。我自己的做法是打开 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 创建 Key,然后把 Claude Code 的 Base URL 填成https://taotoken.net/api。这里要强调一下,TaoToken 在排障环节只负责提供可用的 Key 和 Base URL,它不参与控件查找,也不碰你的 AutoJs 脚本,真正干活的是走这个通道的 Claude Code。你配通之后,把 AutoJs 的选择器代码和完整报错贴过去,让它对照 UiSelector 的属性和 depth 逐条排查,最后拿到修正后的条件再回 AutoJs 运行。API 地址是https://taotoken.net/api,注意这个不带任何多余参数,直接填进 Claude Code 的配置里就行。
2.1 创建 Key 和确认通道
进入控制台后先建一个 API Key,复制出来存好。如果你还没配过 Claude Code,可以先去接入文档看一眼环境变量怎么写。核心就是让 Claude Code 知道请求往哪儿发:
export ANTHROPIC_BASE_URL="https://taotoken.net/api" export ANTHROPIC_API_KEY="你刚创建的Key"Windows 下用set或者直接在 Claude Code 的配置文件里写。配完之后不要急着贴 AutoJs 代码,先确认通道是通的。
3. 可复制的 Claude Code 配置与 AutoJs 选择器对照
3.1 Claude Code 侧配置
Claude Code 的配置方式取决于你用的是命令行还是编辑器插件,但 Base URL 和 Key 这两个东西是绕不开的。下面是一个通用的环境变量写法,你可以直接抄:
# Linux / macOS export ANTHROPIC_BASE_URL="https://taotoken.net/api" export ANTHROPIC_API_KEY="sk-你的Key" # 验证是否生效 echo $ANTHROPIC_BASE_URL如果你用的是 Windows PowerShell:
$env:ANTHROPIC_BASE_URL="https://taotoken.net/api" $env:ANTHROPIC_API_KEY="sk-你的Key"配好之后启动 Claude Code,随便问一句「你能看到我吗」,有正常回复就说明通道通了。这一步别省,很多人后面贴代码贴半天没反应,其实是 Base URL 根本没生效。
3.2 AutoJs 侧的选择器写法对照
回到 AutoJs 本身。UiSelector 的常用属性我列个表,你贴给 Claude Code 的时候也方便它对照:
| 属性 | 含义 | 常见坑 |
|---|---|---|
| text | 控件文本 | 文本带空格或动态变化时匹配不到 |
| desc | 内容描述 | 很多控件 desc 为空 |
| className | 类名 | 同类控件多,单独用容易选错 |
| id | 资源 id | 不同版本 app 可能变 |
| depth | 层级深度 | 差一层就 null |
| indexInParent | 父节点内索引 | 列表滚动后索引会变 |
一个典型的报错场景是这样的:
// 你以为能点到,结果 findOne() 返回 null id("jha").findOne().click();报错信息大概是Cannot read property 'click' of null。这时候你把这段代码和报错一起贴给走 TaoToken 通道的 Claude Code,让它帮你分析。它通常会建议你先打印控件树看看:
// 先别急着点,把当前界面的控件信息打出来 console.show(); var selector = id("jha"); var node = selector.findOne(); if (node) { console.log("找到了: " + node.text() + " depth=" + node.depth()); } else { console.log("没找到,检查 id 是否正确"); // 退而求其次,用 text 或 desc 试试 var alt = text("搜索").findOne(); if (alt) { console.log("用 text 找到了,depth=" + alt.depth()); } }3.3 把报错和代码一起贴过去
贴给 Claude Code 的时候,信息越全越好。我一般会按这个格式整理:
AutoJs 版本:4.11 目标 app:微信 报错:Cannot read property 'click' of null 代码: id("jha").className("android.widget.TextView").depth(22).findOne().click(); 已尝试:单独用 id 能找到,加上 className 和 depth 就 null这样模型就能快速定位到是 depth 写错了还是 className 对不上。实测下来,大部分findOne()返回 null 的情况,都是 depth 或者 className 多写了一个条件导致的。
4. 验证请求与成功结果
通道配好之后,怎么确认 Claude Code 真的在帮你干活?最简单的办法是让它复述你的选择器条件,然后给出修正建议。比如你贴过去之后,它可能会回你:
你的 depth(22) 在当前界面层级里对应的是父容器,不是目标控件。 建议改成 depth(23),或者干脆去掉 depth,用 id + text 组合: id("jha").text("搜索").findOne()你拿到这个建议后,回 AutoJs 改成:
// 修正后的选择器 var searchBtn = id("jha").text("搜索").findOne(); if (searchBtn) { searchBtn.click(); toast("点击成功"); } else { toast("还是没找到,继续排查"); }跑一下,如果 toast 显示「点击成功」,说明修正生效了。如果还是 null,就把新的报错再贴回去,让模型继续缩小范围。这个来回过程一般两三轮就能收敛。
4.1 用控件集合批量验证
有时候单个控件能找到,但批量操作时又出问题。比如原文里那个遍历通讯录的例子:
id("kbq").className("android.widget.TextView").depth(22).find().forEach(element => { if (element.text() != "微信团队") { click(element.text()); sleep(3000); id("com.tencent.mm:id/actionbar_up_indicator").depth(9).findOne().click(); sleep(1500); } });这段代码里find()返回的是控件集合,如果集合为空,forEach不会报错但也不会执行任何操作,你会觉得脚本「卡住了」。排查方法是先打印集合长度:
var collection = id("kbq").className("android.widget.TextView").depth(22).find(); console.log("找到控件数量: " + collection.length); collection.forEach(element => { console.log("文本: " + element.text() + " depth: " + element.depth()); });把这段日志贴给 Claude Code,它就能告诉你 depth 是不是写错了,或者 className 是不是应该换成android.widget.LinearLayout。
5. 本篇常见错排查
5.1 findOne() 返回 null 的几种典型原因
第一种是条件太严。你同时写了 id、className、depth、text 四个条件,只要有一个对不上就全盘皆输。建议先用最宽的条件找到,再逐步加条件缩小范围。第二种是 depth 理解错了。depth 是从根节点往下数的层级,不同 app 版本、不同界面可能差一层。第三种是控件还没加载出来。你sleep时间不够,界面还在渲染,选择器自然找不到。加个waitFor()或者延长sleep试试。
5.2 选择器语法写错
AutoJs 的选择器是链式调用,顺序有讲究。id("jha").className("android.widget.TextView")和className("android.widget.TextView").id("jha")效果一样,但如果你写成id("jha").findOne().className("android.widget.TextView")就错了,因为findOne()返回的是 UiObject,不是 UiSelector,后面不能再链选择器方法。这个错误很隐蔽,报错信息也不直观,贴给 Claude Code 的时候记得把完整调用链带上。
5.3 通道侧的问题
如果你贴了代码半天没回复,先检查 Claude Code 的 Base URL 是不是https://taotoken.net/api,Key 有没有过期。可以先用模型对话页面测一下通道是否正常。排障这件事,通道不通后面全白搭。
6. 配通之后怎么继续用
把 Claude Code 接到 TaoToken 通道之后,你得到的不仅是一次选择器修正,而是一个能反复用的排障入口。下次再遇到findOne()返回 null,直接把控件树日志和报错贴过去,让它帮你逐条对照属性。如果你长期写 AutoJs 脚本,可以考虑用 Coding Plan 把这种排查流程固化下来。需要看模型能力的话,模型对话页面可以直接试。Key 和 Base URL 都在控制台和接入文档里,配一次后面就省事了。实测下来,UiSelector 的问题八成都是条件写太死,让模型帮你做减法,比你自己盯着屏幕猜快得多。