Skip to content

Instantly share code, notes, and snippets.

@AkshayChordiya
Created February 25, 2018 05:24
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 AkshayChordiya/a79bfcc422fd27d52b15cdafc55eac6b to your computer and use it in GitHub Desktop.
Save AkshayChordiya/a79bfcc422fd27d52b15cdafc55eac6b to your computer and use it in GitHub Desktop.
A generic class that can provide a resource backed by both the SQLite database and the network
/**
* A generic class that can provide a resource backed by both the sqlite database and the network.
*
*
* You can read more about it in the [Architecture
* Guide](https://developer.android.com/arch).
*
* @param <ResultType>
* @param <RequestType>
</RequestType></ResultType> */
abstract class NetworkBoundResource<ResultType, RequestType> @MainThread constructor(
private val appExecutors: AppExecutors
) {
/**
* The final result LiveData
*/
private val result = MediatorLiveData<Resource<ResultType?>>()
init {
// Send loading state to UI
result.value = Resource.loading()
val dbSource = this.loadFromDb()
result.addSource(dbSource) { data ->
result.removeSource(dbSource)
if (shouldFetch(data)) {
fetchFromNetwork(dbSource)
} else {
result.addSource(dbSource) { newData -> setValue(Resource.success(newData)) }
}
}
}
/**
* Fetch the data from network and persist into DB and then
* send it back to UI.
*/
private fun fetchFromNetwork(dbSource: LiveData<ResultType>) {
val apiResponse = createCall()
// we re-attach dbSource as a new source, it will dispatch its latest value quickly
result.addSource(dbSource) { result.setValue(Resource.loading()) }
result.addSource(apiResponse) { response ->
result.removeSource(apiResponse)
result.removeSource(dbSource)
response?.apply {
if (isSuccessful) {
appExecutors.diskIO().execute {
processResponse(this)?.let { requestType -> saveCallResult(requestType) }
appExecutors.mainThread().execute {
// we specially request a new live data,
// otherwise we will get immediately last cached value,
// which may not be updated with latest results received from network.
result.addSource(loadFromDb()) { newData -> setValue(Resource.success(newData)) }
}
}
} else {
onFetchFailed()
result.addSource(dbSource) { result.setValue(Resource.error(errorMessage)) }
}
}
}
}
@MainThread
private fun setValue(newValue: Resource<ResultType?>) {
if (result.value != newValue) result.value = newValue
}
protected fun onFetchFailed() {}
fun asLiveData(): LiveData<Resource<ResultType?>> {
return result
}
@WorkerThread
private fun processResponse(response: ApiResponse<RequestType>): RequestType? {
return response.body
}
@WorkerThread
protected abstract fun saveCallResult(item: RequestType)
@MainThread
protected abstract fun shouldFetch(data: ResultType?): Boolean
@MainThread
protected abstract fun loadFromDb(): LiveData<ResultType>
@MainThread
protected abstract fun createCall(): LiveData<ApiResponse<RequestType>>
}
@Liar1995
Copy link

Liar1995 commented Nov 2, 2018

If I only want network data, how should I modify this class?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment