Skip to content

Instantly share code, notes, and snippets.

@ntakouris
Created October 8, 2017 16:13
Show Gist options
  • Save ntakouris/93605cb7a0d50e59ba200c115eb8f8a9 to your computer and use it in GitHub Desktop.
Save ntakouris/93605cb7a0d50e59ba200c115eb8f8a9 to your computer and use it in GitHub Desktop.
package com.eventora.mobile.app.util;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MediatorLiveData;
import android.arch.lifecycle.MutableLiveData;
import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import com.eventora.mobile.app.AppExecutors;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
/**
* Created by Zarkopafilis on 10/8/2017.
*/
public abstract class NetworkBoundData<T> {
private final AppExecutors appExecutors;
private final MediatorLiveData<T> result = new MediatorLiveData<>();
public NetworkBoundData(AppExecutors appExecutors) {
this.appExecutors = appExecutors;
LiveData<T> dbSource = loadFromDb();
result.addSource(dbSource, data -> {
result.removeSource(dbSource);
if (shouldFetch(data)) {
fetchFromNetwork(dbSource);
} else {
result.addSource(dbSource, newData -> setValue(newData));
}
});
}
@MainThread
private void setValue(T newValue) {
if (!Objects.equals(result.getValue(), newValue)) {
result.setValue(newValue);
}
}
private void fetchFromNetwork(final LiveData<T> dbSource) {
Call<T> call = createCall();
// we re-attach dbSource as a new source, it will dispatch its latest value quickly
result.addSource(dbSource, newData -> setValue(newData));
call.enqueue(new Callback<T>() {
@Override
public void onResponse(Call<T> call, Response<T> response) {
if (isResponseValid(response)) {
MutableLiveData<T> networkSource = new MutableLiveData<>();
networkSource.setValue(response.body());
result.addSource(networkSource, newData -> {
result.removeSource(networkSource);
result.removeSource(dbSource);
//noinspection ConstantConditions
appExecutors.diskIO().execute(() -> {
saveCallResult(processResponse(newData));
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(),
otherNewData -> setValue(otherNewData))
);
});
});
}
}
@Override
public void onFailure(Call<T> call, Throwable t) {
onFetchFailed();
result.addSource(dbSource,
newData -> setValue(newData));
}
});
}
protected void onFetchFailed() {
}
public LiveData<T> asLiveData() {
return result;
}
@WorkerThread
protected T processResponse(T response) {
return response;
}
@WorkerThread
protected abstract void saveCallResult(@NonNull T item);
@MainThread
protected abstract boolean shouldFetch(@Nullable T data);
@NonNull
@MainThread
protected abstract LiveData<T> loadFromDb();
@NonNull
@MainThread
protected abstract Call<T> createCall();
@NonNull
@MainThread
protected abstract boolean isResponseValid(Response<T> call);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment