本指南介绍了如何将 AppFunctions API 集成到 Android 应用中、实现函数逻辑,以及验证集成是否正常运行。
版本兼容性
此实现要求将项目 compileSdk 设置为 API 级别 36 或更高级别。
您的应用无需验证是否支持 AppFunctions;AppFunctions Jetpack 库会自动处理此问题。
如果支持该功能,AppFunctionManager 会返回一个实例;否则,会返回 null。
依赖项
将所需的库依赖项添加到模块的 build.gradle.kts(或 build.gradle)文件中,并在顶级应用模块中配置 KSP 插件,如下所示:
dependencies {
implementation("androidx.appfunctions:appfunctions:1.0.0-alpha10")
// If this project uses any Kotlin source, use Kotlin Symbol Processing (KSP)
// See Add the KSP plugin to your project
ksp("androidx.appfunctions:appfunctions-compiler:1.0.0-alpha10")
}
实现 AppFunctions 逻辑
如需为 Android 应用实现 AppFunction,请创建一个实现特定 AppFunctions 逻辑的类。这包括为参数和响应创建可序列化的数据类,然后在函数方法中提供核心逻辑。
以下代码展示了在 TODO 应用中创建任务的示例实现,包括使用代码库定义自定义参数和响应类型以及主要函数逻辑。
@RequiresApi(36) @AndroidEntryPoint @AppFunctionServiceEntryPoint( serviceName = "TaskAppFunctionService", appFunctionXmlFileName = "task_app_function_service", ) abstract class BaseTaskAppFunctionService : AppFunctionService() { @Inject internal lateinit var taskRepository: TaskRepository /** * Creates a task based on [createTaskParams]. * * @param createTaskParams The parameter to describe how to create the task. */ @AppFunction(isDescribedByKDoc = true) suspend fun createTask( createTaskParams: CreateTaskParams, ): Task = withContext(Dispatchers.IO) { // Developers can use predefined exceptions to let the agent know // why it failed. if (createTaskParams.title == null && createTaskParams.content == null) { throw AppFunctionInvalidArgumentException("Title or content should be non-null") } val id = taskRepository.createTask( createTaskParams.title, createTaskParams.content ) return@withContext taskRepository .getTask(id) ?.toTask() ?: throw AppFunctionElementNotFoundException("Task not found for ID = $id") } // Maps internal TaskEntity private fun TaskEntity.toTask() = Task(id = id, title = title, content = description) }
代码要点
- 默认情况下,AppFunction 实现在 Android 界面线程中运行。因此,长时间运行的操作应执行以下操作:
- 将 AppFunction 声明为挂起函数。
- 当操作可能会阻塞线程时,切换到合适的协程调度器。
- 当
isDescribedByKDoc设置为true时,函数说明或可序列化说明会编码为AppFunctionMetadata的一部分,以帮助代理了解如何使用应用的 AppFunction。
在清单中声明 AppFunction 服务
在模块清单(例如 src/main/AndroidManifest.xml)中注册 KSP 生成的服务声明和 app_metadata 属性。
KSP 编译器会生成扩展抽象入口点类的具体服务类 (TaskAppFunctionService),以及 assets/ 目录中的相应 XML 架构。
<service android:name="com.example.snippets.ai.TaskAppFunctionService" android:permission="android.permission.BIND_APP_FUNCTION_SERVICE" android:exported="true" tools:targetApi="36"> <property android:name="android.app.appfunctions.schema" android:value="app_functions_schema.xsd" /> <property android:name="android.app.appfunctions.v2" android:value="task_app_function_service.xml" /> <intent-filter> <action android:name="android.app.appfunctions.AppFunctionService" /> </intent-filter> </service> <property android:name="android.app.appfunctions.app_metadata" android:resource="@xml/app_metadata" />
可选:在运行时切换 AppFunction 可用性
在对 AppFunctions 进行门控时,使用 AppFunctionManager API 显式启用或停用函数。当应用的某些功能不向所有用户开放时,门控功能会非常有用。通过动态启用或停用 AppFunctions,智能系统可以准确了解用户在任何给定时间可使用的功能。
如需安全地限制需要特定账号状态的 AppFunction,请按以下两步流程操作:
第 1 步:默认停用该功能
为防止在验证功能标志之前访问函数,请将 @AppFunction 注解的 isEnabled 参数设置为 false。
@AppFunction(isEnabled = false, isDescribedByKDoc = true) suspend fun createTask( createTaskParams: CreateTaskParams, ): Task = TODO()
第 2 步:在运行时动态启用函数
对于每个 AppFunction 类,编译器都会生成一个包含函数 ID 常量(使用 Ids 后缀)的相应类。您可以将这些生成的 ID 常量与 AppFunctionManagerCompat 中的 setAppFunctionEnabled 方法搭配使用,以便在运行时更改函数的启用状态。
suspend fun onFeatureEnabled(context: Context) { try { AppFunctionManager.getInstance(context) ?.setAppFunctionEnabled( BaseTaskAppFunctionServiceIds.CREATE_TASK_ID, AppFunctionManager.APP_FUNCTION_STATE_ENABLED, ) } catch (e: Exception) { // Handle exception: AppFunctions indexation may not be fully completed // upon initial app startup. } } suspend fun onFeatureDisabled(context: Context) { try { AppFunctionManager.getInstance(context) ?.setAppFunctionEnabled( BaseTaskAppFunctionServiceIds.CREATE_TASK_ID, AppFunctionManager.APP_FUNCTION_STATE_DISABLED, ) } catch (e: Exception) { // Handle exception } }
考虑要提供的功能类型
安全性始终至关重要。在选择将应用的哪些功能作为 AppFunction 提供时,请务必记住,系统代理可能会在服务器上处理用户查询,以利用高级 LLM 功能。
为提供出色的用户体验,同时避免泄露敏感信息,我们建议您遵循以下准则:
- 可从自然语言中获益的功能:提供用户在对话中比通过手动界面导航更容易表达的任务。
- 缩小访问范围:创建 AppFunction,仅授予代理访问满足用户特定请求所需的数据和操作的权限。
- 非敏感信息:仅分享并非高度个人化或机密的数据,或者用户在操作上下文中明确同意分享的数据。
- 针对任何破坏性操作进行明确确认:对于执行破坏性操作(例如删除数据)的函数,请务必格外谨慎。虽然代理可能会调用这些操作,但您的应用应包含自己的确认步骤,并使用清晰明确的语言说明意图。添加多个确认步骤也有助于真正确保用户了解他们需要做什么。
验证 AppFunction 集成
如需验证您是否已正确集成 AppFunctions,可以使用 adb
shell cmd app_function。
使用 adb shell cmd app_function list-app-functions | grep --after-context 10
$myPackageName 查看应用提供的 AppFunctions 的详细信息。
您还可以使用 AppFunction 的显式标识符 ("$enclosingClassName#$methodName") 直接从命令行执行该函数:
adb shell "cmd app_function execute-app-function \
--package com.example.android.appfunctions \
--function 'com.example.android.appfunctions.BaseTaskAppFunctionService#createTask' \
--parameters '{\"createTaskParams\": {\"title\": \"Buy milk\", \"content\": \"From grocery store\"}}'"
如需体验 Android MCP 的实际效果并验证端到端工作流,而无需任何提示,请在设备上安装并运行 AppFunctions 测试代理 Android 应用。
如果您使用基于对话的助理(例如 Android Studio 中的 Gemini)验证集成,请使用 AppFunctions 开发技能,或提供如下提示:
Execute `adb shell cmd app_function` to learn how the tool works, then act as a
chat agent aiming to invoke AppFunctions to fulfil user prompts for this app.
Rely on the AppFunction description as instructions.
从较低的 API 版本迁移
在版本 1.0.0-alpha10 中,AppFunctions 引入了编译时 @AppFunctionServiceEntryPoint 架构,该架构可整合库依赖项并替换旧版配置提供程序 (AppFunctionConfiguration.Provider)。
如果您的应用目前使用的是旧版 AppFunctions(例如 1.0.0-alpha09),您可以在 AI IDE(例如 Android Studio 中的 Gemini)中使用 AppFunctions 代理技能来自动执行迁移。该技能包含专门的迁移规则,可引导代理整合 build 依赖项、创建所需的 @AppFunctionServiceEntryPoint 服务封装容器、分离上下文参数并更新清单声明。
如需使用 AI 智能体启动自动化迁移,请使用以下提示:
Use the AppFunctions migration skill to upgrade my app's AppFunctions implementation from 1.0.0-alpha09 to the 1.0.0-alpha10 @AppFunctionServiceEntryPoint architecture.