如何在Retorfit中使用NetworkBoundResource与suspend fun。

问题描述 投票:5回答:1

我使用的是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.

任何想法将被感激。

android retrofit2 android-livedata kotlin-coroutines
1个回答
0
投票

我也有同样的问题,我的解决方法是这样的。我建议你不要使用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

}
© www.soinside.com 2019 - 2024. All rights reserved.