Renaud Favier Renaud Favier - 1 month ago 20
Android Question

Realm instances and multithreading

I'm trying to code something to get offline some content that could be watched later. This content is made of jsons, images, vidéos and pdfs :

{
"elements": [
{
"id":"3b4c4f3da8bf9d1527010c5242e037b7",
"type":"media"
},
...
],
"id":"58088318ef0b4832f6c0e70b",
"content": "Hello World"
}


So basically my problem is that I switch between async network calls and realm db updates, and I can't figure out how to build it well.

I fist fetch the above Json must store it in realm, then I call a second route for each element to get
DetailedElement
and store it aswell. when an element contains a downloadable document I must dowload it and add its path as a member of
DetailedElement
. Thing is, I can't get my threads right.

I'd idealy like a method with this signature :

FooBar.prefetch(Context ctx, String id, Callback cb)


My first step should be to ensure it's launched in a non-UI Looper thread : I'm not sure how to do it

public static void Bar(final Context ctx, final String id) {
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
final Realm realm = Realm.getDefaultInstance();
final Handler handler = new Handler();

MainThingToDownload mainThingToDownload = realm.where(MainThingToDownload.class).equalTo("id", id).findFirst();
if (mainThingToDownload != null) {
processMain(mainThingToDownload, realm, handler);
} else {
Api.getInstance().backendRepresentation.getMainThing(id).enqueue(new CustomRetrofitCallBack<>(null) {
@Override
public void onResponseReceived(final MainThingToDownload mainThingToDownload) {

handler.post(new Runnable() {
@Override
public void run() {
realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
realm.copyToRealmOrUpdate(mainThingToDownload);
processMain(mainThingToDownload, realm, handler);
}
});
}
});
}
});
}
}
});
}


Is it the right approach ? keeping the realm reference and the handler reference for its thread. And then I always do in my network callbacks something like that :

handler.post(new Runnable() {
@Override
public void run() {
realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
//... add downloaded object to realm or modifies one
}
});
}
});


I had lot of different errors with my code. Most of the time I don't use Realm on the thread where it should be used on and sometimes I use a realm instance that has already been closed.

Is this how I'm suppose to do it ?
Thanks,

Answer

Well, the best solution in this case - use functional reactive programming (rxJava library for android). By doing so, you can create trigger, emitting event every time your data is updated (by using PublishSubject) and handle concurrency easily. If you do so, avod using async.. methods, because synchronization will be done by redirecting event flow inside rx 'pipelines'. Realm can't access objects from another thread if those objects were created from another thread, so, for multithreaded environment, allocate new realm instance, perform transaction and release instance. This is not mandatory, you can still use async method, but it will make interaction a bit more comprehensive.

Comments