Exception Handling in Kotlin Flow
- Authors
- Name
- Amit Shekhar
- Published on
I am Amit Shekhar, Co-Founder @ Outcome School, I have taught and mentored many developers, and their efforts landed them high-paying tech jobs, helped many tech companies in solving their unique problems, and created many open-source libraries being used by top companies. I am passionate about sharing knowledge through open-source, blogs, and videos.
Join Outcome School and get high paying tech job: Outcome School
Before we start, I would like to mention that, I have released a video playlist to help you crack the Android Interview: Check out Android Interview Questions and Answers.
In this blog, we are going to learn about the Exception Handling in Kotlin Flow. We will also learn how to handle errors in the real use-cases in Android Development.
This blog is a part of the series I have written on Flow API in Kotlin:
- Mastering Flow API in Kotlin
- Creating Flow Using Flow Builder in Kotlin
- Terminal Operators in Kotlin Flow
- Cold Flow vs Hot Flow
- StateFlow and SharedFlow
- Long-running tasks in parallel with Kotlin Flow
- Retry Operator in Kotlin Flow
- Retrofit with Kotlin Flow
- Room Database with Kotlin Flow
- Kotlin Flow Zip Operator for Parallel Multiple Network Calls
- Instant Search Using Kotlin Flow Operators
- Exception Handling in Kotlin Flow - YOU ARE HERE
- callbackFlow - Callback to Flow API in Kotlin
- Unit Testing ViewModel with Kotlin Flow and StateFlow
Let's get started.
In Flow, the collection can complete with or without an exception. However, if there is an exception, we can catch that using the catch operator.
For example
Without using the catch operator: It will raise the exception and crash.
(1..5).asFlow()
.map {
// raise exception when the value is 3, intentionally done for the sake of example
check(it != 3) { "Value $it" } // raise exception
it * it
}
.onCompletion {
Log.d(TAG, "onCompletion")
}
.collect {
Log.d(TAG, it.toString())
}
The result will be:
1
4
onCompletion
Exception in thread "main" java.lang.IllegalStateException: Value 3
Exception in thread "main" java.lang.IllegalStateException: Value 3
A crash will happen!
Now, using the catch operator: It will give the handle to us.
(1..5).asFlow()
.map {
// raise exception when the value is 3, intentionally done for the sake of example
check(it != 3) { "Value $it" } // raise exception
it * it
}
.onCompletion {
Log.d(TAG, "onCompletion")
}
.catch { e ->
Log.d(TAG, "Caught $e")
}
.collect {
Log.d(TAG, it.toString())
}
The result will be:
1
4
onCompletion
Caught java.lang.IllegalStateException: Value 3
Caught java.lang.IllegalStateException: Value 3
No crash! Only log.
Note: Whether we use the catch operator or not, in both cases, the emission will stop and it will go in the "onCompletion".
Now, let's take a real use-case in Android Development.
I will be using this project for the implementation part. If you have not gone through the project, you should go through and then come back. The project follows a basic MVVM Architecture for simplicity. You can find the complete code for the implementation mentioned in this blog in the project itself.
Assume that you have two network calls like below:
getUsers()
getMoreUsers()
And we are making both the network calls in parallel using the zip
operator of Flow.
getUsers()
.zip(getMoreUsers()) { usersFromApi, moreUsersFromApi ->
val allUsersFromApi = mutableListOf<ApiUser>()
allUsersFromApi.addAll(usersFromApi)
allUsersFromApi.addAll(moreUsersFromApi)
return@zip allUsersFromApi
}
.flowOn(Dispatchers.IO)
.catch { e ->
// handle error
}
.collect {
// handle response
}
In this case, if any of the network calls fail, it will give the handle to us inside the catch. And we can handle that based on our use-cases.
Suppose, we have another use-case in which if one of the network calls fail, we want to continue with the data from another network call.
We have another network call to stimulate the error:
getUsersWithError()
In this case, we can return the empty list for the network in which we have received the error using the emitAll
by catching the individual error like below:
getUsers().catch { emitAll(flowOf(emptyList())) }
.zip(getUsersWithError().catch { emitAll(flowOf(emptyList())) }) { usersFromApi, moreUsersFromApi ->
val allUsersFromApi = mutableListOf<ApiUser>()
allUsersFromApi.addAll(usersFromApi)
allUsersFromApi.addAll(moreUsersFromApi)
return@zip allUsersFromApi
}
.flowOn(Dispatchers.IO)
.catch { e ->
// handle error
}
.collect {
// handle response
}
Note: We can apply catch to the individual flow and emit data in case of any error.
This was all about the exception handling in Kotlin Flow.
Prepare yourself for Android Interview: Android Interview Questions
That's it for now.
Thanks
Amit Shekhar
Co-Founder @ Outcome School
You can connect with me on:
Follow Outcome School on: