Realm-java: have data but FindFirst return null

Created on 19 Jan 2017  ·  28Comments  ·  Source: realm/realm-java

Not necessarily, but the probability is very high
111111111

T-Help

Most helpful comment

Also consider reading the docs on Best Practices regarding Realm Lifecycle Management although fragment should use onCreateView() and onDestroyView()

All 28 comments

You don't close your Realm instance in case you obtain null in which case your thread-local Realm instance will have its version retained and will not be able to be updated to a newer version of the Realm that would contain the newly added data

Solution, make sure you close the Realm instance in finally block

Consider the possibility of using

realm.beginTransaction() ;
/* query item */
if(/*item does not exist*/)  {
    realm.cancelTransaction() ;
    return;

@Zhuinden I'm cooperate with @henjue The condition as the following:

  1. the realm database has the data which id is:xxxx
  2. When we using the following query condition,
    MeasureObject first = instance.where(MeasureObject.class).equalTo("id", "xxxx").findFirst();

It'll have high properbility that the first will get null. when we didn't nothing, and do the same operation again, the result will not null. Very strange.
The realm version: v2.2.2

@flzyup not strange at all, you are on a non-looping background thread and you don't always close your Realm instance, which means it'll get stuck at an older version. For every call to Realm.getDefaultInstance(), there must be a call to Realm.close().

Solutions:

1.) make sure you always close the Realm using

 try(Realm instance = Realm.getDefaultInstance()) {
 }

or

 Realm instance = null;
 try { 
     instance = Realm.getDefaultInstance();
     ....
} finally {
    if(instance != null) {
         instance.close();
    }
}

2.) force the Realm to a newer version before you evaluate the condition

You have two options:

a.) force a Realm refresh via http://stackoverflow.com/a/38839808/2413303

b.) create a transaction before you do the query, but if the element is null, then use realm.cancelTransaction()

 Realm instance = null;
 try { 
     instance = Realm.getDefaultInstance();
     realm.beginTransaction();
     MeasureObject first = instance.where(MeasureObject.class).equalTo("id", "xxxx").findFirst();
     if(first == null) {
         realm.cancelTransaction();
         return;
     }
     ....
     realm.commitTransaction();
     ....
} catch(Throwable e) {
    if(instance != null && instance.isInTransaction()) {
         instance.cancelTransaction();
    }
    throw e;
} finally {
    if(instance != null) {
         instance.close();
    }
}

@Zhuinden Oh... We using some Realm instance at Background service in Android app. We get the Realm instance at onCreate() method and close it at onDestroy() methods. So that's the problem? We should use the realm instance by getDefaultInstance() and close it when we done?

But the code where query got null we are using is the right way: get it and close it and didn't hold it. So if we do as above, it'll get the problem? right?
Realm instance = Realm.getDefaultInstance(); MeasureObject first = instance.where(MeasureObject.class).equalTo("id", "xxx").findFirst(); instance.close();

So if using the realm instance wrong at the other points, it will also affect this code?

@flzyup if you do this error you're making in the above code snippet which is:

Realm instance = Realm.getDefaultInstance();
if(/*blah*/) {
    return; // here, instance is still open!
}

Then yes, for that given thread, you end up retaining the Realm instance, and any later time you call Realm.getDefaultInstance() you'll still be seeing an old Realm unless you force a refresh or you start a transaction.

@Zhuinden We'll try to update the code and give a feedback later.

@Zhuinden We've tried to modify our code in the project as you mentioned earlier. We got the instance and close it after we've done our work. But still have the same bug: the query got null most of the time especially at first operation. So what is the real problem?

@flzyup I need to see more of the modified code to tell if it's actually fixed

image
image

Is this useful for u? This is where the query got null, that is i have check if "first == null" or not.

@Zhuinden

A.java

write

    public void onCreateMeasure(final MeasureObject info) {
        Realm.getDefaultInstance().executeTransactionAsync(new Transaction() {
            @Override
            public void execute(Realm realm) {
                OffsetDateTime value = Instant.ofEpochMilli(info.getCreatedatetime().getTime()).atOffset(ZoneOffset.UTC);
                MeasureLibObject lib = realm.where(MeasureLibObject.class).equalTo("shortDate", value.format(DateTimeFormatter.ofPattern("yyyyMMdd"))).findFirst();
                if (lib == null) {
                    lib = realm.copyToRealm(new MeasureLibObject());
                }
                info.setDeviceId(device_id);
                info.setyStart(0);
                info.setyEnd(0);
                info.setLibId(lib.getId());
                realm.copyToRealmOrUpdate(info);
                newId = info.getId();
            }
        }, new Transaction.OnSuccess() {
            @Override
            public void onSuccess() {
                mContext.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        LogUtils.d("uu", "start activity id = %s", newId);
                        startActivityForResult(new Intent(mContext, MeasureActivity.class).putExtra(MeasureActivity.EXTRA_ID, newId), REQUEST_MEASURE);
                        newId = null;
                    }
                },100);
            }
        }, new Transaction.OnError() {
            @Override
            public void onError(Throwable error) {
                error.printStackTrace();
            }
        });
    }

MeasureActivity.java

