3

App uses RxJava to make network call, then modify results with data from database and display them. App listens to database changes with android.database.ContentObserver and modify data when there is change. Currently it works with code below but is there some nicer RX way how to achieve same? Thanks!

Observable<ArrayList<Foo>> observable = Observable.create(new Observable.OnSubscribe<ArrayList<Foo>>() {

            @Override
            public void call(Subscriber<? super ArrayList<Foo>> subscriber) {
                //make api call and get list of foos
                ArrayList<Foo> apiResults = api.getFooList();
                //loop results and if foo is already in local sqlite db, update it with local values
                for (Foo foo : apiResults) {
                    if(localSqlite.contains(foo){
                        foo.update(localSqlite.get(foo));
                    }
                }

                subscriber.onNext(apiResults);

                HandlerThread observerThread = new HandlerThread("ContentObserver-thread");
                observerThread.start();
                final ContentObserver co = new ContentObserver(new Handler(observerThread.getLooper())) {
                        @Override
                        public void onChange(boolean selfChange) {
                            super.onChange(selfChange);

                            for (Foo foo : apiResults) {
                               if(localSqlite.contains(foo){
                               foo.update(localSqlite.get(foo));
                            }

                            subscriber.onNext(apiResults);
                }

                    mContext.getContentResolver().registerContentObserver(uri, true, co);

                    subscriber.add(Subscriptions.create(new Action0() {
                        @Override
                        public void call() {
                mContext.getContentResolver().unregisterContentObserver(co);
                        }
                    }));

            }


Subscription subscription = observable
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(subscriber);
Martin Vandzura
  • 3,047
  • 1
  • 31
  • 63

1 Answers1

1

box's suggestion to investigate SqlBrite is probably the best course of action, but if you don't have time to learn a new API here is a cool technique that I found.

If you wrap each database query result in a behavior subject, you can cache it in a hashmap using its URI as a key. Then when you override the Content Observer's onChange method, you can easily push a refreshed object downstream:

final protected MaxSizeHashMap<Uri, Subject<T, T>> subjectMap = new MaxSizeHashMap<>(cacheSize / 300);

final private ContentObserver contentObserver = new ContentObserver(new Handler()) {
        @Override
        public void onChange(boolean selfChange, Uri uri) {
            super.onChange(selfChange, uri);

            if (subjectMap.containsKey(uri)) {
                subjectMap.get(uri).onNext(query(uri));
            }
        }
    };

Then in your get method, build the object, wrap it in a behavior subject and add it to the map before returning the observable:

public Observable<T> getFoo(U id) {
    Uri uri = getUriForId(id);

    if (!subjectMap.containsKey(uri)) {
        subjectMap.put(uri, BehaviorSubject.create(query(id)));
    }

    return subjectMap.get(uri);
}
ajplumlee33
  • 321
  • 3
  • 6