Android 开发者的 AI 时代新武器:AppFunctions 实战指南
技术定位:AppFunctions 是 Android 原生的 Model Context Protocol(MCP)等价物——采用相同的理念,但工具位于应用内部,在设备端本地运行。
一、 目标
核心目标:建立单一的、OS 级别的、与 Agent 无关的接口,使应用只需声明一次其能力,任何平台信任的调用者均可发现并执行。
前置要求:
- compileSdk = 37
- targetSdk = 36 或更高
- 运行 Android 16 或更高版本的设备
二、技术背景
2.1 现有方案的局限性
在 AppFunctions 出现之前,应用与 AI 助手的交互主要存在以下模式:
上述方案的共同特征是助手专属且不具备可移植性。第三方应用需要为每个助手单独实现集成,增加了开发和维护成本。
2.2 Model Context Protocol(MCP)简介
MCP 是由 Anthropic 于 2024 年底发布的开放标准,现已被多家 AI 产品采用。其核心模型如下:
MCP 的挑战:传统 MCP 服务器运行于应用外部,缺乏对应用状态的特权访问。在移动端,通过 API 暴露应用内部状态或在应用进程内运行 MCP 服务器均存在实际困难。
AppFunctions 的设计目标:填补上述空白,提供设备端的 MCP 等价实现。
三、AppFunctions 架构设计
3.1 核心特性
AppFunctions 具备以下三项核心特性:
A. 本地执行
- 无网络往返,无需维护服务器
- Agent 直接调用应用并读取当前状态
B. OS 级索引
- 无需运行时注册
- 注解处理器生成 schema 至 APK(assets/app_function_v2.xml)
- OS 在安装时读取并维护注册表
C. 文档即契约
- @AppFunction(isDescribedByKDoc = true) 将 KDoc 编码至元数据
- 文档质量直接影响 Agent 的调用决策
3.2 架构层次
四、实现指南
4.1 依赖配置
步骤 1:版本目录配置(gradle/libs.versions.toml)
[versions]
appfunctions = "1.0.0-alpha09"
[libraries]
androidx-appfunctions = { module = "androidx.appfunctions:appfunctions", version.ref = "appfunctions" }
androidx-appfunctions-service = { module = "androidx.appfunctions:appfunctions-service", version.ref = "appfunctions" }
androidx-appfunctions-compiler = { module = "androidx.appfunctions:appfunctions-compiler", version.ref = "appfunctions" }
步骤 2:模块构建配置(app/build.gradle.kts)
ksp {
arg("appfunctions:aggregateAppFunctions", "true")
}
dependencies {
implementation(libs.androidx.appfunctions)
implementation(libs.androidx.appfunctions.service)
ksp(libs.androidx.appfunctions.compiler)
}
配置说明:
- ksp(libs.androidx.appfunctions.compiler) 注册 KSP 处理器
- aggregateAppFunctions 标志聚合项目内所有 @AppFunction 至统一 schema
- 多模块项目中,仅应用模块设置此标志,库模块仅需编译器依赖
4.2 Manifest 声明
OS 在安装时需获取两项信息:AppFunction 元数据位置、功能执行时的服务绑定目标。
<application ...>
<property
android:name="android.app.appfunctions.app_metadata"
android:resource="___HL_0___/app_metadata" />
<service
android:name="androidx.appfunctions.service.PlatformAppFunctionService"
android:permission="android.permission.BIND_APP_FUNCTION_SERVICE"
android:exported="true"
tools:targetApi="36">
<intent-filter>
<action android:name="android.app.appfunctions.AppFunctionService" />
</intent-filter>
</service>
</application>
权限说明:
- BIND_APP_FUNCTION_SERVICE:仅限平台绑定
- EXECUTE_APP_FUNCTIONS:调用者权限,非应用所需
4.3 应用元数据配置
创建 res/xml/app_metadata.xml:
<?xml version="1.0" encoding="utf-8"?>
<AppFunctionAppMetadata
xmlns:appfn="http://schemas.android.com/apk/androidx.appfunctions"
appfn:description="本应用支持用户查看和管理笑话内容,包括标记收藏和清除收藏列表。" />
属性说明:
appfn:description | ||
appfn:displayDescription |
复杂场景配置示例:
<AppFunctionAppMetadata
xmlns:appfn="http://schemas.android.com/apk/androidx.appfunctions"
appfn:description="本应用支持用户查看和管理笑话及收藏。
操作模式:
- 调用 'getFavorites' 获取有效笑话 ID 后再调用 'setFavorite'
约束条件:
- 'clearFavorites' 操作不可逆,调用前需确认用户意图"
appfn:displayDescription="___HL_0___/app_function_user_description" />
4.4 数据层扩展
AppFunction 应作为普通业务逻辑的薄壳,而非重复实现。首先通过现有架构层添加业务能力:
DAO 层:
@Query("UPDATE joke SET isFavorite = 0")
suspendfun clearAllFavorites()
数据源层:
interface LocalJokesDataSource {
suspendfun clearAllFavorites()
}
class LocalJokesDataSourceImpl : LocalJokesDataSource {
overridesuspendfun clearAllFavorites() {
database.clearAllFavorites()
}
}
仓库层:
interface JokesRepository {
suspendfun clearAllFavorites(): Result<Unit>
}
class JokesRepositoryImpl : JokesRepository {
overridesuspendfun clearAllFavorites(): Result<Unit> {
return safeCall {
localDataSource.clearAllFavorites()
}
}
}
4.5 AppFunction 实现
基础功能示例:
class JokesAppFunctions {
@AppFunction(isDescribedByKDoc = true)
suspendfun clearFavorites(context: AppFunctionContext): String {
val repository = AppModule.jokesRepository
val result = repository.clearAllFavorites()
returnif (result.isSuccess) {
"所有收藏笑话已成功清除"
} else {
"清除收藏失败:${result.exceptionOrNull()?.message}"
}
}
}
实现要点:
isDescribedByKDoc = true | |
AppFunctionContext | |
String 或 @AppFunctionSerializable 注解的数据类 | |
suspend |
4.6 结构化数据与类型化参数
可序列化数据类:
@AppFunctionSerializable(isDescribedByKDoc = true)
dataclass AppFunctionJoke(
val id: Int,
val question: String,
val answer: String,
)
fun Joke.toAppFunctionJoke(): AppFunctionJoke = AppFunctionJoke(
id = id, question = question, answer = answer,
)
带参数功能:
@AppFunction(isDescribedByKDoc = true)
suspendfun setFavorite(
context: AppFunctionContext,
jokeId: Int,
isFavorite: Boolean,
): String = withContext(Dispatchers.IO) {
val joke = AppModule.jokesRepository.getJokeById(jokeId).getOrNull()
?: throw AppFunctionElementNotFoundException("未找到 ID 为 $jokeId 的笑话")
val result = AppModule.jokesRepository.setFavorite(jokeId, isFavorite)
if (result.isSuccess) {
if (isFavorite) "笑话 ${joke.id} 已添加至收藏"else"笑话 ${joke.id} 已取消收藏"
} else {
"更新笑话 ${joke.id} 失败:${result.exceptionOrNull()?.message}"
}
}
错误处理:
- 通过抛出 AppFunctionException 子类报告失败
- 库内置异常类型:无效参数、元素未找到、权限不足等
支持的数据类型:
IntLong, Float, Double, Boolean | |
IntArrayLongArray, FloatArray, DoubleArray, BooleanArray | |
StringPendingIntent, Uri, LocalTime, LocalDate, LocalDateTime, Instant | |
@AppFunctionSerializableList |
4.7 工厂配置
OS 负责实例化 AppFunctions 类,需在 Application 中声明工厂:
class MyApplication : Application(), AppFunctionConfiguration.Provider {
overridefun onCreate() {
super.onCreate()
AppModule.initialize(applicationContext)
}
overrideval appFunctionConfiguration: AppFunctionConfiguration
get() = AppFunctionConfiguration.Builder()
.addEnclosingClassFactory(JokesAppFunctions::class.java) { JokesAppFunctions() }
.build()
}
Manifest 注册:
<application
android:name=".MyApplication"
...>
4.8 代码组织结构
AppFunction 属于入站入口点(驱动适配器),与 Compose UI 同级,应置于功能模块的独立包中:
features/jokes/
├─ data/ ← 驱动适配器:Room、Ktor、DTO、映射器
├─ domain/ ← 领域模型、仓库接口(无 Android 依赖)
├─ presentation/ ← Compose 界面 + ViewModels(面向人类用户)
└─ appfunctions/ ← AppFunctions、DTO、映射器(面向 AI Agent)
架构原则:appfunctions/ 之于 Agent 如同 presentation/ 之于人类用户——相同的架构等级,不同的交付对象。
五、测试与验证
5.1 adb 命令测试
列出已注册功能:
adb shell cmd app_function list-app-functions | grep -A 10"[应用包名]"
执行功能:
adb shell cmd app_function execute-app-function \
--package [应用包名] \
--function [完整类名]#[方法名] \
--parameters '{"参数名":值}'
示例命令:
# 清除收藏
adb shell cmd app_function execute-app-function \
--package eu.anifantakis.networkapp \
--function eu.anifantakis.networkapp.jokes.features.jokes.appfunctions.JokesAppFunctions#clearFavorites \
--parameters '{}'
# 获取收藏列表
adb shell cmd app_function execute-app-function \
--package eu.anifantakis.networkapp \
--function eu.anifantakis.networkapp.jokes.features.jokes.appfunctions.JokesAppFunctions#getFavorites \
--parameters '{}'
# 设置收藏状态
adb shell "cmd app_function execute-app-function \
--package eu.anifantakis.networkapp \
--function eu.anifantakis.networkapp.jokes.features.jokes.appfunctions.JokesAppFunctions___HL_5___
--parameters '{\"jokeId\":179,\"isFavorite\":true}'"
5.2 环境要求
验证方法:
adb shell getprop ro.build.fingerprint
# sdk_gphone... 表示支持 cmd
# sdk_phone.../test-keys 表示不支持 cmd
六、最佳实践
6.1 幂等性设计
对于破坏性操作,建议采用以下模式确保幂等性:
SET status = 'active' 而非 SET value = value + 1 | |
6.2 安全建议
EXECUTE_APP_FUNCTIONS 当前为特权权限,但功能层应作为最后防线6.3 文档规范
KDoc 质量直接影响 Agent 的调用决策:
daysOlderThan: Int 优于 filter: String)"setFavorite"),避免 KDoc 链接语法七、相关资源
7.1 官方文档
7.2 示例代码
7.3 参考标准
八、版本状态说明
当前版本:AppFunctions 1.0.0-alpha09
已知限制:
- Gemini 集成处于私人预览阶段,仅对可信测试者开放
- EXECUTE_APP_FUNCTIONS 权限在 Android 16 发布版本上为特权权限
- 完整 Agent 集成需等待后续版本
开发者可用功能:
- 构建、注册和执行 AppFunctions(通过 adb)
- 自定义宿主应用调用(需 userdebug 构建或 root 设备)
本文档基于 AppFunctions 1.0.0-alpha09 版本编写,后续版本可能有变更。请以官方文档为准。
参考链接
[1] ProAndroidDev: https://proandroiddev.com/appfunctions-making-your-android-app-discoverable-by-ai-agents-fbfbeddf8103
[2] AppFunctions 概览: https://developer.android.com/ai/appfunctions
[3] 将 AppFunctions API 添加到你的应用: https://developer.android.com/ai/appfunctions/add-appfunctions
[4] androidx.appfunctions Jetpack 发布说明: https://developer.android.com/jetpack/androidx/releases/appfunctions
[5] 起始代码分支: https://github.com/ioannisa/AppFunctionsDemo/
[6] 完成代码分支: https://github.com/ioannisa/AppFunctionsDemo/tree/02-Finished-Code
[7] 添加 AppFunctions 的 Commit: https://github.com/ioannisa/AppFunctionsDemo/commit/dafd9cdb96755b456605bc758ccfd4e6575434d3
[8] AppFunction 聊天应用演示: https://github.com/android/appfunctions
[9] Model Context Protocol 规范: https://modelcontextprotocol.io/