query

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Observable.create(new Observable.OnSubscribe<Void>() {
            @Override
            public void call(final Subscriber<? super Void> subscriber) {
                Realm instance = Realm.getDefaultInstance();
                LogUtils.d("uu", "obj id = %s", MeasureViewModel.this.id);
                MeasureObject first = instance.where(MeasureObject.class).equalTo("id", MeasureViewModel.this.id).findFirst();
                //this is return null
                if(first==null){
                    subscriber.onError(new NotFoundException("请重试"));
                    return;
                }

Realm.getDefaultInstance().executeTransactionAsync(

That Realm instance is never closed, you lose the reference provided by Realm.getDefaultInstance()

And

            Realm instance = Realm.getDefaultInstance();
            LogUtils.d("uu", "obj id = %s", MeasureViewModel.this.id);
            MeasureObject first = instance.where(MeasureObject.class).equalTo("id", MeasureViewModel.this.id).findFirst();
            //this is return null
            if(first==null){
                subscriber.onError(new NotFoundException("请重试"));
                return;
            }

You never fixed what I told you to fix, should be

            Realm instance = null;
            try {
                instance = Realm.getDefaultInstance();
                instance.beginTransaction();
                LogUtils.d("uu", "obj id = %s", MeasureViewModel.this.id);
                MeasureObject first = instance.where(MeasureObject.class).equalTo("id", MeasureViewModel.this.id).findFirst();
                //this is return null
                if(first==null){
                    subscriber.onError(new NotFoundException("请重试"));
                    return;
                }
                // TODO: the other code here you already have
                instance.commitTransaction();
           } catch(Throwable e) {
                if(instance != null && instance.isInTransaction()) {
                    instance.cancelTransaction();
                }
           } finally {
               if(instance != null) {
                    instance.close();
               }
           }

Also consider reading the docs on Best Practices regarding Realm Lifecycle Management although fragment should use onCreateView() and onDestroyView()

We've updated the code:

Realm instance = null;

                try {
                    instance = Realm.getDefaultInstance();
                    MeasureObject first = instance.where(MeasureObject.class).equalTo("id", MeasureViewModel.this.id).findFirst();
//                    if(first==null){
//                        subscriber.onError(new NotFoundException("请重试"));
//                        return;
//                    }
                    setObj(instance.copyFromRealm(first));
                } catch (Exception e) {
                    e.printStackTrace();
                    subscriber.onError(new NotFoundException("请重试"));
                    if(instance != null && instance.isInTransaction()) {
                        instance.cancelTransaction();
                    }
                } finally {
                    if (instance != null) {
                        instance.close();
                    }
                }

The exception is:

01-19 23:44:07.350 6560-6628/com.test.measurecontrol W/System.err: java.lang.IllegalArgumentException: Null objects cannot be copied from Realm.
01-19 23:44:07.350 6560-6628/com.test.measurecontrol W/System.err:     at io.realm.Realm.checkValidObjectForDetach(Realm.java:1482)
01-19 23:44:07.350 6560-6628/com.test.measurecontrol W/System.err:     at io.realm.Realm.copyFromRealm(Realm.java:1200)
01-19 23:44:07.350 6560-6628/com.test.measurecontrol W/System.err:     at io.realm.Realm.copyFromRealm(Realm.java:1174)
01-19 23:44:07.351 6560-6628/com.test.measurecontrol W/System.err:     at com.test.measurecontrol.ui.vm.MeasureViewModel$3.call(MeasureViewModel.java:274)
01-19 23:44:07.351 6560-6628/com.test.measurecontrol W/System.err:     at com.test.measurecontrol.ui.vm.MeasureViewModel$3.call(MeasureViewModel.java:262)
01-19 23:44:07.352 6560-6628/com.test.measurecontrol W/System.err:     at rx.Observable.unsafeSubscribe(Observable.java:10142)
01-19 23:44:07.353 6560-6628/com.test.measurecontrol W/System.err:     at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:48)
01-19 23:44:07.353 6560-6628/com.test.measurecontrol W/System.err:     at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:33)
01-19 23:44:07.353 6560-6628/com.test.measurecontrol W/System.err:     at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
01-19 23:44:07.353 6560-6628/com.test.measurecontrol W/System.err:     at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
01-19 23:44:07.353 6560-6628/com.test.measurecontrol W/System.err:     at rx.Observable.unsafeSubscribe(Observable.java:10142)
01-19 23:44:07.354 6560-6628/com.test.measurecontrol W/System.err:     at rx.internal.operators.OperatorSubscribeOn$1.call(OperatorSubscribeOn.java:94)
01-19 23:44:07.354 6560-6628/com.test.measurecontrol W/System.err:     at rx.internal.schedulers.CachedThreadScheduler$EventLoopWorker$1.call(CachedThreadScheduler.java:230)
01-19 23:44:07.354 6560-6628/com.test.measurecontrol W/System.err:     at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
01-19 23:44:07.354 6560-6628/com.test.measurecontrol W/System.err:     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:428)
01-19 23:44:07.354 6560-6628/com.test.measurecontrol W/System.err:     at java.util.concurrent.FutureTask.run(FutureTask.java:237)
01-19 23:44:07.356 6560-6628/com.test.measurecontrol W/System.err:     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272)
01-19 23:44:07.356 6560-6628/com.test.measurecontrol W/System.err:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
01-19 23:44:07.356 6560-6628/com.test.measurecontrol W/System.err:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
01-19 23:44:07.356 6560-6628/com.test.measurecontrol W/System.err:     at java.lang.Thread.run(Thread.java:761)
01-19 23:44:07.378 6560-6560/com.test.measurecontrol W/ResourceType: No package identifier when getting name for resource number 0x00000000
01-19 23:44:07.411 6560-6560/com.test.measurecontrol W/System.err: com.test.measurecontrol.exception.NotFoundException: 请重试
01-19 23:44:07.411 6560-6560/com.test.measurecontrol W/System.err:     at com.test.measurecontrol.ui.vm.MeasureViewModel$3.call(MeasureViewModel.java:277)
01-19 23:44:07.411 6560-6560/com.test.measurecontrol W/System.err:     at com.test.measurecontrol.ui.vm.MeasureViewModel$3.call(MeasureViewModel.java:262)
01-19 23:44:07.411 6560-6560/com.test.measurecontrol W/System.err:     at rx.Observable.unsafeSubscribe(Observable.java:10142)
01-19 23:44:07.411 6560-6560/com.test.measurecontrol W/System.err:     at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:48)
01-19 23:44:07.411 6560-6560/com.test.measurecontrol W/System.err:     at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:33)
01-19 23:44:07.411 6560-6560/com.test.measurecontrol W/System.err:     at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
01-19 23:44:07.411 6560-6560/com.test.measurecontrol W/System.err:     at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
01-19 23:44:07.411 6560-6560/com.test.measurecontrol W/System.err:     at rx.Observable.unsafeSubscribe(Observable.java:10142)
01-19 23:44:07.411 6560-6560/com.test.measurecontrol W/System.err:     at rx.internal.operators.OperatorSubscribeOn$1.call(OperatorSubscribeOn.java:94)
01-19 23:44:07.412 6560-6560/com.test.measurecontrol W/System.err:     at rx.internal.schedulers.CachedThreadScheduler$EventLoopWorker$1.call(CachedThreadScheduler.java:230)
01-19 23:44:07.412 6560-6560/com.test.measurecontrol W/System.err:     at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
01-19 23:44:07.412 6560-6560/com.test.measurecontrol W/System.err:     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:428)
01-19 23:44:07.412 6560-6560/com.test.measurecontrol W/System.err:     at java.util.concurrent.FutureTask.run(FutureTask.java:237)
01-19 23:44:07.412 6560-6560/com.test.measurecontrol W/System.err:     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272)
01-19 23:44:07.412 6560-6560/com.test.measurecontrol W/System.err:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
01-19 23:44:07.412 6560-6560/com.test.measurecontrol W/System.err:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
01-19 23:44:07.412 6560-6560/com.test.measurecontrol W/System.err:     at java.lang.Thread.run(Thread.java:761)

