* You can read more about it in the Architecture * Guide. * @param * @param */ public abstract class NetworkBoundResource { private final AppExecutors appExecutors; private final MediatorLiveData> result = new MediatorLiveData<>(); @MainThread NetworkBoundResource(AppExecutors appExecutors) { this.appExecutors = appExecutors; result.setValue(Resource.loading(null)); LiveData 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 void setValue(Resource newValue) { if (!Objects.equals(result.getValue(), newValue)) { result.setValue(newValue); } } private void fetchFromNetwork(final LiveData dbSource) { LiveData> apiResponse = createCall(); // we re-attach dbSource as a new source, it will dispatch its latest value quickly result.addSource(dbSource, newData -> setValue(Resource.loading(newData))); result.addSource(apiResponse, response -> { result.removeSource(apiResponse); result.removeSource(dbSource); //noinspection ConstantConditions if (response.isSuccessful()) { appExecutors.diskIO().execute(() -> { saveCallResult(processResponse(response)); 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, newData -> setValue(Resource.error(response.errorMessage, newData))); } }); } protected void onFetchFailed() { } public LiveData> asLiveData() { return result; } @WorkerThread protected RequestType processResponse(ApiResponse response) { return response.body; } @WorkerThread protected abstract void saveCallResult(@NonNull RequestType item); @MainThread protected abstract boolean shouldFetch(@Nullable ResultType data); @NonNull @MainThread protected abstract LiveData loadFromDb(); @NonNull @MainThread protected abstract LiveData> createCall(); }