-
Notifications
You must be signed in to change notification settings - Fork 22
How to run the coroutine even if the user leaves the screen
Devrath edited this page Jun 15, 2021
·
2 revisions
- Usually, when the user leaves the screen, the ViewModel gets cleared and all the coroutines launched in viewModelScope get canceled.
- 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 applications 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.
class ContinueCoroutineWhenUserLeavesScreenViewModel(
private var repository: AndroidVersionRepository
) : BaseViewModel<UiState>() {
// more information in this blogpost about "Coroutines & Patterns for work that shouldn't
// be cancelled" =>
// https://medium.com/androiddevelopers/coroutines-patterns-for-work-that-shouldnt-be-cancelled-e26c40f142ad
fun loadData() {
uiState.value = UiState.Loading.LoadFromDb
viewModelScope.launch {
val localVersions = repository.getLocalAndroidVersions()
if (localVersions.isNotEmpty()) {
uiState.value =
UiState.Success(DataSource.Database, localVersions)
} else {
uiState.value =
UiState.Error(DataSource.Database, "Database empty!")
}
uiState.value = UiState.Loading.LoadFromNetwork
try {
uiState.value = UiState.Success(
DataSource.Network,
repository.loadAndStoreRemoteAndroidVersions()
)
} catch (exception: Exception) {
uiState.value = UiState.Error(DataSource.Network, "Network Request failed")
}
}
}
fun clearDatabase() {
repository.clearDatabase()
}
}
sealed class DataSource(val name: String) {
object Database : DataSource("Database")
object Network : DataSource("Network")
}
class AndroidVersionRepository(
private var database: AndroidVersionDao,
private val scope: CoroutineScope,
private val api: MockApi = mockApi()
) {
suspend fun getLocalAndroidVersions(): List<AndroidVersion> {
return database.getAndroidVersions().mapToUiModelList()
}
suspend fun loadAndStoreRemoteAndroidVersions(): List<AndroidVersion> {
return scope.async {
val recentVersions = api.getRecentAndroidVersions()
Timber.d("Recent Android versions loaded")
for (recentVersion in recentVersions) {
Timber.d("Insert $recentVersion to database")
database.insert(recentVersion.mapToEntity())
}
recentVersions
}.await()
}
fun clearDatabase() {
scope.launch {
database.clear()
}
}
}