MVVM RXAndroid Cannot invoke setValue on a background thread

Viewed 4150

In my app i'm requesting a json to fill recyclerview , at the first in init kotlin viewmodel it always successful but when i wanna to request again in the timer.schedule method it goes to onError method in disposable subscribe , here is Edittext's onTextChange that binded in xml view :

fun onSearchTextChanged(text: CharSequence) {
    if (text.isNotEmpty()) {
        if (myTimer != null) {
            myTimer!!.cancel()
        }

        myTimer = Timer()
        myTimer!!.schedule(object : TimerTask() {
            override fun run() {
                loadMovies(text.toString(), 1)
            }

        }, 1000)
    } else {
        if (myTimer != null) {
            myTimer!!.cancel()
        }
    }
}

and here is my request methods :

fun loadMovies(searchText: String, pageNumber: Int) {
    subscription = searchApi.getSearch(API_KEY, searchText, pageNumber)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .doOnSubscribe { onRetrieveMoviesListStart() }
        .doOnTerminate { onRetrieveMoviesListFinish() }
        .subscribe(
            { result -> onRetrieveMoviesListSuccess(result) },
            { err -> onRetrieveMoviesListError(err) })
}

private fun onRetrieveMoviesListStart() {
    loadingVisibility.value = View.VISIBLE
    Timber.d("started")
}

private fun onRetrieveMoviesListFinish() {
    loadingVisibility.value = View.GONE
    Timber.d("finished")
}

private fun onRetrieveMoviesListSuccess(search: Search) {
    Timber.d(search.toString())
    movieListAdapter.updateMoviesList(search.Search)
    movieEvent.value = MovieEvent.ANIMATE_RECYCLER
}

private fun onRetrieveMoviesListError(err: Throwable) {
    Timber.e(err)
}

And this is viewModel class declaration :

@Inject
lateinit var searchApi: SearchApi

val movieListAdapter: MovieListAdapter = MovieListAdapter()
private var myTimer: Timer? = null

private val movieEvent = SingleLiveEvent<MovieEvent>()
private lateinit var subscription: Disposable
val loadingVisibility: MutableLiveData<Int> = MutableLiveData()

init {
    loadMovies("someText", 1)
}

What should i do ?

1 Answers

Your initial call to loadMovies in the ViewModel init comes from the main/ui thread. The call to loadMovies from the timertask comes from a background thread. Now I don't have too much experience with rx but the problem seems to be related to livedata anyway.

You're observing on the mainthread, which is fine for the success and error methods. Your problem seems to be onRetrieveMoviesListStart() and onRetrieveMoviesListFinish() which are called from a backround thread.

The setter for the value of a livedata can only be called from the main thread, to set the value from a background thread you need to call postValue.

Changing your two methods/functions like this should do the trick:

private fun onRetrieveMoviesListStart() { 
    loadingVisibility.postValue(View.VISIBLE)
    Timber.d("started")
} 
private fun onRetrieveMoviesListFinish() {
    loadingVisibility.postValue(View.GONE)
    Timber.d("finished")
}
Related