Created
February 25, 2018 05:24
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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>> | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
If I only want network data, how should I modify this class?