如何在Retorfit中使用NetworkBoundResource进行暂停乐趣

时间:2019-07-12 08:55:27

标签: android retrofit2 android-livedata kotlin-coroutines

我正在使用翻新,LiveData,会议室(Android AAC)。 NetworkBoundResource是整合googlesimple提供的网络资源和空间的绝佳帮手。

由于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的核心作用。

任何想法都会受到赞赏。

2 个答案:

答案 0 :(得分:1)

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

}

答案 1 :(得分:0)

无需向NetworkBoundResource本身添加协程功能。

您可以在重写函数时调用协程,例如:

fun getList(
    coroutineScope: CoroutineScope
): LiveData<Resource<List<Content>>> {
    return object : NetworkBoundResource<List<Content>, List<Content>>(coroutineScope) {
        override fun onFetchFailed() {
            super.onFetchFailed()
        }

        override fun saveCallResult(items: List<Content>) {
            coroutineScope.launch {
                contentDao.addList(items)
            }
        }

        override fun shouldFetch(data: List<Content>?): Boolean {
            return true
        }

        override fun loadFromDb(): LiveData<List<Content>> {
                contentDao.loadSportContents(sport)
        }

        override fun createCall(): LiveData<Resource<List<Content>>> {
            return pullSportContentFromServer(coroutineScope)
        }
    }.asLiveData()
}