如何在 Retorfit 中使用具有暂停乐趣的 NetworkBoundResource

Che*_*nhe 8 android retrofit2 android-livedata kotlin-coroutines

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

由于Retrofit 2.6.0引入了对暂停的内置支持,我尝试修改NetworkBoundResource使用暂停功能instaed,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...
}
Run Code Online (Sandbox Code Playgroud)

问题是result.addSource(dbSource)无法继承外部范围内的代码。我必须用来GlobalScope启动一个新的协程,这将导致“协程泄漏”,因为它不受viewModel范围控制。

我也找到了另一种方法。但这种方案违反了单一可信来源的原则,失去了核心作用NetworkBoundResource

任何想法将不胜感激。

小智 1

我也遇到了同样的问题,我的解决方案是这样的。由于这些原因,我建议您不要使用 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

}
Run Code Online (Sandbox Code Playgroud)