Skip to content

ActivityResult APIによるonActivityResultの代替

問題

Android開発において、長年使用されてきたonActivityResult()メソッドが非推奨(deprecated)となりました。このメソッドは、他のアクティビティから結果を受け取るために使用されていましたが、新しいActivity Result APIに置き換えられることになりました。

非推奨となったonActivityResultメソッド

解決策

Android X (Jetpack) では、registerForActivityResult() APIを使用してアクティビティの結果を処理する新しい方法が導入されました。このAPIはより安全で、メモリリークのリスクが少なく、型安全な方法で結果を処理できます。

基本的な使用方法

Kotlinでの実装例

kotlin
// アクティビティ結果のランチャーを登録
private val resultLauncher = registerForActivityResult(
    ActivityResultContracts.StartActivityForResult()
) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        val data: Intent? = result.data
        // 結果を処理
        doSomeOperations()
    }
}

// アクティビティを起動
fun openSomeActivityForResult() {
    val intent = Intent(this, SomeActivity::class.java)
    resultLauncher.launch(intent)
}

Javaでの実装例

java
// アクティビティ結果のランチャーを登録
ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
    new ActivityResultContracts.StartActivityForResult(),
    new ActivityResultCallback<ActivityResult>() {
        @Override
        public void onActivityResult(ActivityResult result) {
            if (result.getResultCode() == Activity.RESULT_OK) {
                Intent data = result.getData();
                doSomeOperations();
            }
        }
    });

// アクティビティを起動
public void openSomeActivityForResult() {
    Intent intent = new Intent(this, SomeActivity.class);
    someActivityResultLauncher.launch(intent);
}

複数のリクエストを処理する場合

以前はrequestCodeを使用して異なるリクエストを区別していましたが、新しいAPIでは各リクエストに対して個別のランチャーを作成します。

kotlin
// 写真撮影用のランチャー
private val takePhotoLauncher = registerForActivityResult(
    ActivityResultContracts.StartActivityForResult()
) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        val photoBitmap = result.data?.extras?.get("data") as Bitmap
        // 写真を処理
    }
}

// ギャラリーからの画像選択用ランチャー
private val pickImageLauncher = registerForActivityResult(
    ActivityResultContracts.StartActivityForResult()
) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        val imageUri = result.data?.data
        // 画像を処理
    }
}

// ランチャーを使用してアクティビティを起動
private fun takePhoto() {
    val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
    takePhotoLauncher.launch(intent)
}

private fun pickImageFromGallery() {
    val intent = Intent(Intent.ACTION_PICK).apply {
        type = "image/*"
    }
    pickImageLauncher.launch(intent)
}

権限リクエストの処理

INFO

新しいAPIでは権限リクエストも同様の方法で処理できます

kotlin
// 単一権限のリクエスト
private val requestPermissionLauncher = registerForActivityResult(
    ActivityResultContracts.RequestPermission()
) { isGranted ->
    if (isGranted) {
        // 権限が付与された
    } else {
        // 権限が拒否された
    }
}

// 複数権限のリクエスト
private val requestMultiplePermissionsLauncher = registerForActivityResult(
    ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
    permissions.entries.forEach { 
        // 各権限の許可状態を処理
    }
}

// 権限をリクエスト
fun requestCameraPermission() {
    requestPermissionLauncher.launch(Manifest.permission.CAMERA)
}

fun requestLocationPermissions() {
    requestMultiplePermissionsLauncher.launch(
        arrayOf(
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.ACCESS_COARSE_LOCATION
        )
    )
}

汎用的なランチャークラスの作成

多くのアクティビティで再利用可能な汎用的なソリューションを作成する場合:

kotlin
class BetterActivityResult<Input, Result>(
    caller: ActivityResultCaller,
    contract: ActivityResultContract<Input, Result>,
    private var onActivityResult: ((Result) -> Unit)?
) {
    private val launcher = caller.registerForActivityResult(contract) { result ->
        onActivityResult?.invoke(result)
    }

    fun launch(input: Input, onActivityResult: ((Result) -> Unit)? = null) {
        if (onActivityResult != null) {
            this.onActivityResult = onActivityResult
        }
        launcher.launch(input)
    }

    companion object {
        fun registerActivityForResult(caller: ActivityResultCaller) =
            BetterActivityResult(
                caller,
                ActivityResultContracts.StartActivityForResult(),
                null
            )
    }
}

// ベースアクティビティで使用
abstract class BaseActivity : AppCompatActivity() {
    protected val activityLauncher = BetterActivityResult.registerActivityForResult(this)
    
    // 従来のstartActivityForResultと互換性のあるメソッド
    fun startActivityForResult(intent: Intent, requestCode: Int, callback: (Int, Int, Intent?) -> Unit) {
        activityLauncher.launch(intent) { result ->
            callback(requestCode, result.resultCode, result.data)
        }
    }
}

重要な注意点

WARNING

アクティビティ結果のランチャーは、アクティビティのonCreateやフラグメントのonAttachなどのライフサイクル初期段階で登録する必要があります。RESUMED状態で登録しようとすると例外が発生します。

TIP

新しいAPIではリクエストコードが廃止されました。代わりに、異なるタイプのリクエストごとに個別のランチャーインスタンスを作成します。

まとめ

Activity Result APIは、従来のonActivityResult()メソッドよりも以下の点で優れています:

  1. 型安全性 - コントラクトを使用して入力と出力の型を明確に定義
  2. メモリ安全性 - ライフサイクルに対応した登録によりメモリリークを防止
  3. テスト容易性 - モックやテストが容易な設計
  4. コードの整理 - コールバックが近くに定義されるため可読性が向上

新しいAPIへの移行は、ほとんどの場合でシンプルで直感的です。各リクエストタイプに応じて適切なActivityResultContractsを選択し、対応するランチャーを作成するだけです。