Here's What I Learned from Udemy's Coroutines and Flow Course
Introduction
Hello! My name is Romie. I’m in the Mobile App Development Group, and I’m responsible for developing the my route app for Android. At KINTO Technologies Corporation (KTC), we have access to Udemy Business accounts, giving us access to a wide range of courses! This time, I chose the course Kotlin Coroutines and Flow for Android Development. Taught entirely in English, it covers the basics of asynchronous processing in Android, and demonstrates how to use Coroutines and Flow.
Reflections on the Course
Here are my honest impressions of the course:
- The English is straightforward and easy to understand.
- Aside from Android-specific terms, there are almost no difficult words.
So, I highly recommend this course for anyone who has moved beyond the beginner stage and wants to learn more about asynchronous processing, Coroutines, and Flow, while also practicing their English!
Topics that left an impression on me
Coroutines and Flow differ from traditional asynchronous processing because they run outside the main thread, making asynchronous tasks easier to write. Additionally, because Coroutines and Flow are part of Kotlin's standard library, there’s no need to include any additional libraries, which is a significant advantage! While these are just the basics, I’ve highlighted the key points below for future reference.
Callback
A callback is a basic method for handling asynchronous processes. You can branch the process using 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
In RxJava, you can branch the process within subscribeBy using onSuccess and onError.
exampleRxJava()
.flatMap { result -> example2() }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy(
onSuccess = {
println("Success")
},
onError = {
println("Error")
}
)
.addTo(CompositeDisposable())
async/await
With async/await, asynchronous processing is handled, and awaitAll is used to gather and process the results together. This is a commonly used pattern in traditional asynchronous processing workflows.
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 performs timeout processing. In withTimeout, an exception is thrown when a timeout occurs.
viewModelScope.launch {
try {
withTimeout(1000L) {
exampleWithTimeout()
}
println("Success")
} catch (timeoutCancellationException: TimeoutCancellationException) {
Println("Error due to timeout")
} catch (exception: Exception) {
println("Error")
}
}
withTimeoutOrNull
withTimeoutOrNull also handles timeouts, but unlike withTimeout, it returns null.
viewModelScope.launch {
try {
val resultWithTimeoutOrNull = withTimeoutOrNull(timeout) {
exampleWithTimeoutOrNull()
}
if (resultWithTimeoutOrNull != null) {
println("Success")
} else {
Println("Error due to timeout")
}
} catch (exception: Exception) {
println("Error")
}
}
Database operations with Room and Coroutines
When combining Room and Coroutines, start by checking if the database is empty; if it is, proceed to insert the required values. Since retrieving values from the database can potentially throw an exception, the operation is wrapped in a try/catch block. Currently, Room and Coroutines are frequently used with Flow to handle asynchronous operations in Android development.
viewModelScope.launch {
val resultDatabaseRoom = databaseRoom.exac()
if (resultDatabaseRoom.isEmpty()) {
Println("The database is empty")
} else {
Println("There are values in the database")
}
try {
val examDataList = getValue()
for (resultExam in examDataList) {
database.insert(resultExam)
}
println("Success")
} catch (exception: Exception) {
println("Error")
}
}
Flow
This is a basic Flow setup. In onStart, the Flow emits an initial value, and in onCompletion, a log message is generated to indicate that the process has finished.
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 are types of Flow. Flow is converted into StateFlow using stateIn. The main difference between Flow and SharedFlow is that Flow does not retain emitted values, whereas SharedFlow does. Unlike StateFlow, which holds a single value that can be accessed directly, SharedFlow allows multiple collectors to receive emitted values. While SharedFlow can also retain values, it doesn't store the current state like StateFlow; instead, it emits a sequence of values to all collectors.
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)
)
Summary
Although much of the content covers the basics, the course was primarily in English, which made it take longer to go through. I believe that after gaining a better overall understanding of asynchronous processing, a second review will deepen my comprehension. However, on the second pass, it seems that studying English will take priority. Thank you for reading to the end.
関連記事 | Related Posts
We are hiring!
【iOSエンジニア】モバイルアプリ開発G/東京
モバイルアプリ開発GについてKINTOテクノロジーズにおける、モバイルアプリ開発のスペシャリストが集まっているグループです。KINTOやmy routeなどのサービスを開発・運用しているグループと協調しながら品質の高いモバイルアプリを開発し、サービスの発展に貢献する事を目標としています。
【iOSエンジニア】モバイルアプリ開発G/大阪
モバイルアプリ開発GについてKINTOテクノロジーズにおける、モバイルアプリ開発のスペシャリストが集まっているグループです。KINTOやmy routeなどのサービスを開発・運用しているグループと協調しながら品質の高いモバイルアプリを開発し、サービスの発展に貢献する事を目標としています。