KINTO Tech Blog
Android

UdemyでCoroutinesとFlowの講座を受けました

Cover Image for UdemyでCoroutinesとFlowの講座を受けました

はじめに

こんにちは!モバイルアプリ開発Gにてmy routeアプリのAndroid側の開発を担当しておりますRomieです。
KINTOテクノロジーズ株式会社(以下KTC)では、Udemy Businessのアカウントを利用して様々な講座を受講することができます!
今回はKotlin Coroutines and Flow for Android Developmentを受講しました。
Androidにおける非同期処理およびCoroutinesとFlowの理解を深めるために基本的な部分をAll Englishで解説していく講座です。

学習した感想

率直な感想は以下の通りです。

  • 英語は非常に平坦でわかりやすい
  • Androidの用語を除いて難しい単語がほとんどない

ですので、 初学者から抜け出してしっかり非同期処理およびCoroutinesとFlowを勉強されたい方・Androidの基礎とともに英語の勉強もされたい方には非常におすすめです!

印象に残った項目

CoroutinesとFlowは従来の非同期処理と異なりメインスレッド以外で実行され、非同期処理をより簡潔に記述することができます。
また、CoroutinesとFlowはKotlinの標準ライブラリに含まれているため、追加のライブラリを導入する必要がありません。
ここだけ取り上げても非常に大きなメリットがありますね!
全て基本的な形式になりますが、備忘録も兼ねて以下に記載します。

Callback

Callbackは基本的な非同期処理ですね。onResponse/onFailureで処理を分岐させることができます。

exampleCallback1()!!.enqueue(object : Callback<Any> {
    override fun onFailure(call: Call<Any>, t: Throwable) {
        println("exampleCallback1 : Error - onFailure")
    }

    override fun onResponse() {
        if (response.isSuccessful) {
            println("exampleCallback1 : Success")
        } else {
            println("exampleCallback1 : Error - isSuccessful is false")
        }
    }
})

RxJava

RxJavaはsubscribeByの中でonSuccess/onErrorで処理を分岐させることができます。

    exampleRxJava()
    .flatMap { result -> example2() }
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribeBy(
        onSuccess = {
            println("Success")
        },
        onError = {
            println("Error")
        }
    )
    .addTo(CompositeDisposable())

async/await

async/awaitで非同期処理を行い、awaitAllで結果をまとめて処理を行います。こちらは従来の非同期処理の中でもよく使われる形式ですね。

        viewModelScope.launch {
            try {
                val resultAsyncAwait =
                    awaitAll(
                        async { exampleAsyncAwait1() },
                        async { exampleAsyncAwait2() },
                        async { exampleAsyncAwait3() }
                    )
                println("Success")
            } catch (exception: Exception) {
                println("Error")
            }
        }
        viewModelScope.launch {
            try {
                val resultAsyncAwait = exampleAsyncAwait()
                    .map { result ->
                        async { multiExampleAsyncAwait() }
                    }.awaitAll()
                println("Success")
            } catch (exception: Exception) {
                println("Error")
            }
        }

withTimeout

withTimeoutではタイムアウト処理を行います。withTimeoutではタイムアウト時に例外が発生します。

        viewModelScope.launch {
            try {
                withTimeout(1000L) {
                    exampleWithTimeout()
                }
                println("Success")
            } catch (timeoutCancellationException: TimeoutCancellationException) {
                println("タイムアウトによるError")
            } catch (exception: Exception) {
                println("Error")
            }
        }

withTimeoutOrNull

withTimeoutOrNullもタイムアウト処理ですが、withTimeoutと異なりwithTimeoutOrNullではタイムアウト時にnullを返します。

        viewModelScope.launch {
            try {
                val resultWithTimeoutOrNull = withTimeoutOrNull(timeout) {
                    exampleWithTimeoutOrNull()
                }

                if (resultWithTimeoutOrNull != null) {
                    println("Success")
                } else {
                    println("タイムアウトによるError")
                }
            } catch (exception: Exception) {
                println("Error")
            }
        }

RoomとCoroutinesによるデータベース操作

RoomとCoroutinesを組み合わせた処理ではデータベースが空かどうかを確認し、データベースに値があればinsertします。
現在のデータベースの値を取得する処理は例外が発生する可能性があるため、try/catchで囲んでいます。
現在Androidの非同期処理ではFlowと共に非常に多くの場面で使われているのではないでしょうか。

viewModelScope.launch {
    val resultDatabaseRoom = databaseRoom.exac()
    if (resultDatabaseRoom.isEmpty()) {
        println("データベースは空です")
    } else {
        println("データベースに値あり")
    }

    try {
        val examDataList = getValue()
        for (resultExam in examDataList) {
            database.insert(resultExam)
        }
        println("Success")
    } catch (exception: Exception) {
        println("Error")
    }
}

Flow

基本的なFlowですね。onStartで初期値をemitし、onCompletionで処理が完了したことをログに出力します。

    sealed class UiState {
        data object Loading : UiState()
        data class Success(val stockList: List<Stock>) : UiState()
        data class Error(val message: String) : UiState()
    }

    val anythingAsLiveData: LiveData<UiState> = anythingDataSource
        .map { anyList ->
            UiState.Success(anyList) as UiState
        }
        .onStart {
            emit(UiState.Loading)
        }
        .onCompletion {
            Timber.tag("Flow").d("Flow has completed.")
        }
        .asLiveData()

SharedFlow/StateFlow

SharedFlow/StateFlowはFlowの一種です。stateInでStateFlowに変換します。
FlowとSharedFlowの違いは、Flowがemitされた値を保持しないのに対し、SharedFlowはemitされた値を保持する点です。
StateFlowは他の2つと異なり、初期値を持ち自分自身で値の取得ができます。
SharedFlowはStateFlowと同じように値を保持しますが、複数のコレクターが値を受け取ることができます。

    sealed class UiState {
        data object Loading : UiState()
        data class Success(val stockList: List<Stock>) : UiState()
        data class Error(val message: String) : UiState()
    }

    val anythingAsFlow: StateFlow<UiState> = anythingDataSource
        .map { anyList ->
            UiState.Success(anyList) as UiState
        }
        .onCompletion {
            Timber.tag("Flow").d("Flow has completed.")
        }.stateIn(
            scope = viewModelScope,
            initialValue = UiState.Loading,
            started = SharingStarted.WhileSubscribed(stopTimeoutMillis = 5000)
        )

まとめ

内容自体は基本的な部分が多いですが、英語での解説が多かったため1周するのに時間がかかりました。
もっと非同期処理の全体像について理解したあとに2周目を行うと理解が深まるかと思います。
2周目では英語の勉強がメインになりそうですが、、
最後までお読みいただきありがとうございました!

Facebook

関連記事 | Related Posts

We are hiring!

【iOSエンジニア】モバイルアプリ開発G/大阪

モバイルアプリ開発GについてKINTOテクノロジーズにおける、モバイルアプリ開発のスペシャリストが集まっているグループです。KINTOやmy routeなどのサービスを開発・運用しているグループと協調しながら品質の高いモバイルアプリを開発し、サービスの発展に貢献する事を目標としています。

【iOSエンジニア】モバイルアプリ開発G/東京

モバイルアプリ開発GについてKINTOテクノロジーズにおける、モバイルアプリ開発のスペシャリストが集まっているグループです。KINTOやmy routeなどのサービスを開発・運用しているグループと協調しながら品質の高いモバイルアプリを開発し、サービスの発展に貢献する事を目標としています。