Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@PhilippeBoisney
Created April 7, 2019 14:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PhilippeBoisney/4a5fc718a2b8d9735bdf2a5bfb37aebe to your computer and use it in GitHub Desktop.
Save PhilippeBoisney/4a5fc718a2b8d9735bdf2a5bfb37aebe to your computer and use it in GitHub Desktop.
abstract class NetworkBoundResource<ResultType, RequestType>(private val appDispatchers: AppDispatchers) {
private val result = MediatorLiveData<Resource<ResultType>>()
private val supervisorJob = SupervisorJob()
init {
setValue(Resource.loading(null))
val dbSource = this.loadFromDb()
result.addSource(dbSource) {
result.removeSource(dbSource)
if (shouldFetch(it)) {
fetchFromNetwork(dbSource)
} else {
Log.d(NetworkBoundResource::class.java.name, "Return data from local database")
result.addSource(dbSource) { setValue(Resource.success(it)) }
}
}
}
private fun fetchFromNetwork(dbSource: LiveData<ResultType>) {
Log.d(NetworkBoundResource::class.java.name, "Fetch data from network")
result.addSource(dbSource) { setValue(Resource.loading(it)) } // Dispatch latest value quickly (UX purpose)
GlobalScope.launch(appDispatchers.io + getErrorHandler() + supervisorJob) {
Log.e(NetworkBoundResource::class.java.name, "Data fetched from network")
val apiResponse = createCallAsync().await()
saveCallResults(processResponse(apiResponse))
GlobalScope.launch(appDispatchers.main) {
result.removeSource(dbSource)
result.addSource(loadFromDb()) { setValue(Resource.success(it)) }
}
}
}
fun asLiveData() = result as LiveData<Resource<ResultType>>
@MainThread
private fun setValue(newValue: Resource<ResultType>) {
if (result.value != newValue) result.value = newValue
}
@WorkerThread
protected abstract fun processResponse(response: RequestType): ResultType
@WorkerThread
protected abstract suspend fun saveCallResults(items: ResultType)
@MainThread
protected abstract fun shouldFetch(data: ResultType?): Boolean
@MainThread
protected abstract fun loadFromDb(): LiveData<ResultType>
@MainThread
protected abstract fun createCallAsync(): Deferred<RequestType>
private fun getErrorHandler() = CoroutineExceptionHandler { _, e ->
Log.e("NetworkBoundResource", "An error happened: $e")
GlobalScope.launch(appDispatchers.main) {
result.addSource(loadFromDb()) { setValue(Resource.error(e, it)) }
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment