πŸŽ“ Learning Kotlin Coroutines for Android by example. πŸš€ Sample implementations for real-world Android use cases. πŸ›  Unit tests included!

Overview

CoroutineUsecasesOnAndroid

Kotlin Coroutines - Use Cases on Android

πŸŽ“ Learning Kotlin Coroutines for Android by example.

πŸš€ Sample implementations for real-world Android use cases.

πŸ›  Unit tests included!

This repository is intended to be a "Playground Project". You can quickly look up and play around with the different Coroutine Android implementations. In the playground package you can play around with Coroutines examples that run directly on the JVM.

πŸ”§ Project Setup

Every use case is using its own Activity and JetPack ViewModel. The ViewModels contain all the interesting Coroutine related code. Activities listen to LiveData events of the ViewModel and render received UiStates.

This project is using retrofit/okhttp together with a MockNetworkInterceptor. This lets you define how the API should behave. Everything can be configured: http status codes, response data and delays. Every use case defines a certain behaviour of the Mock API. The API has 2 endpoints. One returns the names of the most recent Android versions and the other one returns the features of a certain Android version.

Unit Tests exist for most use cases.

🍿️ Related Videos

  • Kotlin Coroutines Fundamentals Playlist [link]
  • Kotlin Coroutines Exception Handling explained [link]

✍️ Related blog posts

  • 7 Common Mistakes you might be making when using Kotlin Coroutines [link]
  • Why exception handling with Kotlin Coroutines is so hard and how to successfully master it! [link]
  • Kotlin Coroutines exception handling cheat sheet [link]
  • Understanding Kotlin Coroutines with this mental model [link]
  • Do I need to call suspend functions of Retrofit and Room on a background thread? [link]
  • Comparing Kotlin Coroutines with Callbacks and RxJava [link]
  • How to run an expensive calculation with Kotlin Coroutines on the Android Main Thread without freezing the UI [link]

Sign up to my newsletter to never miss a new blog post. I will publish new blog posts about Coroutines on a regular basis.

πŸŽ“ Online Course

This project is the foundation of a comprehensive Online Course about Mastering Kotlin Coroutines for Android Development

CourseCoroutinesOnAndroid

πŸ“’ Sharing is Caring

If you like this project, please tell other developers about it! ❀️

Share on Twitter

If you like, you can follow me on Twitter @LukasLechnerDev.

⭐️ Use Cases

  1. Perform single network request
  2. Perform two sequential network requests
  3. Perform several network requests concurrently
  4. Perform variable amount of network requests
  5. Perform a network request with timeout
  6. Retrying network requests
  7. Network requests with timeout and retry
  8. Room and Coroutines
  9. Debugging Coroutines
  10. Offload expensive calculation to background thread
  11. Cooperative Cancellation
  12. Offload expensive calculation to several Coroutines
  13. Exception Handling
  14. Continue Coroutine execution even when the user leaves the screen
  15. Using WorkManager with Coroutines
  16. Performance analysis of dispatchers, number of coroutines and yielding
  17. Perform expensive calculation on Main Thread without freezing the UI

πŸ“„ Description

1. Perform single network request

This use case performs a single network request to get the latest Android Versions and displays them on the screen.

[code]

2. Perform two sequential network requests

This use case performs two network requests sequentially. First it retrieves recent Android Versions and then it requests the features of the latest version.

There are also 2 alternative implementations included. One is using old-school callbacks. The other one uses RxJava. You can compare each implementation. If you compare all three implementations, it is really interesting to see, in my opinion, how simple the Coroutine-based version actually is.

[code]

3. Perform several network requests concurrently

Performs three network requests concurrently. It loads the feature information of the 3 most recent Android Versions. Additionally, an implementation that performs the requests sequentially is included. The UI shows how much time each implementation takes to load the data so you can see that the network requests in the concurrent version are actually performed in parallel. The included unit test is also interesting, as it shows how you can use virtual time to verify that the concurrent version really gets performed in parallel.

[code]

4. Perform variable amount of network requests

Demonstrates the simple usage of map() to perform a dynamic amount of network requests. At first, this use case performs a network request to load all Android versions. Then it performs a network request for each Android version to load its features. It contains an implementation that performs the network requests sequentially and another one that performs them concurrently.

[code]

5. Perform network request with timeout

This use case uses the suspending function withTimeout() from the coroutines-core library. It throws a TimeoutCancellationException if the timeout was exceeded. You can set the duration of the request in the UI and check the behaviour when the response time is bigger than the timeout.

