android - 如何在 Retrofit 中使用具有暂停乐趣的 NetworkBoundResource

标签 android retrofit2 android-livedata kotlin-coroutines

我正在使用 Retrofit、LiveData、Room (Android AAC)。 NetworkBoundResourcegooglesimple提供的网络资源和房间结合的好 helper .

由于 Retrofit 2.6.0 引入了对挂起的内置支持,我尝试修改 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启动一个新的协程,这将导致“协程泄漏”,因为它不受 viewModel 控制范围。

我还发现了another way .但是这个方案违反了single trusted source的原则。 ,失去了NetworkBoundResource的核心作用.

任何想法将不胜感激。

最佳答案

我也遇到了同样的问题,我的解决方法是这样的。我建议您不要使用 GlobalScope for these reasons .明确 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

}

关于android - 如何在 Retrofit 中使用具有暂停乐趣的 NetworkBoundResource,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57003541/

相关文章:

android - Activity 无法转换为 LifecycleOwner

android - 如何数据绑定(bind)edittext属性android :enabled with viewmodel data?

android - 通过将路径点传递到 GoogleMaps android 来创建电路路线

java - 在多项 Activity 中使用改造响应的最佳方式

java - 使用 Retrofit2 将文件上传到 AWS S3 预签名 URL

android - Retrofit 2 附加 @Body 请求

android - 没有 LiveData 的房间

android - 如何获取联系人列表的最后修改日期(添加/删除/修改)

android - 进度对话框 - WindowManager$BadTokenException : Unable to add window -- token null is not for an application

android - Activity 的视觉测试