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

Created on 16 Nov 2016  路  6Comments  路  Source: realm/realm-java

I wrote an object in realm in getResourceData method and deleting it in onKeyExited method. But I am not able to as it is giving error in onKeyExited method. Adding object runs perfectly.
Stack trace points error at below line:
<<< if (ResourcesOnlineResults2.size() > 0) { >>>

Code Sample

  private void getResourceData(final String key, final String id) {
    final Realm mRealm = Realm.getDefaultInstance();
    mOnlineResourceDatabaseRef = FirebaseDatabase.getInstance().getReference().child(Constants.FIREBASE_CHILD_ONLINE_RESOURCE).child(mJobType).child(key);
    mOnlineResourceDatabaseRef.addValueEventListener(new ValueEventListener() {

        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            mMapResourceList.put(id, dataSnapshot.getValue(ResourceOnline.class));
            final ResourceOnline resourceOnline = dataSnapshot.getValue(ResourceOnline.class);
            mRealm.executeTransactionAsync(new Realm.Transaction() {
                @Override
                public void execute(Realm realm) {
                    realm.copyToRealmOrUpdate(resourceOnline);
                }
            });
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    });
    mRealm.close();
}

@Override
public void onKeyExited(String key) {
    Realm realm = Realm.getDefaultInstance();
    // Remove any old marker
    Marker marker = this.mMapMarkers.get(key);
    if (marker != null) {
        final ResourceOnline resourceOnline = mMapResourceList.get(marker.getId());
        final RealmResults<ResourceOnline> ResourcesOnlineResults2 = realm.where(ResourceOnline.class).equalTo("resourceId",resourceOnline.getResourceId()).findAllAsync();

        realm.executeTransactionAsync(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
                if (ResourcesOnlineResults2.size() > 0) {
                    ResourcesOnlineResults2.deleteAllFromRealm();
                }else{
                    Log.v("wai", "onKeyExited Realmsize is zero ");
                }
            }
        });
        marker.remove();
        this.mMapMarkers.remove(key);
        mMapResourceList.remove(marker.getId());
    }
    realm.close();
}

Actual Results

java.lang.IllegalStateException: Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.
at io.realm.BaseRealm.checkIfValid(BaseRealm.java:383)
at io.realm.RealmResults.isLoaded(RealmResults.java:913)
at io.realm.RealmResults.size(RealmResults.java:413)
at com.waiapp.Booking.MapViewFragment$4.execute(MapViewFragment.java:420)
at io.realm.Realm$2.run(Realm.java:1351)
at io.realm.internal.async.BgPriorityRunnable.run(BgPriorityRunnable.java:34)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:423)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)

Version of Realm and tooling

Realm version(s): 2.2.0

Realm sync feature enabled: no

Android Studio version: 2.2.0

Which Android version and device: 6.0.1 and moto g2

T-Help

Most helpful comment

You need to load the data inside the transaction:

        realm.executeTransactionAsync(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
final ResourceOnline resourceOnline = mMapResourceList.get(marker.getId());
final RealmResults<ResourceOnline> ResourcesOnlineResults2 = realm.where(ResourceOnline.class).equalTo("resourceId",resourceOnline.getResourceId()).findAllAsync();

                if (ResourcesOnlineResults2.size() > 0) {
                    ResourcesOnlineResults2.deleteAllFromRealm();
                }else{
                    Log.v("wai", "onKeyExited Realmsize is zero ");
                }
            }
        });

Otherwise they are loaded on the wrong thread

All 6 comments

You need to load the data inside the transaction:

        realm.executeTransactionAsync(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
final ResourceOnline resourceOnline = mMapResourceList.get(marker.getId());
final RealmResults<ResourceOnline> ResourcesOnlineResults2 = realm.where(ResourceOnline.class).equalTo("resourceId",resourceOnline.getResourceId()).findAllAsync();

                if (ResourcesOnlineResults2.size() > 0) {
                    ResourcesOnlineResults2.deleteAllFromRealm();
                }else{
                    Log.v("wai", "onKeyExited Realmsize is zero ");
                }
            }
        });

Otherwise they are loaded on the wrong thread

@cmelchior the findAllAsync() should be findAll() for background thread synchronous query to delete result set

        realm.executeTransactionAsync(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
                final RealmResults<ResourceOnline> ResourcesOnlineResults2 = realm.where(ResourceOnline.class)
                                 .equalTo("resourceId",resourceOnline.getResourceId())
                                 .findAll();
                if (ResourcesOnlineResults2.size() > 0) {
                    ResourcesOnlineResults2.deleteAllFromRealm();
                }else{
                    Log.v("wai", "onKeyExited Realmsize is zero ");
                }
            }
        });
}

I kinda hope that resourceOnline isn't a managed RealmObject, because then it would be

    final String resourceId = resourceOnline.getResourceId();
    realm.executeTransactionAsync(new Realm.Transaction() {
        @Override
        public void execute(Realm realm) {
            final RealmResults<ResourceOnline> ResourcesOnlineResults2 = realm.where(ResourceOnline.class)
                             .equalTo("resourceId", resourceId)

@cmelchior Thanks problem is resolved but I didnt understand why I need to load data inside Transaction method.
@Zhuinden Yes resourceOnline is a managed RealmObject. Can you please tell me more when to do Async or synchronous queries.

Is there any active slack group for realm I can join.

@kevivforever there is a slack, but it didn't look active when I was there.

But in that case, this is the right way:

final String resourceId = resourceOnline.getResourceId();
realm.executeTransactionAsync(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        final RealmResults<ResourceOnline> ResourcesOnlineResults2 = realm.where(ResourceOnline.class)
                         .equalTo("resourceId", resourceId)
                         .findAll();
        //...

You need to query the data inside the transaction, because async() means that transaction block will run on another thread. So trying to access data from the original thread will break the Realm threading model, which doesn't allow reading Realm data across multiple threads.

Closing

Thanks for all the help. Sorry for not closing it on time.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mithrann picture mithrann  路  3Comments

wyvern610 picture wyvern610  路  3Comments

jjorian picture jjorian  路  3Comments

CNyezi picture CNyezi  路  3Comments

wezley98 picture wezley98  路  3Comments