PLUS: the id exist is realm database. We exported to checked

You kinda didn't open the transaction with realm.beginTransaction() even though I'm pretty sure I specified it quite clearly in my example code?

It's the 4th line.

@Zhuinden sorry about that...

@flzyup report back if the example code I provided does not work.

It should though unless you start creating nested transactions.

@Zhuinden we're testing and preparing update all query to transaction block. So why should we use transaction to query? force refresh as you metioned ealier?

@flzyup by putting the query in the transaction block, you force the background thread Realm to be at the latest version. This enables you to always see up to date data in that given scope.

Force refresh would also bump the version of the Realm to a newer version, you'd add it under Realm.getDefaultInstance() and you would also see the latest version and therefore the version in which your new data exists.

@Zhuinden the data which can't be query actually is not new data, it already exists and is old. I.e. if we don't use transaction block and it can also be query but not. Why this happen?

?

@Zhuinden I mean the data which was null after query it not new data. It already exists in database. And if we didn't use the transaction block, it got null most of time especially at first query.

Sorry for the late reply. If you are absolutely sure the object is in your Realm, it sounds like you have to check the value of MeasureViewModel.this.id.

@flzyup have you succeeded in wrapping the query in a transaction block?

Hey @flzyup, have you resolved this? Can we close the ticket?

If there is anything further that we can do for you, please don't hesitate to reopen this ticket, or to open a new one.

Im facing this similar issue

It does even reach the catch block.
BuyStory buyStory = new BuyStory();
Story story;
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
realm.beginTransaction();
story = realm.where(Story.class)
.equalTo("name", mStoryId.trim(), Case.INSENSITIVE)
.findFirst();
Timber.d("storyyy " + story);
if(story == null) {
realm.cancelTransaction();
return;
}
realm.commitTransaction();

        } catch(Exception e) {
            if(realm != null && realm.isInTransaction()) {
                realm.cancelTransaction();
            }
            Timber.d("storyyy e " + e.getMessage());
            throw e;
        } finally {
            if(realm != null) {
                realm.close();
            }
        }
Was this page helpful?
0 / 5 - 0 ratings

Related issues

pawlo2102 picture pawlo2102  ·  3Comments

yuwu picture yuwu  ·  3Comments

wyvern610 picture wyvern610  ·  3Comments

mithrann picture mithrann  ·  3Comments

nolanamy picture nolanamy  ·  3Comments