4

Setup

I retrieve data from a REST API using Retrofit. Instead of Retrofit’s usual response, I use RxJava’s Observable. In the onNext(Object obj) callback method I tell the view to show markers on a map for each item I receive over the API. Additionally, I want to save each entry to a Realm database.

Problem

Saving to the database works. However, it blocks the UI for quite a while, sometimes leading to up to 1000 skipped frames. Without the Realm part, showing all the markers—although over 3000 at a time (I cluster them for performance and usability reasons) takes only fractions of a second. With it, though, the app seems to freeze for seconds.

Saving to the database works, though—the Realm gets populated with all entries. But it’s way too slow.

Implementation

(I did some renaming and shortened the code before pasting it here to just show what I think might be the problematic part.)

MapsPresenter.java

class MapsPresenter implements Presenter<MapsView> {

    private MapsView mapsView;

    private Subscription subscription;

    private MyApiClient apiClient;
    private Realm realm;

    MapsPresenter() {
        apiClient = MyApp.getMyApiClient();
        realm = RealmController.with(MyApp.getInstance()).getRealm();
    }


    void loadRetailers(...) {
        if (subscription != null && !subscription.isUnsubscribed()) {
            subscription.unsubscribe();
        }

        mapsView.showProgressIndicator();

        subscription = apiClient.retailers()
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(Schedulers.io())
            .flatMap(new Func1<Retailers, Observable<Retailer>>() {
                @Override
                public Observable<Dealer> call(Retailers retailers) {
                    return Observable.from(retailers.retailerList);
                }
            })
            .subscribe(new Subscriber<Dealer>() {
                @Override
                public void onCompleted() {
                    ...
                }

                @Override
                public void onError(Throwable e) {
                    ...
                }

                @Override
                public void onNext(Retailer retailer) {
                    realm.beginTransaction();
                    realm.insertOrUpdate(retailer);
                    realm.commitTransaction();
                }
            });
        }
    }
}

RealmController.java

public class RealmController {

    private static RealmController instance;
    private final Realm realm;

    public RealmController(Application application) {
        realm = Realm.getDefaultInstance();
    }

    public static RealmController with(Application application) {

        if (instance == null) {
            instance = new RealmController(application);
        }
        return instance;
    }

    public static RealmController getInstance() {

        return instance;
    }

    public Realm getRealm() {

        return realm;
    }

    ...
}

Question

How can I improve the speed of inserting/updating the Realm entries, even if there are a couple hundred operations following an API call? Is there something I’m overlooking?

PattaFeuFeu
  • 1,828
  • 3
  • 19
  • 24

1 Answers1

4

First of all, really liked the way you have structured the question.

  1. Your flatMap code should be above your subscribeOn to make it run on the Scheduler given in observeOn. Right now that code is running on MainThread.

  2. You are doing Realm write operation on MainThread, which is fine but you are not using Aync method given by Realm, try executing write operation within executeTransactionAsync block.

  3. This one is not related to the problem you are facing but the way you are handling subscription in your Presenter can be improved.

A sample example looks like this,

public void fetchGitHubUsersFromRetrofit() {
  internetConnection.isInternetOn(context)
    .filter(connectionStatus -> connectionStatus)
    .switchMap(connectionStatus -> gitHubApiInterface.getGitHubUsersList())
    .subscribeOn(rxSchedulerConfiguration.getComputationThread())
    .observeOn(rxSchedulerConfiguration.getMainThread())
    .map(gitHubUserList -> {
      realm.executeTransactionAsync(realm -> realm.copyToRealmOrUpdate(gitHubUserList));
    });
}

If you are looking for a detailed code reference, have a look at this https://github.com/viraj49/Realm_android-injection-rx-test

Viraj Tank
  • 1,285
  • 11
  • 14
  • RxJava and Realm are still quite new to me—I’m using them in a project to learn how to use those libraries, among others. Dependency Injection and Lambdas are on my list. In the end it turned out that I don’t even need the flatMap operation anymore. So your tips plus EpicPandaForce’s comment helped me. I will look into item 3 on your list. Thanks! – PattaFeuFeu Jul 24 '16 at 18:38