Reputation: 5761
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
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