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周目では英語の勉強がメインになりそうですが、、
最後までお読みいただきありがとうございました!
関連記事 | Related Posts
We are hiring!
【iOSエンジニア】モバイルアプリ開発G/大阪
モバイルアプリ開発GについてKINTOテクノロジーズにおける、モバイルアプリ開発のスペシャリストが集まっているグループです。KINTOやmy routeなどのサービスを開発・運用しているグループと協調しながら品質の高いモバイルアプリを開発し、サービスの発展に貢献する事を目標としています。
【iOSエンジニア】モバイルアプリ開発G/東京
モバイルアプリ開発GについてKINTOテクノロジーズにおける、モバイルアプリ開発のスペシャリストが集まっているグループです。KINTOやmy routeなどのサービスを開発・運用しているグループと協調しながら品質の高いモバイルアプリを開発し、サービスの発展に貢献する事を目標としています。