General networking timeouts can also be configured in the okhttp client.

[code]

6. Retrying network requests

Demonstrates the usage of higher order functions together with coroutines. The higher order function retry() retries a certain suspending operation for a given amount of times. It uses an exponential backoff for retries, which means that the delay between retries increases exponentially. The behavior of the Mock API is defined in a way that it responses with 2 unsuccessful responses followed by a successful response.

[code]

Unit tests verify the amount of request that are performed in different scenarios. Furthermore they check if the exponential backoff is working properly by asserting the amount of elapsed virtual time.

7. Network requests with timeout and retry

Composes higher level functions retry() and withTimeout(). Demonstrates how simple and readable code written with Coroutines can be. The mock API first responds after the timeout and then returns an unsuccessful response. The third attempt is then successful.

Take a look at the included callback-based implementation to see how tricky this use case is to implement without Coroutines.

I also implemented the use case with RxJava.

[code]

8. Room and Coroutines

This example stores the response data of each network request in a Room database. This is essential for any "offline-first" app. If the View requests data, the ViewModel first checks if there is data available in the database. If so, this data is returned before performing a network request to get fresh data.

[code]

9. Debugging Coroutines

This is not really a use case, but I wanted to show how you can add additional debug information about the Coroutine that is currently running to your logs. It will add the Coroutine name next to the thread name when calling Thread.currentThread.name() This is done by enabling Coroutine Debug mode by setting the property kotlinx.coroutines.debug to true.

[code]

10. Offload expensive calculation to background thread

This use case calculates the factorial of a number. The calculation is performed on a background thread using the default Dispatcher.

Attention: This use case does not support cancellation! UseCase#11 fixes this!

[code]

In the respective unit test, we have to pass the testDispatcher to the ViewModel, so that the calculation is not performed on a background thread but on the main thread.

11. Cooperative cancellation

UseCase#10 has a problem. It is not able to prematurely cancel the calculation because it is not cooperative regarding cancellation. This leads to wasted device resources and memory leaks, as the calculation is not stopped and ViewModel is retained longer than necessary. This use case now fixes this issue. The UI now also has a "Cancel Calculation" Button. Note: Only the calculation can be cancelled prematurely but not the toString() conversion.

There are several ways to make your coroutines cooperative regarding cancellation: You can use either use isActive(), ensureActive() or yield(). More information about cancellation can be found here

[code]

12. Offload expensive calculation to several Coroutines

The factorial calculation here is not performed by a single coroutine, but by an amount of coroutines that can be defined in the UI. Each coroutine calculates the factorial of a sub-range.

[code viewmodel] [code factorial calculator]

13. Exception Handling

This use case demonstrates different ways of handling exceptions using try/catch and CoroutineExceptionHandler. It also demonstrates when you should to use supervisorScope{}: In situations when you don't want a failing coroutine to cancel its sibling coroutines. In one implementation of this use case, the results of the successful responses are shown even tough one response wasn't successful.

[code]

14. Continue Coroutine execution when the user leaves the screen

Usually, when the user leaves the screen, the ViewModel gets cleared and all the coroutines launched in viewModelScope get cancelled. Sometimes, however, we want a certain coroutine operation to be continued when the user leaves the screen. In this use case, the network request keeps running and the results still get inserted into the database cache when the user leaves the screen. This makes sense in real world application as we don't want to cancel an already started background "cache sync".

You can test this behavior in the UI by clearing the database, then loading the Android version and instantly close the screen. You will see in LogCat that the response still gets executed and the result still gets stored. The respective unit test AndroidVersionRepositoryTest also verifies this behavior. Check out this blogpost for details of the implementation.

[code viewmodel] [code repository]

15. Using WorkManager with Coroutines

Demonstrates how you can use WorkManager together with Coroutines. When creating a subclass of CoroutineWorker instead of Worker, the doWork() function is now a suspend function which means that we can now call other suspend functions. In this example, we are sending an analytics request when the user enters the screen, which is a nice use case for using WorkManager.

[code viewmodel] [code worker]

16. Performance analysis of dispatchers, number of coroutines and yielding

This is an extension of use case #12 (Offload expensive calculation to several coroutines). Here it is possible to additionally define the dispatcher type you want the calculation to be performed on. Additionally, you can enable or disable the call to yield() during the calculation. A list of calculations is displayed on the bottom in order to be able to compare them in a convenient way.

17. Perform expensive calculation on Main Thread without freezing the UI

This example shows how you can perform an expensive calculation on the main thread in a non-blocking fashion. It uses yield() for every step in the calculation so that other work, like drawing the UI, can be performed on the main thread. It is more a "showcase" rather than a use case for a real application, because of performance reasons you should always perform expensive calculations on a background thread (See UseCase#10). See [this blog post] for more information!

You can play around and check the performance of different configurations!

πŸ‘·β€β™€οΈ Contributing πŸ‘·β€β™‚οΈ

I am currently learning Coroutines myself. So if you have any ideas for or improvements or other use cases, feel free to create a pull request or an issue.

Author

LukasLechner

Lukas Lechner

Twitter

License

Licensed under the Apache License, Version 2.0 (the "License"). You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

You agree that all contributions to this repository, in the form of fixes, pull-requests, new examples etc. follow the above-mentioned license.

Comments
  • Main or IO thread

    Main or IO thread

    According to this code your network request will be made in MainThread. Isn't it?

    `class PerformSingleNetworkRequestViewModel( private val mockApi: MockApi = mockApi() ) : BaseViewModel() {

    fun performSingleNetworkRequest() {
        uiState.value = UiState.Loading
        viewModelScope.launch {
            try {
                val recentAndroidVersions = mockApi.getRecentAndroidVersions()
                uiState.value = UiState.Success(recentAndroidVersions)
            } catch (exception: Exception) {
                Timber.e(exception)
                uiState.value = UiState.Error("Network Request failed!")
            }
        }
    }}`
    

    Maybe need to change it a little bit to launch(Dispatchers.IO) {... and uiState.postValue(...)

    opened by w201 4
  • Add info about Workmanager gotcha

    Add info about Workmanager gotcha

    Here's something that caught me out while using WorkManager with coroutines that you might want to add: Do not call a suspend function in the catch block (when you are trying to set the action as failed in the database etc). It will not be called when the work is cancelled due to system constraints. In that case WorkManager will throw JobCancellationException which means suspend functions will re-throw immediately. If you really want the suspend function to run, you must use NonCancellable context. See: https://kotlinlang.org/docs/reference/coroutines/cancellation-and-timeouts.html#run-non-cancellable-block

    opened by CarsonRedeye 2
  • Typo in name of test function

    Typo in name of test function

    opened by kosikov2006 1
  • Propagating state

    Propagating state

    Please how best can propagate state(like status) from the repository down to viewmodel using coroutine(no livedata usage in repository)?. I read a medium article about livedata usage in any other layers aside presentation being inappropriate

    opened by IkemNwodo 1
  • Perform several network requests concurrently

    Perform several network requests concurrently

    coroutineScope {
                        val oreoFeaturesDeferred = async { mockApi.getAndroidVersionFeatures(27) }
                        val pieFeaturesDeferred = async { mockApi.getAndroidVersionFeatures(28) }
                        val android10FeaturesDeferred = async { mockApi.getAndroidVersionFeatures(29) }
    
                        val oreoFeatures = oreoFeaturesDeferred.await()
                        val pieFeatures = pieFeaturesDeferred.await()
                        val android10Features = android10FeaturesDeferred.await()
    
                        val versionFeatures = listOf(oreoFeatures, pieFeatures, android10Features)
    
                        // other alternative: (but slightly different behavior when a deferred fails, see docs)
                        // val versionFeatures = awaitAll(oreoFeaturesDeferred, pieFeaturesDeferred, android10FeaturesDeferred)
    
                        uiState.value = UiState.Success(versionFeatures)
                    }
    

    i have some question about this code, deferred's await() function will suspend this coroutines, so this demo code can't performed in parallel

    opened by ahhsfj1991 1
  • This can be done by synchronously with the help of withContext?

    This can be done by synchronously with the help of withContext?

    https://github.com/LukasLechnerDev/Kotlin-Coroutine-Use-Cases-on-Android/blob/fd939486e24ddad1815c2fa0330c0109c4752e76/app/src/main/java/com/lukaslechner/coroutineusecasesonandroid/usecases/coroutines/usecase14/AndroidVersionRepository.kt#L21

    opened by gouri-panda 0
  • usecase4/VariableAmountOfNetworkRequestsViewModel#performNetworkRequestsConcurrently crashes on exception

    usecase4/VariableAmountOfNetworkRequestsViewModel#performNetworkRequestsConcurrently crashes on exception

    Hi, Lukas!

    I think there's a problem with the solution for

    usecase4/VariableAmountOfNetworkRequestsViewModel#performNetworkRequestsConcurrently.

    It crashes when I configure MockApi to return a 500 on the call to http://localhost/android-version-features/28.

    Reading this about exceptions, it seems like try-catch will not catch the exception because async is not the direct child of the scope - launch is. So async will propagate the exception up to its parent (launch) and launch will throw the exception.

    What do you think?

    - Julian

    opened by lgtout 2
Owner
Lukas Lechner
Freelance Android Developer and Online Course Creator
Lukas Lechner
CovidTracker traces all the covid-19 cases all over the world.

CovidTracker Crona Tracker trace india covid-19 cases upto district level and can trace other countries cases too. It can also traces user's current l

Anuraj Jain 6 May 22, 2021
Victor Hugo 1 Feb 2, 2022
Modular Android architecture which showcase Kotlin, MVVM, Navigation, Hilt, Coroutines, Jetpack compose, Retrofit, Unit test and Kotlin Gradle DSL.

SampleCompose Modular Android architecture which showcase Kotlin, MVVM, Navigation, Hilt, Coroutines, Jetpack compose, Retrofit, Unit test and Kotlin

Mohammadali Rezaei 7 Nov 28, 2022
sample project that shows you how you can use Ktor to creat a server for real Project.

Ktor-Sample This is a sample project that shows you how you can use Ktor to creat a server for real Project. What is done Save data to database (Get a

Mohamed Emad 4 Dec 23, 2022
GraphQL Jetpack - A collection of packages for easily writing Java GraphQL server implementations

GraphQL Jetpack A collection of packages for easily writing Java GraphQL server

Ryan Yang 18 Dec 2, 2022
Documentation and implementations of a recursive procedural generation algorithm authored by Christopher Ravosa.

Recursive-Dungeon-Generation This repository contains implementations of a recursive algorithm, authored by Christopher Ravosa, for generating dungeon

Christopher Ravosa 1 Mar 30, 2022
Meetups microservice, applying tactical DDD building blocks and in a real example.

Meetups microservice: Applying tactical DDD Description This project is a practical example to understand the tactical patterns/building-blocks that d

Albert Llousas Ortiz 6 Aug 2, 2022
These files are included in an Android Studio Project for a Magic the Gathering Life Counter app. The app was written in Kotlin.

Magic-Life-Counter These files were created in Android Studio using Kotlin. Usage This app was made to keep track of life totals while playing the tra

null 0 Dec 24, 2021
Shreyas Patil 2.2k Jan 4, 2023
Kotlin-phoenix - A set of tools aimed to bridge Phoenix with the Kotlin Multiplatform world

Kotlin Phoenix This project is aimed to allow developers to work with Phoenix Ch

Adrien Jacquier Bret 5 Sep 21, 2022
A learning project which focuses on designing an OTP flow and use of various components of Android efficiently

Otp Demo A learning project which focuses on designing an OTP flow using Firebase. Article: https://saurabhpant.medium.com/otp-login-using-firebase-hi

Saurabh Pant 27 Dec 22, 2022
Kotlin hello world for Cloudflare Workers

Kotlin hello world for Cloudflare Workers Your Kotlin code in main.kt, running on Cloudflare Workers In addition to Wrangler you will need to install

ProtoByter 0 Dec 9, 2021
HelloKMM - Hello World in Kotlin Multiplatform Mobile (new empty project)

Hello KMM! Hello World in Kotlin Multiplatform Mobile (new empty project) Gettin

Blake Barrett 1 Feb 2, 2022
Kotlin Unit Testing Examples

Kotlin Unit Testing Examples Table of Contents Application Gradle, Kotlin & Groovy Junit4 Junit5 KotlinTest Spek Mockito Mockito-Kotlin Mockk Strikt T

JarosΕ‚aw 110 Dec 11, 2022
Example of using coroutines with Spring MVC

This is an example of using coroutines with Spring MVC (not webflux!) DemoController uses https://github.com/joost-de-vries/spring-coroutine, but sinc

Tiberiu Tofan 4 Dec 9, 2022
An Android application for browsing video games and checking the latest gaming news from around the world.

Gamedge An Android application for browsing video games and checking the latest gaming news from around the world. Built entirely using the Jetpack Co

Paul Rybitskyi 602 Dec 25, 2022
CodeLab for the workshop: A Composable New World

A Composable New World! Compose is here! ?? I've created a codelab where you can follow step by step the development of android application using Comp

Carlos Mota 9 Nov 25, 2022