Android notes: Understanding viewModelScope.launch{}
Tags: kotlin, tristan
Published: 2023-05-05 19:50:17

### Table of contents
1. [What we are talking about](#talk)
2. [Main parts to a coroutine](#main)
3. [Background work](#back)
4. [Best practice](#best)



### My app on the Google Playstore
- [HERE](https://play.google.com/store/apps/details?id=com.elliottsoftware.calftracker)

### GitHub code <a name="github"></a>
- [HERE](https://github.com/thePlebDev/CalfTracker)

### Resources<a name="intro"></a>
- [Elements of Kotlin Coroutines](https://commonsware.com/Coroutines/) by Mark L. Murphy


### What I am trying to understand <a name="talk"></a>
- So inside my Android app I use a lot of code like this:
```kotlin
viewModelScope.launch{
  // do some work here
}

```
- And up until now, I used it without any sort of real understanding of what is going on. So by the end of this blog post both you and I should have a better understanding of how this code works.

### The 4 main parts to a coroutine. <a name="main"></a>
- Within a coroutine there are 4 main parts

**1) Scope**
**2) Builder**
**3) Dispatcher**
**4) Context**


### 1) Scope
- All coroutine work is managed by a `coroutine scope`. Primarily a coroutine scope is responsible for canceling and cleaning up coroutines when the coroutine scope is no longer needed. With the previously mentioned code, the scope is [viewModelScope](https://medium.com/androiddevelopers/easy-coroutines-in-android-viewmodelscope-25bffb605471). A viewModelScope is defined for each ViewModel in our app. Any coroutine launched in this scope is automatically canceled if the ViewModel is cleared. This is useful for any work that needs to be done only when the ViewModel is active. By destroying the scope when the ViewModel is destroyed, it saves us from potentially wasting resources and memory leaks. Thanks to viewModelScope all of our coroutine clean up and setup is done for us.

### 2) Builder
- All coroutines start with a coroutine builder. The block of code  passed to the builder, along with anything called from that code block(directly or indirectly), represents the coroutine. For us the builder is `launch{}`, launch{} is a fire and forget coroutine builder. Which allows us to pass it a lambda expression to form the route of the coroutine. Since we don't have to wait for any sort of value we  call it and forget about it

### 3) Dispatcher
- When using a coroutine builder, we can provide it a dispatcher. The dispatcher is what indicated what thread pool should be used for the executing the code inside the coroutine.
- We know with `viewModelScope.launch{}` there is a scope and a builder, but where is the dispatcher? As it turns out, when we use `launch{}` without any parameters, it inherits the dispatcher from the coroutine scope. So that means that we inherit the dispatcher from viewModelScope. According to [documentation](https://developer.android.com/kotlin/coroutines/coroutines-best-practices#inject-dispatchers) the dispatcher of viewModelScope is hardcoded to `Dispatchers.Main`. Meaning, unless we provide a dispatcher to launch{}, all the work will be done on the main UI thread. Which is not good and we will see later on how we can avoid this.


### 4) Context
- This is the area of coroutines I am the most unfamiliar with, so I apologize for the brevity 
- The dispatcher that we provide to a coroutine builder is always part of a [CoroutineContext](https://kotlinlang.org/docs/coroutine-context-and-dispatchers.html). As the name suggests the `CoroutineContext` provides a context for executing a coroutine.

### Background work <a name="back"></a>
- If you are like me and you are using `viewModelScope.launch{}`, there is a good chance you are using wrong. Before I started reading about coroutines my code looked like this:
```kotlin
fun getCalves() = viewModelScope.launch(){


        getCalvesUseCase.execute(_uiState.value.calfLimit)
            .collect{response ->
            _uiState.value = _uiState.value.copy(
                data = response,
            )
        }

    }

```
- Which seems perfectly fine until you realize `getCalvesUseCase.execute` is making a network request and all that work is being done on the main UI thread and causing some [Jank](https://developer.android.com/studio/profile/jank-detection). Jank is defined as:

`Android renders UI by generating a frame from your app and displaying it on the screen. If your app suffers from slow UI rendering, then the system is forced to skip frames. When this happens, the user perceives a recurring flicker on their screen, which is referred to as jank.`

- How can we fix some Jank?

- You may have noticed from the code block above that I am using `.collect{}` which means I am using [Flows](https://developer.android.com/kotlin/flow). So now we have to figure out how to change dispatchers with the Flow api. This is pretty easy thanks to [flowOn](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/flow-on.html). To change the dispatcher of a flow we can use `flowOn`. This will make the producer of the flow(code that calls emit) work on the background thread and the upstream of the flow will be unaffected. The new and approved code looks like this:
```kotlin
fun getCalves() = viewModelScope.launch(){


   getCalvesUseCase.execute(_uiState.value.calfLimit)
            .flowOn(Dispatchers.IO) //network requests done on 
             //background thread 
            .collect{response -> // done on main thread
            _uiState.value = _uiState.value.copy(
                data = response,
            )
        }
    }

```
- Thanks to `flowOn` all the network requests are done on the `Dispatchers.IO` which is specifically optimized to handle network requests.

### Best Practice <a name="best"></a>
- It is considered [best practice](https://developer.android.com/kotlin/coroutines/coroutines-best-practices#inject-dispatchers) to inject dispatchers into your ViewModel:
```kotlin
class MainViewModel  constructor(
  private val dispatcherIO: CoroutineDispatcher = Dispatchers.IO

):ViewModel() {
   fun getCalves() = viewModelScope.launch(){


   getCalvesUseCase.execute(_uiState.value.calfLimit)
            .flowOn(dispatcherIO) //network requests done on 
             //background thread 
            .collect{response -> // done on main thread
            _uiState.value = _uiState.value.copy(
                data = response,
            )
        }
    }

}

```

### Conclusion
- Thank you for taking the time out of your day to read this blog post of mine. If you have any questions or concerns please comment below or reach out to me on [Twitter](https://twitter.com/AndroidTristan).