Roman Levinzon
05/19/2023, 12:10 PMViewModel
now allows use custom Coroutines Scopes instead of using viewModelScope
extension
@HiltViewModel
class LoginViewModel @Inject constructor(
private val loginUseCase: LoginUseCase,
private val viewModelScope: CloseableCoroutineScope,
) : ViewModel(viewModelScope) {
I really like the idea, but I feel you need to be very careful with closing this scope. And so, if one forgets to pass this CloseableCorotuineScope
to the ViewModel constructor, the scope wont clear itself after ViewModel is destroyed
Have anybody solved this issue somehow? My only go-to as of know is building a custom linter, but I’d love to see some other ideas 🙂Nino
05/19/2023, 3:08 PMviewModelScope
extension if you're not willing to tinker with ViewModel(CoroutineScope)
?Roman Levinzon
05/19/2023, 3:09 PMNino
05/19/2023, 3:29 PMPatrick Steiger
05/20/2023, 4:35 PMefemoney
05/20/2023, 11:44 PMAnd so, if one forgets to pass thisThing is, a scope delimits the lifecycle of a bunch of work & whoever creates a scope has the responsibility to close that scope when that lifecycle is ended. The fact that you pass ato the ViewModel constructor, the scope wont clear itself after ViewModel is destroyedCloseableCorotuineScope
CloseableCoroutineScope
and it is automatically closed is an implementation detail.
Even the ViewModel
constructor you are referencing takes a list of Closeables
so it knows nothing about whether its a coroutine scope or not. It only has a simple contract, “if you pass me some closeables, when I am closed, I will auto close those too”.Roman Levinzon
05/21/2023, 11:50 AMMuhammad Utbah
05/22/2023, 11:48 AMonCleared()
method in your ViewModel and call the close()
or cancel()
method on the scope to clean up any active coroutines. This requires careful attention to detail and ensuring that the scope is always cleared correctly.
2. CoroutineScope tied to ViewModel lifecycle: Instead of using a custom coroutine scope, you can tie the lifecycle of your coroutines to the ViewModel's built-in coroutine scope (viewModelScope
). This scope is automatically cleared when the ViewModel is destroyed. You can achieve this by using the viewModelScope
directly in your coroutines or by creating child scopes using viewModelScope.launch
or viewModelScope.async
.
3. CoroutineScope with AutoDispose: Another option is to use a library like AutoDispose (or similar solutions) that provides automatic disposal of coroutines. These libraries allow you to define a scope for your coroutines that is tied to the lifecycle of the ViewModel. When the ViewModel is destroyed, the library automatically cancels or disposes of any active coroutines within that scope.
4. Custom CoroutineScope wrapper: You can create a custom wrapper around the coroutine scope that handles automatic cleanup. This wrapper can internally manage the lifecycle of the coroutines and ensure they are properly cleared when the ViewModel is destroyed. You can implement this by extending the CoroutineScope
interface and providing additional methods to handle lifecycle events.
Remember, whichever approach you choose, it's essential to thoroughly test and validate the behavior of your ViewModel and coroutines to ensure proper cleanup and prevent any potential memory leaks or unintended behavior.