Kotlinの1.2.xではまだexperimentalだけど、1.3で正式導入予定のCoroutine、特にasync/awaitはAPI呼び出しなどの非同期な処理に便利そうです。

ただ、AndroidのActivityやFragmentにはライフサイクルがあって、Activityが破棄されたあとにViewをいじったりすると例外が発生してしまうという問題があります。

その辺のことをまじめに考えずに、非同期処理を書いてしまっったら、上記の問題はどうなるのでしょうか?

結論を言ってしまうと、適当に書いても問題なさそうです。

MainActivity.kt
class MainActivity : AppCompatActivity() {

    companion object {
        const val TAG = "MainActivity"
    }

    private val button by lazy { findViewById<TextView>(R.id.button) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        button.setOnClickListener {
            Log.d(TAG, "clicked")
            launch(UI) {
                // coroutineからでないとsuspendedなメソッドを使えない(コンパイル時にエラー)
                Log.d(TAG, "clicked - launch")
                onPressed()
            }
            Log.d(TAG, "clicked - finish")
            finish()
            Log.d(TAG, "clicked - finished")
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG, "destroy")
    }

    private suspend fun onPressed() {
        Log.d(TAG, "pressed")
        val v = async {
            Thread.sleep(5000)
            Log.d(TAG, "pressed - wake up")
            System.currentTimeMillis().toString()
        }
        Log.d(TAG, "pressed - before await")
        button.text = v.await()
        Log.d(TAG, "pressed - after await")
    }
}

こんなActivityを書いて、実行してみます。

onPressed()は、suspend(つまり一旦中断される事がある)で、かつ、UIをいじるメソッドなので、launch(UI)内で使うことになります。

onPressed()内での処理は、確認のために遅延が起こればいいだけなので、5秒スリープしてから、ボタンのキャプションを書き換えるようにしました。

さらに、button.setOnClickListenerでは、onPressed()の終了を待たずに、finish()します。
あと、開発者オプションで、アクティビティを保持しないようにしておけば、Activityが破棄されたあとに、Viewの書き換えが起こるはずです。

そのときのログは、こんな感じになりました。

D/MainActivity: clicked
D/MainActivity: clicked - finish
D/MainActivity: clicked - finished
D/MainActivity: pressed
D/MainActivity: pressed - before await
D/MainActivity: destroy
D/MainActivity: pressed - wake up
D/MainActivity: pressed - after await
D/MainActivity: clicked - launch

onDestroyのあとにViewをいじっても、エラーにならないんですね。

ちょっと気持ち悪い感じがしないでもないですが、async/awaitを使った非同期処理は、Activity等のライフサイクルを考えずに、つかってもよしなにしてくれると思っていいみたいです。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.