startActivityForResultを待ち合わせる

これは

遷移先のActivityから処理結果を得るには、これまでonActivityResultをオーバーライドし、requestCodeで分岐するような実装が必要でした。

androidx.activity:1.2.0、で代替手段となるコールバック方式のAPI registerForActivityResultが提供されています。

developer.android.com

これをRxJavaやKotlin Coroutinesと一緒に使うことで、処理結果であるActivityResultを待ち合わせる実装をしてみます。

Normalな実装

registerForActivityResultの基本的な使い方は上記の公式ガイドを見ていただくとして、後述する実装と統一感をもたせるためregisterForActivityResultをラップした実装を載せておきます。

class NormalStartActivityForResultLauncher(
    activity: ComponentActivity,
    action: (ActivityResult) -> Unit,
) {

    private val startActivityForResult = activity.registerForActivityResult(
        StartActivityForResult()
    ) { activityResult -> action(activityResult) }

    fun launch(intent: Intent) {
        startActivityForResult.launch(intent)
    }
}

これはこう使います。

private val normalLauncher = NormalStartActivityForResultLauncher(this) { activityResult ->
        // なにか処理
}

val intent = Intent(this, TargetActivity::class.java)
normalLauncher.launch(intent)

TargetActivityの結果をコールバックで得ることができます。

RxJavaな実装とKotlin Coroutinesな実装

RxJava

Singleを使ってこのような実装をしてみました。RxJava3を利用しています。

class RxStartActivityForResult(
    activity: ComponentActivity
) {

    private var emitter: SingleEmitter<ActivityResult>? = null
    private val startActivityForResultLauncher = activity.registerForActivityResult(
        StartActivityForResult()
    ) {
        emitter?.onSuccess(it)
    }

    fun launch(intent: Intent): Single<ActivityResult> {
        startActivityForResultLauncher.launch(intent)
        return Single.create { emitter = it }
    }
}

コールバックで取得したActivityResultをemitter::onSuccessで渡しています。 これはこんな感じに使えます。

private val rxLauncher = RxStartActivityForResult(this)

val intent = Intent(this, TargetActivity::class.java)
rxLauncher.launch(intent)
    .subscribeOn(Schedulers.single())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe { activityResult ->
        // なにか処理
    }

Kotlin Coroutines

同じような要領で実装します。CompletableDeferredを使ってみました。

class DeferredStartActivityForResultLauncher(
    activity: ComponentActivity
) {

    private var deferred: CompletableDeferred<ActivityResult>? = null
    private val startActivityForResultLauncher = activity.registerForActivityResult(
        StartActivityForResult()
    ) {
        deferred?.complete(it)
    }

    fun launch(intent: Intent): CompletableDeferred<ActivityResult> {
        startActivityForResultLauncher.launch(intent)
        deferred = CompletableDeferred()
        return deferred!!
    }
}

利用側はこんな感じです。

private val deferredLauncher = DeferredStartActivityForResultLauncher(this)

lifecycleScope.launch {
    val intent = Intent(this, TargetActivity::class.java)
    val activityResult = deferredLauncher.launch(intent).await()
    // なにか処理
}

さいごに

コールバックではなくRxJavaやKotlin Coroutinesを使う必要があるユースケースはあまり多くないように思いますが、registerForActivityResultの登場でこういう実装もしやすくなったと感じます。

挙げているコードはこちらに置いています。

github.com