ActivityResultLauncher: その概要と使用方法

著者: IT Sectr 公開日: 2026-06-10 読了時間: 9 分

ActivityResultLauncherはAndroid Activity Result APIのコンポーネントであり、androidx.activityライブラリのActivity 1.2.0で導入されました。これは、Android SDKの初期から存在していた非推奨のstartActivityForResultおよびonActivityResultメソッドを置き換えます。Android Developers (2024)によると、新しいAPIはActivityとの密結合や型安全性の欠如といった問題を解消します。ActivityResultLauncherは事前に登録され、Contractを使用して入出力データの厳密な型付けを行います。

主なポイント

  • ActivityResultLauncher — 非推奨のstartActivityForResultに代わる、Activityから結果を取得するための新しいAPI
  • Contract — 特定のシナリオの入出力データの型を定義するオブジェクト
  • 登録はlaunchを呼び出す前にregisterForActivityResultを通じて行います
  • CallbackはターゲットActivityが結果とともに終了した後に呼び出されます
  • APIはActivity 1.2.0以降、Activity、Fragment、Composeで利用可能

ActivityResultLauncherとは

ActivityResultLauncherはandroidx.activity.resultパッケージのクラスで、Activityを起動して結果を受け取るための型安全なメカニズムを提供します。ランチャーはregisterForActivityResultメソッドを通じて作成され、Contract(入出力タイプを記述)とActivityResultCallback(結果ハンドラ)の2つのパラメータを受け取ります。登録後、ランチャーはlaunchメソッドを介して呼び出し可能になります。

古いAPIとの主な違いは、登録と起動の分離です。登録は初期化段階(Activity.onCreateまたはFragment.onCreate)で実行され、コールバックはランチャーに一度だけバインドされ、結果が返ってきたときに確実に実行されます。これにより、onActivityResultが予期しない順序で呼び出されたり、破棄されたActivityで呼び出されたりする問題が解消されます。

ActivityResultLauncherは、以前onActivityResultを介して処理されていたすべてのシナリオをサポートします:カメラの起動、ギャラリー、連絡先の要求、権限、カスタムActivity。さらに、APIは拡張可能で、開発者はActivity間の特定のデータ交換シナリオ向けにカスタムContractを作成できます。

Activity Result APIがstartActivityForResultを置き換えた理由

startActivityForResultはAPI Level 1(2008)からAndroid SDKの一部であり、12年以上にわたってActivityから結果を取得する主要な方法でした。しかし、このメソッドには根本的な欠点があり、GoogleはActivity Result APIでそれらを解決しました。主な問題と新しいAPIがどのように解決するかを見てみましょう。

問題1: Activityとの密結合

startActivityForResultメソッドは、requestCode(onActivityResultに渡される任意の整数)を介してActivityおよびFragmentに結び付けられていました。開発者は手動でコードを起動した操作と照合する必要があり、コードの再利用や継承でエラーが発生していました。ActivityResultLauncherはrequestCodeを完全に排除します。コールバックは登録時に特定のランチャーにバインドされ、そのランチャーに対してのみ呼び出されます。

問題2: 画面回転時の結果の損失

構成変更(画面の回転、言語の変更)時、Activityは再作成され、onActivityResultが実行されない可能性がありました — コールバックが失われていました。Activity Result APIはSavedStateRegistryを介してランチャーの状態を自動的に保存および復元し、Activityの再作成後でも結果が確実に受信されるようにします。

問題3: 型安全性の欠如

古いAPIは、キーとデータ型がコンパイラによってチェックされないBundleを伴うIntentを介して結果を渡していました。Activity Result APIはContract(入力データ型(I)と結果型(O)を定義するジェネリックインターフェース)を使用します。型の不一致エラーは実行時ではなくコンパイル時に検出されます。

特徴startActivityForResultActivityResultLauncher
RequestCode手動管理が必要自動、不要
型安全性なしジェネリックContract
回転時の保存失われるSavedStateRegistry
最小APIAPI Level 1Activity 1.2.0
Composeでの使用非対応rememberLauncherForActivityResult

Activity Result APIの主要なコントラクト

ContractはActivityResultContract<I, O>インターフェースであり、Activityを起動する方法と結果を解釈する方法を定義します。Googleは、ほとんどの開発者のニーズをカバーする一般的なシナリオ向けの組み込みコントラクトのセットを提供しています。

StartIntentSenderForResult

