I am using Retrofit, LiveData, Room (Android AAC). NetworkBoundResource
is a great helper to combine network source and room provided by googlesimple.
Since Retrofit 2.6.0 introduced built-in support for suspend, I tried to modify NetworkBoundResource
to use suspend funciton instaed of LiveDataCallAdapter
but encountered a lot of troubles.
Here's my modification:
abstract class NetworkBondResource<ResultType, RequestType>
@MainThread constructor() {
private val result = MediatorLiveData<Resource<ResultType>>()
private val supervisorJob = SupervisorJob()
init {
result.value = Resource.loading(null)
}
fun asLiveData() = result as LiveData<Resource<ResultType>>
suspend fun load() {
withContext(Dispatchers.Main) {
val dbSource = loadFromDb()
result.addSource(dbSource) { data ->
result.removeSource(dbSource)
if (shouldFetch(data)) {
// ! HERE--------------
GlobalScope.launch(Dispatchers.IO) {
fetchFromNetwork(dbSource)
}
} else {
result.addSource(dbSource) { newData ->
setValue(Resource.success(newData))
}
}
}
}
}
// others code...
}
The question is code within result.addSource(dbSource)
unable to inherit external scope. I have to use GlobalScope
to launch a new coroutine which will causing a 'Coroutine Leak' since it is not controlled by the viewModel
scope.
Also I found another way. But this scheme violates the principle of a single trusted source, lost the core role of NetworkBoundResource
.
Any ideas will be appreciated.
I also had the same problem, my solution was this. I recommend that you do not use GlobalScope for these reasons. Make it clear that result.addSoruce can only be declared within the main thread. I hope it helps you, if there is a better solution please let me know.
private val result = MediatorLiveData<Resource<ResultType>>()
private val supervisorJob = SupervisorJob()
suspend fun load(): NetworkBoundResource<ResultType, RequestType> {
val context = Dispatchers.IO
context + supervisorJob
withContext(Dispatchers.Main) {
result.value = Resource.loading(null)
val dbResult = loadFromDb()
result.addSource(dbResult){data->
result.removeSource(dbResult)
if (shouldFetch(data)){
try {
CoroutineScope(context).launch {
fetchFromNetwork(dbResult)
}
}catch (e:Exception){
Timber.i("NetworkBoundResource: An error happened: $e")
result.addSource(dbResult){newData->
setValue(Resource.error(e.message!!, newData))
}
}
}else{
Timber.i("NetworkBoundResource: Return data from local database")
result.addSource(dbResult){newData->
setValue(Resource.success(newData))
}
}
}
}
return this
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With