I am trying to use Workmanager to sync local datab...
# android-architecture
a
I am trying to use Workmanager to sync local database with server,b ut getting this error.
java.lang.IllegalStateException: Cannot invoke setValue on a background thread
Copy code
override fun doWork(): Result {
    println("Worker is Running >>>> ")
    val database = DatabaseClient.getInstance(applicationContext)
    val server = APIClient.retrofitServiceProvider<ArticleService>()
    val articleRepo = ArticleRepo.getInstance(database, server, applicationContext)
    GlobalScope.launch {
            articleRepo.clearData()
            val data = articleRepo.refreshData(1)

    }
    return Result.success()
}
Here, I am trying to clean database and then again making a server call which will fetch data from server and load it into local database.
clearData
is a suspend function which will clear a local db
s
You are updating a livedata and using setValue on it. Replace the setValue by a postValue(newValue). However, it’s strange that you’re updating a livedata from a WorkManager 🤔
👍🏾 1
☝️ 1
f
That's what I was typing. It seems your
clearData
has a flow/livedata which is trying to update the UI in a non-main thread
a
clearData
is suspend function declare in room dao to clear database
And I am making a api call using this abstract class
Copy code
abstract class NetworkBoundResource<ResultType, RequestType>
@MainThread constructor(private val appExecutors: AppExecutors) {

    private val result = MediatorLiveData<Resource<ResultType>>()

    init {
        result.value = Resource.loading(null)
        @Suppress("LeakingThis")
        val dbSource = loadFromDb()

        result.addSource(dbSource) { data ->
            result.removeSource(dbSource)
            if (shouldFetch(data)) {
                fetchFromNetwork(dbSource)
            } else {
                result.addSource(dbSource) { newData ->
                    setValue(Resource.success(newData))
                }
            }
        }
    }

    @MainThread
    private fun setValue(newValue: Resource<ResultType>) {
        if (result.value != newValue) {
            result.value = newValue
        }
    }

    private fun fetchFromNetwork(dbSource: LiveData<ResultType>) {
        val apiResponse = createCall()
        // we re-attach dbSource as a new source, it will dispatch its latest value quickly
        result.addSource(dbSource) { newData ->
            setValue(Resource.loading(newData))
        }
        result.addSource(apiResponse) { response ->
            result.removeSource(apiResponse)
            result.removeSource(dbSource)
            when (response) {
                is ApiSuccessResponse -> {
                    appExecutors.diskIO().execute {
                        saveCallResult(processResponse(response))
                        appExecutors.mainThread().execute {
                            // we specially request a new live data,
                            // otherwise we will get immediately last cached value,
                            // which may not be updated with latest results received from network.
                            result.addSource(loadFromDb()) { newData ->
                                setValue(Resource.success(newData))
                            }
                        }
                    }
                }
                is ApiEmptyResponse -> {
                    appExecutors.mainThread().execute {
                        // reload from disk whatever we had
                        result.addSource(loadFromDb()) { newData ->
                            setValue(Resource.success(newData))
                        }
                    }
                }
                is ApiErrorResponse -> {
                    onFetchFailed()
                    result.addSource(dbSource) { newData ->
                        setValue(Resource.error(response.errorMessage, newData))
                    }
                }
            }
        }
    }

    protected open fun onFetchFailed() {}

    fun asLiveData() = result as LiveData<Resource<ResultType>>

    @WorkerThread
    protected open fun processResponse(response: ApiSuccessResponse<RequestType>) = response.body

    @WorkerThread
    protected abstract fun saveCallResult(item: RequestType)

    @MainThread
    protected abstract fun shouldFetch(data: ResultType?): Boolean

    @MainThread
    protected abstract fun loadFromDb(): LiveData<ResultType>

    @MainThread
    protected abstract fun createCall(): LiveData<ApiResponse<RequestType>>

}
s
I think you should read room documentation, I’m sure they are plenty of example to use Room with a WorkManager 🙂
a
This is a part of githubBrowsersample project by google
refreshData
is implementation of above abstract class
a
It seems in ApiErrorResponse block in your abstract class you are missing appExecutors.mainThread().execute and just call setValue
While in other blocks you call main thread executors but the general reason for this exception - you can't call setValue or value = (which is the same thing) outside of the main thread
👍 1
And it is not really related to work manager or room, you can have the same crash when using any handler or a coroutine
a
Yeah it seem to be the issue