StartIntentSenderForResult — IntentSenderを起動するための基本コントラクト。システムシナリオで使用され、例えばGoogle Sign-Inによる認証やGoogle Payによる支払い時に使用されます。入力パラメータはPendingIntent、出力はコードとIntentを含むActivityResultです。

RequestMultiplePermissions

RequestMultiplePermissions — Android 6.0+で複数の権限を同時に要求するためのコントラクト。入力パラメータは権限名のString配列、出力は各要求の結果を含むMap<String, Boolean>です。以前は、onRequestPermissionsResultで要求コードを照合しながら手動で解析する必要がありました。

TakePictureとTakeVideo

TakePicture — システムカメラを介して写真を撮影するためのコントラクト。入力は画像を保存するUri、出力はBoolean(成功)です。TakeVideoはビデオでも同様に機能します。これらのコントラクトは、デバイスによって動作が不安定な非推奨のMediaStore.ACTION_IMAGE_CAPTUREを置き換えます。

GetContentとOpenDocument

GetContent — システムピッカーを介してコンテンツを選択するためのコントラクト。入力はMIMEタイプ(例:image/*)、出力は選択されたファイルのUriです。OpenDocumentは複数選択とドキュメントタイプによるフィルタリングをサポートする点が異なります。どちらのコントラクトもSAF(Storage Access Framework)を介して動作します。

CreateDocumentとOpenDocumentTree

CreateDocument — システムダイアログを介して新しいドキュメントを作成するためのコントラクト。ユーザーが名前とフォルダを選択し、システムが書き込み用のUriを返します。OpenDocumentTreeはディレクトリ全体へのアクセスを提供します — ユーザーがフォルダを選択すると、アプリは内部のすべてのファイルを読み書きするためのtree-uriを受け取ります。

ActivityとFragmentでの使用

クラシックAndroidでActivityResultLauncherを使用する基本パターンは、初期化時にregisterForActivityResultを介した登録と、ユーザーアクションに応答したlaunchの呼び出しの2つのステップで構成されます。ギャラリーから画像を選択する典型的な例を見てみましょう。

Activityでの登録と起動

ActivityのonCreateでランチャーを登録します — これにより、可能性のある呼び出しの前にコールバックが準備されていることが保証されます。起動の直前にランチャーを登録しないでください — APIコントラクトに違反し、Activityの再作成時に結果が失われる可能性があります。

kotlin
class MainActivity : AppCompatActivity() {
    private val pickImageLauncher =
        registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
            uri?.let { binding.imageView.setImageURI(it) }
        }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        pickImageLauncher.launch("image/*")
    }
}

Fragmentでの使用

Fragmentでは、onCreate、onAttach、またはonCreateViewでの初期化時に登録が行われます。FragmentActivityは親Activityを介してランチャーを渡すため、結果はActivityではなくFragment内で処理されます。これにより、すべてのFragmentからのすべての結果が1つのActivityメソッドに集約されていたonActivityResultと比較して、カプセル化が向上します。

kotlin
class ProfileFragment : Fragment() {
    private val cameraLauncher =
        registerForActivityResult(ActivityResultContracts.TakePicture()) { success ->
            if (success) { updateProfilePhoto() }
        }

    fun takePhoto(photoUri: Uri) {
        cameraLauncher.launch(photoUri)
    }
}

Jetpack ComposeでのActivityResultLauncher

Jetpack Composeは、Activity Result APIのための特別なcomposable関数 — rememberLauncherForActivityResultを提供します。従来のアプローチとは異なり、Composeではランチャーはrememberを介してcomposableのライフサイクルにバインドされたオブジェクトとして作成されます。これにより、ActivityやFragmentに直接アクセスすることなく、宣言的スタイルでActivity Result APIを完全に使用できます。

rememberLauncherForActivityResult

rememberLauncherForActivityResultはContractとコールバックを受け取り、ActivityResultLauncherを返します。ランチャーは再コンポジション中に保持され、コンポジションを離れると自動的にクリアされます。launchの呼び出しは、イベント(ボタンクリックや状態変更など)に応答して発生します。

kotlin
@Composable
fun PhotoPicker() {
    val context = LocalContext.current
    val launcher = rememberLauncherForActivityResult(
        ActivityResultContracts.GetContent()
    ) { uri -> handleImage(uri) }

    Button(onClick = { launcher.launch("image/*") }) {
        Text("画像を選択")
    }
}

Composeでの権限処理

Composeでの権限要求も、RequestPermissionまたはRequestMultiplePermissionsコントラクトを使用してrememberLauncherForActivityResultを介して行われます。Googleはaccompanist-permissionsの使用を推奨していますが、内部ではこれもActivity Result APIを使用しています。権限の状態を追跡するには、rememberまたはViewModelに状態を保存すると便利です。

よくある間違いとベストプラクティス

Activity Result APIは古いアプローチの多くの問題を解消しましたが、不適切な使用は新しい種類のエラーにつながる可能性があります。最も一般的な問題とその回避方法を見てみましょう。

誤り: ラムダやコルーチン内での登録

ランチャーの登録はコンポーネントの初期化時(ActivityのonCreateまたはFragmentの初期化子)に行う必要があります。ラムダ、コールバック、またはコルーチン内でランチャーを登録すると、Activityの再作成時に登録が再度実行され、古いランチャーが結果との接続を失う可能性があります。

誤り: 同じキーで複数のランチャーを登録する

各ランチャーは状態保存のための一意のキーを受け取ります。1つのコンポーネントに同じContractを持つ2つのランチャーを登録すると、SavedStateRegistryが一方の状態を他方で上書きする可能性があります。Android StudioはlintルールUnnecessaryRegisterForActivityResultを介してこれを警告しますが、一意性は手動で制御することをお勧めします。

ベストプラクティス: nullの結果を常に処理する

ユーザーはアクションをキャンセルする可能性があります — システムの戻るボタンを押す、アプリを最小化する、または別のアプリに切り替える。この場合、コールバックはnullまたはRESULT_CANCELEDを含むActivityResultを受け取ります。NullPointerExceptionを回避するために、使用前に結果がnullでないか常に確認してください。

ベストプラクティス: 再利用可能なロジックのためのカスタムContract

アプリが頻繁に同様のシナリオを起動する場合 — 例えば、連絡先を選択して名前と電話番号を返す — カスタムContractを作成します。これによりコードの可読性が向上し、起動と結果処理のロジックを集中管理できます。

kotlin
class PickContactContract : ActivityResultContract<Void, ContactData?>() {
    override fun createIntent(context: Context, input: Void?) =
        Intent(Intent.ACTION_PICK).setType(ContactsContract.Contacts.CONTENT_TYPE)

    override fun parseResult(resultCode: Int, intent: Intent?) =
        intent?.data?.let { queryContact(it) }
}

よくある質問

ViewModelでActivityResultLauncherを使用できますか?

いいえ — ActivityResultLauncherは登録にActivityまたはFragmentのコンテキストが必要です。ViewModelは状態の保存のみに使用し、ランチャーはActivityまたはFragmentで作成して結果をViewModelに渡してください。

Activity Result APIに必要な最小SDKは?

Activity Result APIはライブラリactivity-ktx 1.2.0以降で利用可能です。最小SDKはAPI Level 14(Android 4.0)ですが、ほとんどのコントラクトはAPI Level 19+でのみ動作します。

結果を受け取る前にlaunchを2回呼び出すとどうなりますか?

最初の操作が完了する前の繰り返しのlaunch呼び出しは無視されます。Activity Result APIは並行起動をサポートしていません — 新しい呼び出しの前に最初の操作からのコールバックを待機してください。

古いコードのonActivityResultを置き換えるには?

移行は、startActivityForResultの呼び出しを適切なContractを持つregisterForActivityResultに置き換えることで行います。onActivityResultを削除し、ランチャーのコールバックで結果を処理します。GoogleはAndroid Developersドキュメントで移行ガイドを提供しています。

ActivityResultLauncherはML KitやBarcode Scannerなどのライブラリで動作しますか?

はい、多くのライブラリがActivityResultContractsを介した統合をサポートしています。例えば、ML Kit Barcode Scannerはスキャナーを起動するためにStartIntentSenderForResultを使用します。特定のライブラリのドキュメントを確認してください。

まとめ

  • ActivityResultLauncher — 非推奨のstartActivityForResultに代わる最新の型安全API
  • Contractは入出力データ型を定義し、手動のrequestCode照合を排除
  • 登録は初期化時に実行され、結果はコールバックを介して確実に配信
  • 組み込みコントラクトはカメラ、ギャラリー、権限、ドキュメント、連絡先をカバー
  • Jetpack ComposeはAPIを使用するためにrememberLauncherForActivityResultを使用
  • カスタムContractによりコンポーネント間で起動ロジックを再利用可能
  • APIはSavedStateRegistryを介して構成変更中も状態を保持

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください