Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is ViewModelScoped coroutine unusable after ViewModel onCleared() method called

I am sharing an ActivityScoped viewModel between multiple Fragments in my current Android application.

The viewModel employs Coroutine Scope viewModelScope.launch{}

My issue is the .launch{} only works until the owning ViewModel onCleared() method is called.

Is this how ViewModel scoped coroutines are supposed to work?

Is there an approach I can use to "Reset" the viewModelScope so that .launch{} works following the onCleared() method being called?

heres my code::

Fragment

RxSearchView.queryTextChangeEvents(search)
        .doOnSubscribe {
            compositeDisposable.add(it)
        }
        .throttleLast(300, TimeUnit.MILLISECONDS)
        .debounce(300, TimeUnit.MILLISECONDS)
        .map { event -> event.queryText().toString() }
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe { charactersResponse ->
            launch {
                viewModel.search(charactersResponse.trim())
            }
        }

. . .

override fun onDetach() {
    super.onDetach()
    viewModel.cancelSearch()
    compositeDisposable.clear()
}

ViewModel

suspend fun search(searchString: String) {
    cancelSearch()

    if (TextUtils.isEmpty(searchString)) {
        return
    }

    job = viewModelScope.launch {
        repository.search(searchString)
    }
}

fun cancelSearch() {
    job?.cancelChildren()
}

. . .

override fun onCleared() {
    super.onCleared()
    repository.onCleared()
 }

What am I doing wrong?

UPDATE

If I amend my launch code to this

job = GlobalScope.launch {
    repository.search(searchString)
}

It solves my issue, however is this the only way to achieve my desired result?

I was under the impression GlobalScope was "Bad"

like image 858
Hector Avatar asked Oct 11 '19 13:10

Hector


People also ask

How do I add a coroutinescope to a ViewModel?

Add a CoroutineScope to your ViewModel by creating a new scope with a SupervisorJob that you cancel in the onCleared () method. The coroutines created with that scope will live as long as the ViewModel is being used. See following code:

What happens when the ViewModel is cleared?

When the ViewModel is cleared, it executes the method clear () before calling the onCleared () method that we would’ve had to override otherwise. In the clear () method the ViewModel cancels the Job of the viewModelScope. The full ViewModel code is also available but we are just focusing on the parts we are interested in:

What is the default coroutine dispatcher for viewmodelscope?

That’s why viewModelScope is of type CloseableCoroutineScope that extends CoroutineScope overriding the coroutineContext and implements the Closeable interface. Dispatchers.Main.immediate is set as the default CoroutineDispatcher for viewModelScope.

What is viewmodelscope in Android lifecycle?

AndroidX lifecycle v2.1.0 introduced the extension property viewModelScope to the ViewModel class. It manages the coroutines in the same way we were doing in the previous section.


1 Answers

following a cal to onCleared() my viewModelScoped cororoutine Launch stops executing

That's a feature, not a bug.

Once the ViewModel is cleared, you should not be doing anything in that ViewModel or whatever its LifecycleOwner was. All of that is now defunct and should no longer be used.

however is this the only way to achieve my desired result?

The correct solution is to get rid of the code from the ViewModel. If you are expecting some background work to go past the lifetime of an activity or fragment, then that code does not belong in the activity/fragment or its associated viewmodels. It belongs in something that has a matching lifetime to the work that you are trying to do.

like image 140
CommonsWare Avatar answered Sep 28 '22 15:09

CommonsWare