ActivityResult APIによるonActivityResultの代替
問題
Android開発において、長年使用されてきたonActivityResult()
メソッドが非推奨(deprecated)となりました。このメソッドは、他のアクティビティから結果を受け取るために使用されていましたが、新しいActivity Result API
に置き換えられることになりました。
解決策
Android X (Jetpack) では、registerForActivityResult()
APIを使用してアクティビティの結果を処理する新しい方法が導入されました。このAPIはより安全で、メモリリークのリスクが少なく、型安全な方法で結果を処理できます。
基本的な使用方法
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での実装例
// アクティビティ結果のランチャーを登録
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では各リクエストに対して個別のランチャーを作成します。
// 写真撮影用のランチャー
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では権限リクエストも同様の方法で処理できます
// 単一権限のリクエスト
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
)
)
}
汎用的なランチャークラスの作成
多くのアクティビティで再利用可能な汎用的なソリューションを作成する場合:
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()
メソッドよりも以下の点で優れています:
- 型安全性 - コントラクトを使用して入力と出力の型を明確に定義
- メモリ安全性 - ライフサイクルに対応した登録によりメモリリークを防止
- テスト容易性 - モックやテストが容易な設計
- コードの整理 - コールバックが近くに定義されるため可読性が向上
新しいAPIへの移行は、ほとんどの場合でシンプルで直感的です。各リクエストタイプに応じて適切なActivityResultContracts
を選択し、対応するランチャーを作成するだけです。