Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Realm - Accessing Realm Object from Service

I have a realm object that is created in my activity. I need to be able to access this object within a service that I created. However I'm getting the error when creating the Realm object within the service

        mRealm = Realm.getInstance(getApplicationContext());

java.lang.IllegalStateException: Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created

Now I understand this means that because the realm object was created on my activity, I cannot access it from a background thread. However, I'm not finding an easy way around this other than creating my own custom Handler Thread but that just seems like a clunky way of doing it.

Am I missing something here or is there really no better way to be able to access Realm object from within different threads?

Update:

I dug a little deeper to figure out that in an IntentService, the onHandleIntent method runs in a separate thread than other methods within the class. Therefore, I cannot create a class level Realm instance and be able to interact with that inside and outside of the onHandleIntent method. That is what was causing the thread exception. Aside from creating a separate instance of Realm in each method I need to access the object and retrieving it over and over again, I think Ilya Tretyakov's answer will be best. I can copy the object from realm in my constructor and then work with it throughout the life of the service. Any methods that need to write back to the Realm object will need to instantiate their own Realm instance within that method.

like image 224
Landen Avatar asked Feb 18 '16 20:02

Landen


People also ask

What is a Realm object?

Realm is an open source object database management system, initially for mobile operating systems (Android/iOS) but also available for platforms such as Xamarin, React Native, and others, including desktop applications (Windows), and is licensed under the Apache License.

How do we create a Realm object?

To define a Realm object in your application, create a subclass of RealmObject or implement RealmModel.


2 Answers

You can try to use realm.copyFromRealm(youRealmObject);. These method copy Realm data into normal Java objects and detaching them from Realm.

Here is the example of usage:

youRealmObject = realm.copyFromRealm(youRealmObject);

Here is the information about it from docs:

Makes a standalone in-memory copy of an already persisted RealmObject. This is a deep copy that will copy all referenced objects. The copied object(s) are all detached from Realm so they will no longer be automatically updated. This means that the copied objects might contain data that are no longer consistent with other managed Realm objects. WARNING: Any changes to copied objects can be merged back into Realm using copyToRealmOrUpdate(RealmObject), but all fields will be overridden, not just those that were changed. This includes references to other objects, and can potentially override changes made by other threads.

https://realm.io/docs/java/latest/api/io/realm/Realm.html#copyFromRealm-E-

like image 150
Ilya Tretyakov Avatar answered Sep 28 '22 01:09

Ilya Tretyakov


Technically you're supposed to open the Realm instance at the beginning of the background thread, close it at the end of execution in that background that, and pass it to methods in between.

public void handleIntent() { // or doInBackground etc
    Realm realm = null;
    try {
         realm = Realm.getDefaultInstance();
         .... 
         MyObj obj = realm.where(MyObj.class)
                          .equalTo(MyObjFields.ID, myObjId)
                          .findFirst(); // get by id
         .... 
    } finally {
         if(realm != null) {
              realm.close(); // important 
         } 
    } 
} 

Using realm.copyFromRealm() is a workaround, not a solution.


With AS 3.0, you can actually use try-with-resources no matter what your minSDK is (just like if you were using Retrolambda):

public void handleIntent() { // or doInBackground etc
    try(Realm realm = Realm.getDefaultInstance()) {
         .... 
         MyObj obj = realm.where(MyObj.class)
                          .equalTo(MyObjFields.ID, myObjId)
                          .findFirst(); // get by id
         .... 
    } // auto-close
} 
like image 30
EpicPandaForce Avatar answered Sep 28 '22 03:09

EpicPandaForce