これはGoogle I/O Writing Challenge
への提出です。Google I/O 2026では、みんなが「Compose First」「Gemini統合」と「Android 17の適応レイアウト推進」について話していました。公平なことですが、それらは大きなものです。しかし、私が考えられないでいた発表がありました。ほとんどヘッドラインにも載らなかった発表ですが、それがAppFunctions APIです。
私は10年間アンドロイドアプリを開発してきました。APIが来たり去ったりしました。これが違うと感じるのは、今日何をするかではなく、アプリがこれからどうなっていくかを示しているからです。
AppFunctionsの実際の意味
根本的には、AppFunctionsはあなたのアプリがAIアシスタントに独立した、名前付きのアクションを直接公開できるようにします。考えられるのは
"Create expense report""Book appointment""Send daily update"
ユーザーがアプリを起動したり、画面にナビゲートしたり、フローをタップしたりする代わりに、Geminiのようなエージェントは、アプリが前景に来ることなく、ユーザーを代表してこれらのアクションを直接呼び出すことができます.
AppFunctionを登録する方法の大まかなイメージはこちらです.
@AppFunction
suspend fun createExpenseReport(
context: AppFunctionContext,
params: CreateExpenseParams
): ExpenseResult {
// your business logic here
return ExpenseResult(id = repo.create(params))
}
関数を定義します。パラメータを定義します。AIがいつ、どのようにそれを呼び出すかを判断します.
AppFunctionsの統合方法:ステップバイステップの例
具体的に説明しましょう。ゼロからGemini呼び出し可能な形までの完全な統合方法です.
ステップ1 — 依存関係を追加
// build.gradle.kts (module)
dependencies {
implementation("androidx.appfunctions:appfunctions:1.0.0-alpha01")
ksp("androidx.appfunctions:appfunctions-compiler:1.0.0-alpha01")
}
ステップ 2 — 入力/出力の型を定義する
AppFunctionsは型付きKotlinデータクラスを使用します。それらをシリアライズ可能に保ちなさい — AIエージェントは自然言語からそれらを構築する必要があります.
@Serializable
data class CreateExpenseParams(
val title: String,
val amount: Double,
val category: String? = null // nullable — agent may not always provide this
)
@Serializable
data class ExpenseResult(
val id: String,
val success: Boolean,
val message: String
)
ステップ 3 — 関数を注釈する
使用@AppFunction はサスペンド関数上で実行されます。アノテーションプロセッサはコンパイル時に登録スキーマを生成します—ボイラープレートXMLは不要です。
class ExpenseAppFunctions(
private val repo: ExpenseRepository
) {
@AppFunction
suspend fun createExpenseReport(
context: AppFunctionContext,
params: CreateExpenseParams
): ExpenseResult {
val id = repo.create(params)
return ExpenseResult(
id = id,
success = true,
message = "Expense created: ${params.title}"
)
}
}
ステップ4 — アジェントセーフにする(全員がスキップするステップ)
アジェントはネットワーク障害時に再試行できる。 idempotencyがないと、1つのユーザーボイスコマンドが重複レコードを作成する可能性があります。
@AppFunction
suspend fun createExpenseReport(
context: AppFunctionContext,
params: CreateExpenseParams
): ExpenseResult {
// Idempotency: return existing record if already created
val existing = repo.findByTitle(params.title)
if (existing != null) {
return ExpenseResult(
id = existing.id,
success = true,
message = "Already exists"
)
}
return ExpenseResult(id = repo.create(params), success = true, message = "Created")
}
注意: AppFunctionContext.callerPackageNameを使用して、エージェントごとにidempotencyキーをスコープ化する場合、より細かい制御が必要な場合は
ステップ5 — AndroidManifestに登録
これがないと、Geminiは実行時にあなたの関数を発見できません
<!-- AndroidManifest.xml -->
<service
android:name=".ExpenseAppFunctions"
android:exported="true"
android:permission="android.permission.BIND_APP_FUNCTION_SERVICE">
<intent-filter>
<action android:name="androidx.appfunctions.AppFunctionService"/>
</intent-filter>
</service>
ステップ6 — ジェミニがそれを使う方法
記録されたら、ユーザーが 「£45のタクシーを経費に追加」 と言えば、これが背景でトリガーされます — UIなし、ナビゲーションなし、タップなし:
// Gemini constructs and executes this on-device
val result = appFunctionManager.executeAppFunction(
targetPackage = "com.yourapp",
functionId = "createExpenseReport",
params = CreateExpenseParams(
title = "Taxi",
amount = 45.0,
category = "Transport"
)
)
// result.message → "Expense created: Taxi"
それは完全なループです。依存からエージェントコール可能なアクションへの6つのステップ——そして実際の作業の大部分はステップ3と4で、ステップ1や
ではありません。 なぜこれは聞こえるより大きいのか
この精神的な変化が必要であることを率直に言います.
過去10年間、Androidアプリは受動的なツールでした は、ユーザーがそれらを開き、それらとやり取りし、閉じます。アプリは待ちます。ユーザーが戻ってきます。それがモデルです.
AppFunctions はそのモデルを完全に破ります.
注意: あなたのアプリはもはや UI だけではありません。外部の知性によって呼び出される機能のセットです — ユーザーを代表して、複数のアプリ間で動作し、明示的なナビゲーションを必要としません。
それはインクリメンタルなアップデートではありません。それは別のパラダイムです.
これが永遠に私たちが持ってきたUXの仮定にとって意味することを考えてみてください.
- ユーザーを案内するために設計されたオンボーディングフロー?エージェントがそれらをバイパスする場合、それほど関連性はありません.
- ナビゲーションアーキテクチャ?まだ重要ですが、それが唯一の入り口ではありません.
- 状態管理?今や、UIライフサイクルの外からトリガーされたアクションを考慮する必要があります
誰も話していない部分:これが開発者に求めるもの
ここで、私は多くのカバレッジがボールを落としていると考えます
皆がAppFunctionsをアプリに「追加」する機能として捉えています。注釈をいくつか追加し、いくつかのアクションを公開すれば完了です。しかし、この捉え方はアーキテクチャ的な意味合いを見逃しています。
あなたのアプリケーションロジックはエージェントに安全である必要があります
これは意味します
-
一貫性がより重要ですネットワークの問題が発生した場合、エージェントは
createExpenseReportを二回呼び出すかもしれません。それを処理しましたか - エラーメッセージは機械が読める必要がありますあなたの関数が失敗した場合、エージェントは復旧またはレポートするための構造化された情報が必要です——トーストではありません
- 権限と認証は明示的に設定される必要があります. ユーザーを代表して行動するエージェントもスコープを尊重する必要があります。周囲のユーザー認証を単に想定することはできません.
これは「機能を追加する」よりも「サービスレイヤーを再考する」に近いです.
私の率直な見解:興奮しますが、準備はできていません
正直に言わせてもらいますが — 広義のAndroid開発者コミュニティはこの移行にまだ準備できていません.
Composableへの移行はまだ中です。多くのチームがまだスパゲッティからMVVMを解きほぐしています。そして今、私たちはアプリをエージェント互換APIとして考えるよう求められていますか?
注意:「GoogleがAppFunctionsを発表した」と「大多数の生産アプリが設計良好なAppFunctionsを公開している」という間のギャップは、年単位で測られるでしょう。それでいいのです——ただし、アーキテクチャについて考えることを始めるなら。今.
このカーブをリードする開発者は、今すぐビジネスロジックを第一級APIとして扱う人々です——クリーンな使用例、明確な入出力契約、適切なエラーハンドリング。まだエージェントが呼んでいるわけではなく、良いアーキテクチャが利益を還元するからです.
明日私がチームに伝えるべきこと
もし明日、I/O 2026から一つの要点を引き出してスタンドアップに入って行ったら、それがこれだ:
エージェント対応のためにあなたのユースケースを監査するのを始めてください。
次のスプリントにAppFunctionsをリリースするためではなく、次のように問いかけます:AIがキーボードに人間がいない場合にこのアクションを呼び出す必要があったら、私たちのコードは耐えうるか?エラー状態は意味があるか?認証モデルは機能するか?
その質問だけで、AppFunctionsに関わらず解決すべき多くの構造的負債が浮かび上がります。
プライバシーについて一言 — 誰かが言わなければならないことです
AppFunctionsに興奮しています。しかし、このプライバシー表面を開くことについて指摘しなければ、あなたに不誠実になることになります。
エージェントがユーザーが明示的に何かをタップしないであなたのアプリを呼び出せる場合、同意モデルが曖昧になります。考えに値するいくつかのことは以下の通りです:
- エージェントは静かに動作します。ユーザーは一度「タクシー費用を追加」と言いました—彼らは自分のデバイスからどのデータが残ったかやどのシステムが触られたかを確認しませんでした。あなたの関数はそれが言う通りに正確に動作する必要があります。
- 機密データには明示的なスコープが必要です。 アプリ機能が金融、健康、またはメッセージに触れる場合、出力を公開APIレスポンスとして扱ってください。必要な最小限のデータのみを返し、それ以上は何も返さないようにしてください.
-
呼び出し元のパッケージ名を検証してください.
AppFunctionContextは呼び出し元のエージェントのパッケージを提供します。機密操作の場合、信頼できる呼び出し元のみをホワイトリストに追加してください——すべてのエージェントが同等の信頼を得るとは想定しないでください. - すべての呼び出しを記録してください。 ユーザーは自分のエージェントが自分の代わりに何をしたかを確認できるべきです。最初から監査証跡を構築してください。
良いニュースは:AndroidのBIND_APP_FUNCTION_SERVICE権限により、デフォルトではGeminiのようなシステムに認められたエージェントのみがあなたの機能を呼び出すことができます。プラットフォームのガイドラインは十分です。しかし、エージェントエコシステムが成長するにつれて、攻撃対象も広がります。
注意: すべてを@AppFunctionは公開APIエンドポイントのように、入力を検証し、出力の範囲を設定し、呼び出しを記録します。注釈はシンプルですが、それが持つ責任はありません。
実際に公開しているもの
これが開発者を驚かせることです:あなたが公開しているのはソースコードではなく、あなたは実際に公開しているのはアプリケーションの機能表面であり、それには独自のリスクがあります。
アプリの機能が外部に示すもの:
createExpenseReportやdeleteUserAccountといった関数名は発見可能な公開スキーマとして機能します — 適切なツールを持つ誰でも、あなたのアプリが登録している内容を列挙できます- パラメータの型はあなたの内部データモデルを明らかにします。
CreateExpenseParamsの形状も、単に名前だけではなく見えます - あなたのビジネスロジックの境界 — もし
archiveAllRecordsは AppFunctionとして存在し、UIを提供する前にその機能を宣伝していた
考慮すべき実際の攻撃シナリオ:
- プローブアプリが登録された関数を列挙してデータモデルを逆-engineerする
- 管理者レベルの関数が内部認証チェックなしで信頼できないエージェントによって呼び出される——マニフェスト権限だけでは不十分
- リリース前に競合他社はあなたの関数名を読み取り、何が来るかを正確に知る
どうすればいいか:
- 明示的にエージェント呼び出し可能として設計されていない関数を登録しない — あなたのリポジトリにあるすべてが
@AppFunction - 認証チェックを追加関数内に すべての機能、宣言された権限とは独立して
- 敏感な操作では汎用的な名前を使用し、関数名自体が意図を漏らさないように
- リリースする前に
@AppFunction注釈を監査する。公開されるREST APIを監査するのと同じ方法で
Google I/O 2026には多くの興奮する発表がありました。Compose Firstは遅れても歓迎されます。AI開発ツールは本当に役立ちます。Android Automotiveは探求する価値のある実際のキャリアパスです.
しかし、AppFunctionsは「Androidアプリ」という意味を変えるものです。それは見過ごされがちなものです。そして5年後、I/O 2026をシフトが始まった瞬間として振り返ると思います。
今日からエージェントがあなたの関数を呼び始めた場合、あなたの現在のサービスレイヤーで idempotency(一貫性)をどのように処理しますか?アーキテクチャの「悪夢」シナリオをコメント欄に投稿してください——それを聞けて嬉しいです.
関連セッション: 開発者キーノート — Google I/O 2026












