StackOverflower
StackOverflower

Reputation: 5761

What's the correct way to deal with multithreading and Realm?

On my Android application I have a data access layer where I have the following property on each data store class

Realm realm = Realm.getDefaultInstance();

My problema is when I try to call any data store method from a different thread. Then I get

Realm objects can only be accessed on the thread they were created.

I've read that I should be creating a new Realm instance on the new thread. Problem is that data access layer is no thread aware, it doesn't know if it's called from main or separated and it seems to me it would smell adding logic to check that.

I was checking different questions here and issues filled on github but it's not clear for me what's the oficial way to handle a situation like this. I don't think I'm dealing with a strange scenario...

EDIT

My arquitecture is Fragment containing a Presenter whic contains a DataStore.

I detected a very long running process so I moved it to a separated thread like this on presenter

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        // Events is a collection of DTOs, they are not Realm objects (converted from Realm object to DTO inside dataStore)
        events = dataStore.getEvents(filterTerm);

        view.getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (events.size() > 0) {
                    view.showEvents(events);
                }
            }
        });
    }
});

thread.start();

EDIT

Adding 3 queries executed in a row. They are called from 3 different methods

Query 1

final RealmResults<Event> results = query.findAllAsync();
results.addChangeListener(new RealmChangeListener() {
    @Override
    public void onChange() {
        ....
    }
});

Query 2

final RealmResults<T> result = realm.where(type).findAllAsync();
result.addChangeListener(new RealmChangeListener() {
    @Override
    public void onChange() {
        ...
    }
});

Query 3

final RealmResults<PresentationSpeaker> results = query.findAllAsync();
results.addChangeListener(new RealmChangeListener() {
    @Override
    public void onChange() {
        ...
    }
});

Upvotes: 4

Views: 2207

Answers (1)

Christian Melchior
Christian Melchior

Reputation: 20126

It is normally custom to let your DataStore be asynchronous to avoid exactly the problems you run into now: That the operation are taking to long, and you have to create custom threads and use postRunnable. This should be something the DataStore is concerned about, not your Presenter and UI which should run on the UI thread. One solution is to let your Datastore be asynchronous instead, either by implementing some observer pattern yourself or use RxJava. Then you could use Realms async API's (https://realm.io/docs/java/latest/#asynchronous-queries) to do the following:

// DataStore
public class DataStore {

   private final Realm realm;

   public DataStore() {
     realm = Realm.getDefaultInstance(); // Called on the UI thread
   }

    public void getEvents(EventsLoadedListener listener) {
      RealmResults<Event> results = realm.where(Events.class).findAllAsync();
      results.addChangeListener(new RealmChangeListener() {
        public void onChange() {
          if (listener != null) {
            listener.eventsLoaded(results);
          }
        }
      });
    }

    public interface EventsLoadedListener {
      public void eventsLoaded(List<Event> events);
    }

}

// UI code
datastore.getEvents(new DataStore.EventsLoadedListener() {
    public void eventsLoaded(List<Event> events) {
      view.showEvents(events);
    }
})

The above code can be massively simplified using RxJava, so I would really encourage you to look into that. Realm also supports RxJava natively: https://realm.io/docs/java/latest/#rxjava

Upvotes: 3

Related Questions