Handling Data Requests in Open Event Organizer Android App
Open Event Organizer is a client side application of Open Event API Server created for event organizers and entry managers. The app maintains a local database and syncs it with the server when required. I will be talking about handling data requests in the app in this blog.
The app uses ReactiveX for all the background tasks including data accessing. When a user requests any data, there are two possible ways the app can perform. The one where app fetches the data directly from the local database maintained and another where it requests data from the server. The app has to decide one of the ways. In the Organizer app, AbstractObservableBuilder class takes care of this. The relevant code is:
final class AbstractObservableBuilder<T> { private final IUtilModel utilModel; private boolean reload; private Observable<T> diskObservable; private Observable<T> networkObservable; ... ... @NonNull private Callable<Observable<T>> getReloadCallable() { return () -> { if (reload) return Observable.empty(); else return diskObservable .doOnNext(item -> Timber.d("Loaded %s From Disk on Thread %s", item.getClass(), Thread.currentThread().getName())); }; } @NonNull private Observable<T> getConnectionObservable() { if (utilModel.isConnected()) return networkObservable .doOnNext(item -> Timber.d("Loaded %s From Network on Thread %s", item.getClass(), Thread.currentThread().getName())); else return Observable.error(new Throwable(Constants.NO_NETWORK)); } @NonNull private <V> ObservableTransformer<V, V> applySchedulers() { return observable -> observable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } @NonNull public Observable<T> build() { if (diskObservable == null || networkObservable == null) throw new IllegalStateException("Network or Disk observable not provided"); return Observable .defer(getReloadCallable()) .switchIfEmpty(getConnectionObservable()) .toList() .flatMap(items -> diskObservable.toList()) .flattenAsObservable(items -> items) .compose(applySchedulers()); } }
DiskObservable is a data request to the local database and networkObservable is a data request to the server. The build function decides which one to use and returns a correct observable accordingly. The class object takes a boolean field reload which is used to decide which observable to subscribe. If reload is true, that means the user wants data from the server, hence networkObservable is returned to subscribe. Also switchIfEmpty in the build method checks whether the data fetched using diskObservable is empty, if found empty it switches the observable to the networkObservable to subscribe.
This class object is used for every data access in the app. For example, this is a code snippet of the gettEvents method in EventRepository class.
@Override public Observable<Event> getEvents(boolean reload) { Observable<Event> diskObservable = Observable.defer(() -> databaseRepository.getAllItems(Event.class) ); Observable<Event> networkObservable = Observable.defer(() -> eventService.getEvents(JWTUtils.getIdentity(getAuthorization())) ... ... .flatMapIterable(events -> events)); return new AbstractObservableBuilder<Event>(utilModel) .reload(reload) .withDiskObservable(diskObservable) .withNetworkObservable(networkObservable) .build(); }
Links:
1. Documentation of ReactiveX API
2. Github repository link of RxJava – Reactive Extension for JVM