一名开发者在维护自己的 Android UI Renderer MCP 时发现,原本用于自动渲染 XML/View 界面的本地工具,被 coding agent 进一步扩展到了 Jetpack Compose 场景。该工具无需模拟器或真机,就能让 agent 获得 PNG 截图和可查询的组件结构,从而支持「修改 UI → 渲染 → 检查图像和结构 → 继续修复」的自动化循环。
从 XML 到 Compose:入口差异让任务天然变复杂
这个 Android UI Renderer MCP 最初面向传统 Android UI:加载 XML 布局、注入测试数据、执行 measure/layout、绘制图像,并输出 View 树。流程清晰、入口明确。
但 Jetpack Compose 没有等价的布局文件入口。一个界面往往只是一个 Kotlin 函数,例如 @Composable fun BookItem(book: Book, selected: Boolean, onClick: () -> Unit)。要渲染它,工具需要理解函数签名,构造枚举、可空类型、数据类、集合、回调等参数,还要处理主题环境,并在可绘制环境中调用该 composable。
作者承认,这类任务并不是无法解决,而是研究成本可能高于功能收益,因此自己通常不会主动开工。这一次,他把探索和实现交给了 coding agent。
不要求项目手写适配层,agent 自动生成临时探针
最稳妥的方案是让每个 Compose 项目自行提供渲染入口:由开发者写好状态构造、依赖注入和调用逻辑,MCP 只负责调用。但这会破坏工具的核心目标——让 agent 无需人工准备就能直接操作现有项目。
作者给出的约束是:MCP 必须自己适配 Kotlin/Compose,项目不需要编写特殊渲染入口。
最终,agent 为 MCP 增加了新工具。调用时不再传入 XML 布局,而是传入顶层 composable 的完整函数名和 JSON 参数,例如:
{
"function": "io.github.example.ComposeBookItem",
"arguments": {
"selected": true,
"book": {
"title": "Designing Data-Intensive Applications",
"status": "AVAILABLE"
}
},
"widthPx": 1280,
"heightPx": 800,
"densityDpi": 240
}
渲染器随后会自行生成临时探针代码,放在 .android-ui-renderer 目录中,不修改项目源码。素材提到,该方案支持可空值、枚举、数据类、可变属性、List 和 Set,因此 agent 不需要在应用内预先编写 renderForMcp() 之类的函数。
为验证效果,作者让 agent 在一个开源 Jetpack Compose 示例项目中完成端到端测试。仓库中现在包含两个等价应用:基于 XML/View 的 sample/ 和基于 Compose 的 sample-compose/。两者共享同一套图书库和 7 个场景:书籍行、字体缩放 1.3 的长标题、列表、主从布局、全屏、加载状态,以及俄语深色主题。
这些对比并非用于证明 XML 与 Compose 像素级一致,而是展示同一渲染器可以在相同画布尺寸 1920×1200 px、240 dpi 条件下,通过两种不同机制复现等价状态。
真正的难点不是截图,而是可查询的语义树
在真实 Compose 组件测试中,agent 很快生成了正常的 PNG。如果只看截图工具标准,任务已经完成。但树序列化失败了:某个 Compose semantics 节点缺少 className,而渲染器原有协议要求每个节点都必须有该字段。
这暴露了 agent 工具与普通截图工具的差别。agent 不能只靠图片判断,还需要继续追问:元素在哪里、边界是多少、文本是什么、是否可点击、是否选中、是否勾选、内部包含什么。
XML 时代这些问题由 View 树回答;Compose 场景下,渲染器现在返回 semantics 树,包含 bounds、text、contentDescription、enabled、clickable、selected、checked 以及子节点。agent 为缺少常规 className 的 semantics 节点增加了回退处理,重新构建渲染器并再次执行请求。
素材给出的 view-tree.json 片段显示,一个按钮节点具有可点击属性、边界坐标和文本为「Reserve」的子节点:
{
"id": "semantics-33",
"className": "androidx.compose.ui.semantics.SemanticsNode",
"clickable": true,
"bounds": {
"left": 650,
"top": 500,
"right": 799,
"bottom": 560
},
"children": [{
"id": "semantics-35",
"text": "Reserve",
"bounds": {
"left": 686,
"top": 515,
"right": 763,
"bottom": 545
}
}]
}
只有完成这一步后,工具才算满足完整契约:同时返回图像和结构,而不是让 agent 自行目测截图。
工具链扩展的边界:agent 也会撞上构建系统约束
素材还提到,渲染器会通过 Gradle init script 临时注入测试依赖。在新的 Compose 演示项目中,这种方式遇到了严格的 repositoriesMode 配置:项目的 Gradle 设置禁止以该方式添加 Robolectric 依赖。
这段未完整展开的细节说明,coding agent 虽然可以承担研究、生成代码和修复协议问题,但仍会面对真实工程环境中的构建规则与依赖限制。
从结果看,这个案例的价值不只是给一个渲染器增加了 Compose 支持。它展示了 coding agent 在工具链建设中的另一种角色:不是简单补全代码,而是在开发者设定约束后,自主完成方案探索、临时探针生成、协议适配和结构化输出修复。对于 AI 编程工具而言,下一步竞争点可能不只是生成业务代码,而是能否持续扩展 IDE、渲染器、测试和调试工具之间的自动化闭环。
原创文章,作者:点点,如若转载,请注明出处:https://www.dian8dian.com/coding-agent-rang-android-xuan-ran-gong-ju-lian-zi-ji-zhang