realm / realm-java

Realm is a mobile database: a replacement for SQLite & ORMs
http://realm.io
Apache License 2.0
11.46k stars 1.75k forks source link

Access Realm from Background Service #1910

Closed jemshit closed 8 years ago

jemshit commented 8 years ago

I am trying to get some data from Realm DB from IntentService class, which runs in background, and upload data to web service.

I have RealmManager class which has Realm instance and does CRUD on database. So i create RealmManager intance inside my background IntentService and try to query. I am getting error: Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.

I see Realm cannot be shared across threads. So what might be solution for this ? Is there any way to make CRUD on Realm from background android service, thread?

jemshit commented 8 years ago
public class BackgroundService extends IntentService {
    RealmManager realmManager;
    public BackgroundService() {
        super("BackgroundServiceConstructor");
        realmManager=new RealmManager();
    }
   ...
    @Override
    protected void onHandleIntent(Intent intent) {      
        List<ModelM> list = getDeliveryList();
       ...
    }

    public List<ModelM> getDeliveryList() {
        RealmResults<ModelM> rr = realmManager.getTransferList();
        return ...;
    }
}

public class RealmManager {
    private Realm realm;

    public RealmManager(){
        realm = Realm.getDefaultInstance();
    }

    public RealmResults<ModelM> getTransferList(){
        RealmQuery<ModelM> rq=realm.where(ModelM.class);    
        return rq.findAll();
    }
}
kneth commented 8 years ago

Currently, Realm doesn't support multiple processes but we are working on it (see #1300).

jemshit commented 8 years ago

@kneth What do you mean by multiple processes? I have 1 Application process running and 1 service running in background

cmelchior commented 8 years ago

Hi @jemshit The problem is that IntentService is multi-threaded. Your constructor runs in another thread than onHandleIntent. You need to open and close the RealmManager in your onHandleIntent method like this:

    @Override
    protected void onHandleIntent(Intent intent) {
        RealmManager manager = new RealmManager();      
        List<ModelM> list = getDeliveryList();
       ...
       manager.close();
    }
jemshit commented 8 years ago

@cmelchior Hi, thanks a lot, that did work. I have not called realm.close(); when i am working on UI thread until now. I am injecting RealmManager on Activities. Is it necessary to close, because i use injection?

cmelchior commented 8 years ago

Yes, in this case it is. The method onHandleIntent is designed to not run on the UI thread, but this also means you have to close your Realm when done with it. Because there is no start/stop methods for the IntentService worker thread you need to do everything inside onHandleIntent

jemshit commented 8 years ago

Yes i will close it on this case, but when i am using them on UI threads with injection, i think i don't need to close. I do Realm.setDefaultConfiguration(config); inside Application class and realm = Realm.getDefaultInstance(); inside realm manager. So realm object lives throughout Application lifecycle ?

cmelchior commented 8 years ago

Well, you don't need to close the Realm, but it is considered good practice as you are otherwise using a lot more memory/space when the app is put in the background, which means it might be killed faster by Android. We have some tips for handling this here: https://realm.io/docs/java/latest/#controlling-the-lifecycle-of-realm-instances