96SEO 2026-05-04 06:15 24
说实话,Android 开发这行当里每隔一阵子就会冒出个新玩意儿。有的昙花一现,有的却Neng改变游戏规则。Zui近 App Functions 这个话题在圈子里热度渐起,尤其是在 alpha08 版本发布后hen多细节开始浮出水面。这不仅仅是一个简单的 API geng新,它geng像是在为未来的 AI Agent 时代铺路。咱们今天不聊虚的,直接扒开源码和文档,kankan这东西到底该怎么玩,怎么才Neng算是“深入掌握”。

以前咱们搞 Android 跨 App 交互,脑子里想的第一反应是什么?Deep Link?Intent 隐式调用?说白了本质上还是“我知道你有个页面我跳过去”。这种模式在 AI 时代显得有点笨重。现在的趋势是我不关心你的 UI 长什么样,我只关心你Neng不Neng干这件事。
这就是 App Functions 的核心逻辑。它把 App 的入口从“页面地址”升级成了“Neng力描述”。Ru果你的记事本 App 暴露了一个 createNote 函数,提醒 App 暴露了 scheduleReminder,那么系统或者某个 Agent 就Ke以直接编排这些动作,完全不需要用户去点点点。这事儿挺有意思的,因为它逼着咱们开发者重新审视 App 的边界:你的 App 到底提供了什么Neng力,而不仅仅是展示了什么界面。
工欲善其事,必先利其器。要上手这套机制,先得把依赖搞对。别kan就几行代码,这里面的门道可不少。官方现在的结构分得hen细,不是一个大包全扔给你。
dependencies {
implementation
implementation
ksp
}
这里咱们得拆开kan。第一个 appfunctions 是核心 API,不管是调用方还是提供方dou得用它;第二个 appfunctions-service 主要是给服务端用的,负责把你的Neng力暴露出去;第三个 appfunctions-compiler Zui关键,它利用 KSP 在编译期生成元数据和那些繁琐的接线代码。这也就意味着,这玩意儿不是那种“运行时随手反射一下”的轻量级库,而是一套严丝合缝的声明式Neng力暴露机制。
Zui基础的入口,莫过于 @AppFunction 这个注解了。但光有注解不行,你还得有个“门卫”守着,这就是 AppFunctionService。
在 Manifest 里你得这么注册:
然后是 Service 的具体实现。这里有个坑,也是亮点:addEnclosingClassFactory。
import androidx.appfunctions.AppFunctionException
import androidx.appfunctions.ExecuteAppFunctionRequest
import androidx.appfunctions.ExecuteAppFunctionResponse
import androidx.appfunctions.service.AppFunctionConfiguration
import androidx.appfunctions.AppFunctionService
class NoteAppFunctionService : AppFunctionService,
AppFunctionConfiguration.Provider {
override val appFunctionConfiguration: AppFunctionConfiguration
get = AppFunctionConfiguration.Builder
.addEnclosingClassFactory {
NoteFunctions)
}
.build
override fun executeFunction(
request: ExecuteAppFunctionRequest
): ExecuteAppFunctionResponse {
throw AppFunctionException(
errorCode = ,
errorMessage = "Use compiler generated dispatch path"
)
}
}
官方文档特意强调了这一点。Ru果你的 @AppFunction 所在的类没有无参构造函数,或者你需要注入 Repository 之类的依赖,千万别硬来用这个工厂方法接进去才是正道。这也意味着,App Functions 并不强迫你把业务逻辑写成一堆难kan的静态工具类方法,你完全Ke以保留原本清爽的 Repository / Use Case 架构,只是多开了一扇面向系统的窗户而Yi。
光有函数还不够,还得有“说明书”。alpha08 引入了一个新东西:@AppFunctionSchemaDefinition。这玩意儿的意义在于,它把函数Neng力升级成了可检索、可共享、可版本化的 Schema。
import androidx.appfunctions.AppFunctionContext
import androidx.appfunctions.AppFunctionSchemaDefinition
@AppFunctionSchemaDefinition(
name = "createNote",
version = ,
category = "Notes"
)
interface CreateNoteSchema {
suspend fun createNote(
appFunctionContext: AppFunctionContext,
params: CreateNoteParams
): NoteDto
}
这可不是为了代码好kan。官方文档里写得hen直白,Agent Ke以通过 AppFunctionManager.observeAppFunctions 拿到这些元数据。再往深了想,这就不再是某个 App 的私有协议了而是一种跨 App 的“Neng力协定”。Ru果你的函数准备长期暴露给 Agent 用,用 Schema 来描述清楚,绝对是明智之举。
以前咱们是“拿着地址找门”,现在是“先查目录再办事”。App Functions 的调用模型是先发现,再执行。
import androidx.appfunctions.AppFunctionManager
import androidx.appfunctions.AppFunctionSearchSpec
import kotlinx.coroutines.flow.first
suspend fun discoverNoteFunctions {
val manager = AppFunctionManager.getInstance ?: return
val packages = manager.observeAppFunctions(
AppFunctionSearchSpec(
packageNames = listOf,
schemaName = "createNote"
)
).first
val packageMetadata = packages.singleOrNull ?: return
val functionMetadata = packageMetadata.appFunctions.firstOrNull ?: return
println
println
}
这段代码展示了怎么去“查目录”。这和传统 Android 开发感觉hen不一样,geng像是在调用 RPC 接口。找到了函数 ID 之后怎么执行呢?核心对象是 ExecuteAppFunctionRequest。
import androidx.appfunctions.AppFunctionData
import androidx.appfunctions.AppFunctionManager
import androidx.appfunctions.ExecuteAppFunctionRequest
suspend fun callCreateNote(
context: Context,
functionId: String
) {
val manager = AppFunctionManager.getInstance ?: return
val params = AppFunctionData.Builder
.setString
.setString
.build
val request = ExecuteAppFunctionRequest(
targetPackageName = "com.example.notes",
functionIdentifier = functionId,
parameters = params
)
val response = manager.executeAppFunction
when {
is ExecuteAppFunctionResponse.Success -> {
val result = response.result
println
}
is ExecuteAppFunctionResponse.Error -> {
println
}
}
}
这里有两个工程上的细节得记一下。第一,函数 ID Zui好别自己手写硬编码字符串,官方编译器会生成一个 ID 类,用那个常量稳得多。第二,错误处理要讲究。因为调用方hen可Neng是系统或者别的 Agent,你抛个莫名其妙的“failed”过去,人家根本没法处理。
警惕主线程陷阱:异步处理的正确姿势这个点必须得单独拎出来说太重要了。官方 API reference 明确写了@AppFunction 默认是在主线程执行的,AppFunctionService.executeFunction 也是主线程入口。
这说明什么?说明官方没把它当成纯静态声明,而是把“函数当前是否可用”也纳入了运行时控制。但这对咱们开发者来说就是个坑。Ru果你直接在函数里搞网络请求、数据库读写、文件 IO,那不是“可Neng有点慢”,而是模型直接就错了直接 ANR 给你kan。
Zui稳妥的写法,是把真正干活的部分切到后台 Dispatcher:
class NoteFunctions(
private val noteRepository: NoteRepository,
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) {
@AppFunction
suspend fun createNote: NoteDto =
withContext {
val note = noteRepository.create
NoteDto
}
}
Ru果以后线上真大规模用这套东西,这里绝对是第一批踩坑的重灾区,大家务必小心。
错误处理与参数校验既然是跨边界调用,参数校验和错误语义就格外重要。别像写内部代码那样随意,得按规矩来。
import androidx.appfunctions.AppFunctionInvalidArgumentException
import androidx.appfunctions.service.AppFunction
class NoteFunctions(
private val noteRepository: NoteRepository
) {
@AppFunction
suspend fun createNote: NoteDto {
if ) {
throw AppFunctionInvalidArgumentException
}
if ) {
throw AppFunctionInvalidArgumentException
}
val note = noteRepository.create
return NoteDto
}
}
官方文档这里给得hen明确,应该抛 AppFunctionException 体系里的异常,而不是把所有问题dou吞成未知错误。参数对象也一样,尽量Zuo成明确的数据结构,别一股脑塞个 Map,那样维护起来简直是噩梦。
import androidx.appfunctions.AppFunctionSerializable
@AppFunctionSerializable
data class NoteDto(
val id: Long,
val title: String,
val content: String
)
测试:跨边界调用的保障
alpha08 里Yi经有了 AppFunctionTestRule,而且官方给了相对像样的测试路径。这事儿挺重要,因为 App Functions 本来就是跨边界调用。没有发现链路、执行链路、错误链路的完整测试,这东西hen容易只剩“注解写上了kan起来挺先进”,实际上一跑就挂。
先配一下测试环境:
dependencies {
kspTest
testImplementation
}
ksp {
arg
}
然后写个测试用例跑跑kan:
class ExampleFunctionsTest {
@get:Rule
val appFunctionTestRule = AppFunctionTestRule
@Test
fun add_returnsCorrectSum = runBlocking {
val manager = appFunctionTestRule.getAppFunctionManager
val packageMetadata = manager.observeAppFunctions(
AppFunctionSearchSpec(
packageNames = listOf
)
).first.single
val functionMetadata = packageMetadata.appFunctions.single
val request = ExecuteAppFunctionRequest(
targetPackageName = context.packageName,
functionIdentifier = functionMetadata.id,
parameters = AppFunctionData.Builder
.setInt
.setInt
.build
)
val response = manager.executeAppFunction
println
}
}
演进思维:版本控制与参数对象
Zui后聊聊长期维护。一旦函数暴露给系统或 Agent,它就不再只是你 App 内部的私有方法,而geng像一个对外协议。协议一旦被消费,就会遇到版本演进问题。
比如下面这两个函数,技术上douNeng工作:
@AppFunction
suspend fun createNote: NoteDto
@AppFunction
suspend fun createNote: NoteDto
第二种通常geng适合往长期演进。因为一旦后面要补字段,比如 tagsfolderIdpinnedsource,参数对象Neng稳住接口形态,也geng适合和 Schema 对齐。alpha07 开始官方支持对 AppFunction Zuo @Deprecated,这个改动不大,但意义hen重。
@AppFunction
@Deprecated instead")
suspend fun createLegacyNote: NoteDto {
// ...
}
此外isDescribedByKDoc = true 这个参数也特别值钱。因为它不是拿来给人kan注释这么简单,而是会把 KDoc 里的函数描述、参数描述、返回值描述,转成函数元数据。对 Agent 来说这些描述不是装饰,而是理解Neng力边界的一部分。
Ru果只是想追新,App Functions Ke以当新闻kan。但Ru果认真一点kan,它geng像 Android 正在补的一块长期基础设施。它把以前分散的问题——Neng力建模、函数元数据、编译期生成、服务暴露、跨进程执行、可观测发现、运行时开关和测试——重新合在一起了。
它现在还在 alpha 阶段,所以不适合吹成“Android 下一代主流开发方式Yi经完全成熟”。但它Yi经足够值得咱们花时间去深挖,因为它正在重塑 Android App 的存在形态:从一个个孤立的“页面”,变成一个个可被连接的“服务”。这事儿,值得。
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback