web-dev-qa-db-fra.com

NetworkBoundResource avec les coroutines Kotlin

Avez-vous des idées sur la façon d'implémenter un modèle de référentiel avec NetworkBoundResource et les coroutines Kotlin? Je sais que nous pouvons lancer une coroutine avec un GlobalScope, mais cela peut entraîner une fuite de coroutine. Je voudrais passer un viewModelScope comme paramètre, mais c'est un peu délicat, quand il s'agit d'implémentation (parce que mon référentiel ne connaît pas de CoroutineScope de tout ViewModel).

abstract class NetworkBoundResource<ResultType, RequestType>
@MainThread constructor(
    private val coroutineScope: CoroutineScope
) {

    private val result = MediatorLiveData<Resource<ResultType>>()

    init {
        result.value = Resource.loading(null)
        @Suppress("LeakingThis")
        val dbSource = loadFromDb()
        result.addSource(dbSource) { data ->
            result.removeSource(dbSource)
            if (shouldFetch(data)) {
                fetchFromNetwork(dbSource)
            } else {
                result.addSource(dbSource) { newData ->
                    setValue(Resource.success(newData))
                }
            }
        }
    }

    @MainThread
    private fun setValue(newValue: Resource<ResultType>) {
        if (result.value != newValue) {
            result.value = newValue
        }
    }

    private fun fetchFromNetwork(dbSource: LiveData<ResultType>) {
        val apiResponse = createCall()
        result.addSource(dbSource) { newData ->
            setValue(Resource.loading(newData))
        }
        result.addSource(apiResponse) { response ->
            result.removeSource(apiResponse)
            result.removeSource(dbSource)
            when (response) {
                is ApiSuccessResponse -> {
                    coroutineScope.launch(Dispatchers.IO) {
                        saveCallResult(processResponse(response))

                        withContext(Dispatchers.Main) {
                            result.addSource(loadFromDb()) { newData ->
                                setValue(Resource.success(newData))
                            }
                        }
                    }
                }

                is ApiEmptyResponse -> {
                    coroutineScope.launch(Dispatchers.Main) {
                        result.addSource(loadFromDb()) { newData ->
                            setValue(Resource.success(newData))
                        }
                    }
                }

                is ApiErrorResponse -> {
                    onFetchFailed()
                    result.addSource(dbSource) { newData ->
                        setValue(Resource.error(response.errorMessage, newData))
                    }
                }
            }
        }
    }
}
8
Kamil Szustak

Je suis nouveau chez Kotlin Coroutine. Je viens de rencontrer ce problème cette semaine.

Je pense que si vous optez pour le modèle de référentiel comme mentionné dans le post ci-dessus, mon opinion se sent libre de passer un CoroutineScope dans le NetworkBoundResource. CoroutineScope peut être l'un des paramètres de la fonction dans Repository, qui retourne un LiveData, comme:

suspend fun getData(scope: CoroutineScope): LiveDate<T>

Passez la portée intégrée viewmodelscope comme CoroutineScope lors de l'appel getData () dans votre ViewModel, donc NetworkBoundResource fonctionnera dans la viewmodelscope et être lié au cycle de vie du Viewmodel. La coroutine dans NetworkBoundResource sera annulée lorsque ViewModel est mort, ce qui serait un avantage.

Pour utiliser la portée intégrée viewmodelscope, n'oubliez pas d'ajouter ci-dessous dans votre build.gradle.

implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0-alpha01'
0
Freddie