Android 开发者的 AppFunctions:Android 移动应用的 MCP 时刻
Android 开源社区最抢眼的不是什么聊天机器人 UI,而是 AppFunctions——Android 针对每个移动团队都可能面临的问题给出的早期答案:你的应用应该允许智能体(Agent)做些什么?
有意思的点不在于 Android 又搞了个 AI API,而在于 Android 应用可能很快需要把自己的操作描述成安全、类型化的工具。
简单来说:Android 正在构建一个 OS 层面的机制,让应用可以暴露一些小巧、类型化的操作,让智能体能够在设备上发现并运行它们。Android AppFunctions 概览 说得很明白,AppFunctions 让应用可以向“内置于 Android 操作系统中的注册表”提供“服务、数据和操作”,这样用户就能通过智能体和系统级交互完成任务。同一页面还将 AppFunctions 比作模型上下文协议(Model Context Protocol)中的工具,只不过它用的是 Android 原生的钩子,是在本地执行,而不是只通过云端的工具来跑。
正因如此,我觉得这就像 Android 移动应用的“MCP 时刻”。不是说 API 完全一样,而是这个模式似曾相识:智能体选一个工具,填好结构化参数,然后让这个工具的所有者去执行工作。移动端的不同之处在于,这个工具可以是属于应用的代码,运行在本地状态、账户上下文、权限、离线行为和用户信任边界附近。
时间点也很重要。Android 16 已经上线,AppFunctions 的文档写着这个 API 可在运行 Android 16 或更高版本的设备上使用。目前还处于早期阶段的是更广阔的智能体故事:截至2026年5月,Gemini 集成还在与受信任的测试者进行私人预览,AppFunctions 本身也仍被标注为实验性预览。所以,操作系统层面的要求已经就绪,但 API 的形状、权限和助手行为仍可能变化。Google 还发布了 AppFunctions 示例应用 和 AppFunctions 智能体技能,这让人觉得它瞄准的是 Android 开发者,而不仅仅是平台团队。
如果你在开发 Android 应用,这事关重大,因为智能体操作正在成为一个应用架构问题,而不仅仅是一个助手功能。难点不在于加个注解。难点在于,你得决定你产品的哪些部分应该变得可调用,哪些部分应该只生成草稿,哪些部分永远不应该通过智能体来执行。
我的看法:不要只把 AppFunctions 当成“给你的应用加个 AI”。把它们当作一条新的产品边界。问题不仅仅在于智能体能不能调用你的应用,更在于你的应用应该安全地暴露哪些操作。
这样,移动 AI 的故事就从:
助手可以谈论你的应用。
变成了:
助手可以通过系统拥有的边界,要求你的应用做一个具体的事情。
这就把助手的角色从“导航”推进到了“执行”,而移动端产品设计的难度也正在于此。
为什么 Android 开发者应该关注
简短的回答是:现在就开始研究它,因为 Android 16 已经上线,但先别把它当成一个广泛的生产级智能体渠道。
最明显的信号是,官方文档不光是在描述一个助手功能,而是在描述一个开发者的集成面:
这让你可以将应用的能力作为可编排的“工具”暴露出来,授权的应用(调用者)可以发现并执行这些工具,以实现用户的意图。
这句话道出了全貌。Android 应用可以暴露能力作为工具,但调用者仍然需要平台权限。这可不只是一句关于 Gemini 的营销口号。
Jetpack 的发布说明则让开发者角度更加清晰。库的描述说,AppFunctions 让应用能够与 AI 助手共享功能和数据,“使它们能够直接在设备上发现并执行任务,以满足用户请求。”这不仅仅是 API 的管道工程。最近的 Alpha 版本说明覆盖了 Android 开发者实际需要的东西:针对 Int 和 String 参数的约束、面向 LLM 的自然语言描述、智能体应用内部的用户可见描述、返回文件的 URI 授权,以及通过 AppFunctionTestRule 的 Robolectric 支持。
所以,这项工作不是“加个 AI”,而是更具体的:
- 识别出哪些应用工作流应该是可调用的
- 将输入和输出建模成类型化函数
- 写一些足够好的描述,让智能体能正确选择
- 决定哪些操作需要确认
- 在真正的智能体依赖它之前,测试应用函数的边界
Google 自己的 AppFunctions 示例仓库 也印证了这点。示例是一个聊天应用,它提供了发送消息、搜索联系人和发起通话的 AppFunctions。这些可不是玩具示例。它们是真实的产品操作,一旦执行错误,后果会很严重。
一个实用的 AppFunctions 检查清单
如果我要为一个真实的应用评估 AppFunctions,在大量写代码之前,我会先对照这个清单检查一遍:
- 列出那些反复出现的、且已经映射到明确应用操作的用户意图。
- 从可逆或可审查的操作开始,而不是高风险的提交。
- 用狭窄的类型化输入和明确的结果类型来建模每个函数。
- 写出的描述能帮助智能体选择正确的函数,而不是营销文案。
- 对风险较高的工作流,将“准备”和“提交”步骤分开。
- 当操作影响到另一个人、涉及金钱、安全或公开内容时,要求用户确认。
- 明确要求指定账户、工作区和租户。
- 在智能体依赖函数之前,就决定好离线行为和同步行为。
- 围绕函数边界添加测试,包括错误的调用者、缺少参数和状态模糊的情况。
- 用用户和支持团队都能理解的方式记录智能体触发的操作。
这个清单是故意搞得有些乏味的。AppFunctions 之所以有趣,是因为它让应用的能力变得可调用,但信任来自于小而简单、可预测、建模良好的操作。
为什么它吸引了我的注意
很多移动设备上的 AI 功能,感觉仍然像是在真实应用上糊了一层东西。它们能摘要、重写或搜索,但一到执行环节就卡壳。助手让用户打开应用,应用自己再建个助手界面,或者后台工具调用一个没有本地应用上下文的 API。
AppFunctions 则给出了一种更清晰的模式:用户用自然语言表达,而应用则继续拥有对操作的控制权。
AOSP 告诉我们的
公开的 Android 文档给出了高层级的介绍,但 AOSP 让架构更容易理解。AppFunctionManager 指出,大多数开发者应该使用 AppFunctions SDK 来获得类型安全的模式、参数类和返回值。在内部,这些 SDK 类型会变成 ExecuteAppFunctionRequest 参数和 ExecuteAppFunctionResponse 结果文档。
核心流程很简单:
- 应用定义 AppFunctions 和静态元数据。
- 调用者通过元数据发现可用的函数。
- 调用者构建一个
ExecuteAppFunctionRequest。 - Android 通过
AppFunctionManager来路由调用。 - 应用的
AppFunctionService执行这个操作。
AOSP 的 Javadoc 示例是一个笔记应用。助手试图执行“把 XYZ 保存到我的笔记里”,它找到一个笔记添加函数,构建一个请求,然后执行它。关键不在于笔记应用本身,而在于这个抽象层级:助手不是在截屏抓取 UI,而是在调用一个声明过的应用能力。
服务端部分在 AppFunctionService 里。它是一个绑定服务,受到 android.permission.BIND_APP_FUNCTION_SERVICE 保护,AOSP 将其定义为签名权限。随便一个调用者是不能直接绑定到应用函数服务的。系统会坐在中间当裁判。
一图胜千言:架构概览
从高层来看,我认为 AppFunctions 有四个层次:
用户意图 "在我的购物清单上添加燕麦奶" 智能体或助手 选择一个函数并填充参数 Android AppFunction 管理器 检查调用者、元数据、启用状态和用户边界 目标应用服务 运行应用自己的代码,操作本地应用状态
这跟很多人已经知道的 LLM 系统中的工具调用模式很接近,但有一点移动端的区别:这里的工具是本地应用代码,而不仅仅是远程服务器。
AppFunctions 概览 直接做了同样的比较。它把 AppFunctions 称为模型上下文协议中工具的移动端等价物,但也指出标准的 MCP 是平台无关、通常基于云端的,而 AppFunctions 是 Android 操作系统层面的钩子,在本地执行。
如果操作依赖于本地认证、应用设置、离线状态、最近的本地缓存、账户选择或设备拥有的权限,那么在应用内部执行通常比绕道云端工具要干净得多。
一个小例子
想象一个杂货应用,它暴露了一个添加商品的函数。
这个属于应用的函数用 Jetpack 库的形式可能会大致长这样:
classGroceryFunctions( private val repository: GroceryRepository, ) { @AppFunction suspend fun addGroceryItem( itemName: String, quantity: Int =1, listName: String ="Default", ): AddGroceryItemResult { val item = repository.addItem( listName = listName, name = itemName, quantity = quantity, source ="app_function", ) return AddGroceryItemResult( itemId = item.id, visibleName = item.name, listName = listName, ) } }
这是伪代码,不是一个完整的、可以编译的示例。一个真实的应用还会加上 AppFunctionContext、可序列化的参数和结果类型、更好的描述、约束、错误处理和测试。Jetpack AppFunctions 参考 提到,编译器会生成一个 XML 文件来描述 @AppFunction 注解的函数的签名,并提供基础设施来通过 android.app.appfunctions.AppFunctionService 暴露它们。
生成的这个元数据才是最有意思的部分。助手不需要知道杂货应用的内部 UI。它只需要知道存在一个函数、需要哪些参数、以及会返回什么结果。
用户可以说:
在我的 Costco 清单上加两盒燕麦奶。
助手会把它映射成:
{
"function": "addGroceryItem",
"parameters": {
"itemName": "oat milk",
"quantity": 2,
"listName": "Costco"
}
}
应用仍然拥有最终的行为控制权。它可以决定哪个账户是激活的、清单是否存在、同步应该如何工作、撤销应该如何工作、以及下次用户打开应用时应该显示什么。
这就是我喜欢的地方。助手负责处理意图。应用则继续拥有产品逻辑。
为什么这不仅仅是深度链接
Android 已经有意图(Intent)、应用链接(App Link)、快捷方式(Shortcut)、微件(Widget)、切片(Slice)和无障碍服务了。所以有理由问:为什么还要再加一个机制?
AppFunctions 解决的是另一个问题。
深度链接擅长导航。它们回答的是:“这个用户应该去哪里?”
AppFunctions 是关于类型化执行的。它们回答的是:“应该执行哪个属于应用的操作,输入什么结构化参数,以及在什么调用者权限下执行?”
对于 AI 智能体来说,这个区别很重要。自然语言必须变成可执行的东西。如果唯一的目标是一个 URL 或一个屏幕,助手仍然需要依赖应用的 UI 来完成整个任务。如果目标是一个带有元数据的类型化函数,助手就能选择更精确的操作。
一个有用的思维模型:
- 意图或应用链接: 把用户带到正确的表面。
- 快捷方式: 暴露一个常见的用户操作。
- AppFunction: 暴露一个类型化的操作,智能体可以发现、填充并执行它。
这些机制可以协同工作。一个安全的 AppFunction 可能直接执行操作并返回结果。一个风险较高的 AppFunction 可能创建一个草稿,并用深度链接把用户带去确认界面。
安全边界才是产品
这个系统最重要的部分不是那个注解,而是围绕执行的安全边界。
AOSP 为这个准备了单独的权限。在 AndroidManifest.xml 中,EXECUTE_APP_FUNCTIONS_TRUSTED 是为那些能够代表用户行事、并具有系统隐私保证的受信任应用定义的。EXECUTE_APP_FUNCTIONS 则被记录为一个系统 API 权限,供预装或具有 ASSISTANT 角色的系统应用使用。
这告诉我,Android 并没有把这事当作“任何应用都可以调用其他应用的私有工具。”它更接近于一个有中介的智能体边界。
这个边界很重要,因为智能体操作的风险特征与普通导航不同。一个深度链接可以打开一个编辑界面。一个 AppFunction 可能会直接发送消息。一个快捷方式可以启动一个任务。一个 AppFunction 可能会用从对话中推断出的参数来完成它。
所以,应用需要决定哪些函数应该立即执行、需要确认、只暴露读取权限、返回一个草稿,还是生成一个审计轨迹。平台会为执行做中介,但产品团队仍然需要为他们领域内的确认、撤销和信任体验负责。
这正是良好的应用设计比花哨的 AI 管道工程更重要的时候。
一个更好的例子:消息草稿
一个消息应用可以暴露两个不同的函数:
@AppFunction suspend fun createDraftMessage( recipient: ContactRef, body: String, ): DraftMessageResult @AppFunction suspend fun sendMessage( draftId: String, ): SendMessageResult
第一个函数风险较低。它只是创建一个草稿。第二个函数风险较高。它会发送消息。
助手可以帮助用户说:
告诉 Alex 我会晚到10分钟,但语气要客气点。
更安全的流程是:
- 助手调用
createDraftMessage。 - 应用返回一个草稿 ID 和预览文本。
- 应用或助手请求用户确认。
- 只有到那时,
sendMessage才会运行。
这种拆分给了产品一个清晰的安全策略边界。创建草稿是智能体友好型的。发送消息则需要用户确认。
这应该是 AppFunctions 鼓励的设计:不是“让 AI 做所有事”,而是“让正确的应用操作在正确的护栏下变得可调用。”
哪些地方可能会变得棘手
我喜欢这个方向,但难点也是实实在在的。
- 发现质量很重要。 如果函数的名字含糊、描述很弱,智能体就会选错。Jetpack 的发布说明已经提到了面向 LLM 的自然语言描述,这说明元数据质量是设计的一部分,而不是事后才想到的。
- 确认体验会很困难。 “添加一个商品”和“转账”不应该感觉一样。
- 账户、离线和策略行为仍然属于应用。 工作区、租户、同步队列、托管设备和工作资料都需要显式处理。OS 的边界并没有取消产品层面的决策。
- 私人预览意味着公开的故事还没讲完。 Android 16 已经上线,但 Gemini 集成仍在与受信任的测试者进行私人预览。开发者可以准备,但这还没有到每个已安装的助手都能广泛生产访问的地步。
我会先暴露什么
对于大多数 Android 开发者,我会建议密切关注并轻量原型实践。现在还不到围绕它重构应用的时候。Android 16 已经上线,但公开文档仍说 Gemini 集成在私人预览中,依赖也还是 Alpha 阶段。
不过,如果应用有明确、重复、属于用户自己的操作(笔记、待办事项、消息、购物清单、预约、媒体搜索或企业状态更新),我会花一天时间来梳理一下真实应用的工作流。
给 Android 开发者的要点很简单:AppFunctions 让你的核心工作流对系统变得可理解。如果智能体界面在 Android 上发展起来,那么拥有干净、类型化、描述良好的函数的应用,会比那些只暴露屏幕的应用更容易被智能体使用。
如果今天要给一个真实应用添加 AppFunctions,我会从那些无聊的、可逆的操作开始:
- 创建一条笔记草稿
- 添加一个待办事项
- 搜索本地应用内容
- 在应用内创建一个类似日历的占位
- 摘要一条属于应用的记录
- 附加一个标签
- 准备一个可分享的草稿
我会避免一开始就触碰不可逆的操作:
- 转账
- 删除数据
- 给另一个人发消息
- 更改安全设置
- 发布公开内容
- 邀请团队成员
对于高风险操作,我会把 API 拆成两步:准备,然后确认。这就是同一个检查清单的产品版本:让智能体有用,但把最终的信任边界保持可见。
@AppFunction suspend fun prepareRefund( orderId: String, reason: String, ): RefundDraft @AppFunction suspend fun submitRefund( refundDraftId: String, ): RefundResult
这种两步的形状没那么神奇,但信任起来要容易得多。
结论
AppFunctions 之所以有趣,是因为它让 AI 集成感觉再次变成了一个移动架构问题。
那些棘手的问题我们都熟悉:数据源、活跃账户、离线行为、确认、局部失败、用户反馈和审计日志。智能体并不会消除这些问题。它只会让它们变得更加显眼。
在这里成功的应用,不会是 AI 功能最多的那些。它们会是那些在智能体界面在 Android 上成为常态之前,就以正确的护栏暴露了正确操作的应用。
延伸阅读
有用的来源:
- Android AppFunctions 概览:产品模型,Android 16 可用性,与 MCP 的比较,Gemini 私人预览说明,示例和智能体技能链接。
- 向你的应用添加 AppFunctions API:设置流程,编译 SDK 要求,KSP 设置和 Jetpack 依赖。
- Jetpack AppFunctions 发布说明:Alpha 历史,参数约束,LLM 描述,URI 授权和测试辅助工具。
- Android AppFunctions 示例:官方示例仓库,包含一个聊天应用示例。
- AOSP
AppFunctionManager:平台 API 形状和笔记助手示例。 - AOSP 应用函数权限:绑定和执行权限。
如果这篇文章对你有用,欢迎 请我喝杯咖啡 ☕。如果你有问题、指正,或者有产品想让我下次分析,请留言评论。
原文链接:https://seankim.dev/blog/appfunctions-for-android-developers-androids-mcp-moment-for-mobile-apps/
夜雨聆风