我使用的是Retrofit,LiveData,Room(Android AAC)。NetworkBoundResource
是一个很好的帮手,结合网络源和房间提供的。简易.
由于Retrofit 2.6.0引入了对suspend的内置支持,我试着修改了以下内容 NetworkBoundResource
暂停功能的使用。LiveDataCallAdapter
但遇到了很多麻烦。
这是我的修改。
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...
}
问题是代码在 result.addSource(dbSource)
无法继承外部作用域。我必须使用 GlobalScope
启动一个新的coroutine,这将导致 "Coroutine Leak",因为它不受控制。viewModel
范围。
此外,我还发现 另一路. 但这一方案违反了 "一刀切 "的原则。单一信源丧失了核心作用 NetworkBoundResource
.
任何想法将被感激。
我也有同样的问题,我的解决方法是这样的。我建议你不要使用GlobalScope 基于这些原因. 明确result.addSoruce只能在主线程内声明。希望对你有所帮助,如果有更好的解决方案请告诉我。
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
}