Cannot Retrieve Field Values from Realm Object, Values Are Null in Debugger

Cannot retrieve field values from realm object, values are null in debugger

Relevant FAQ in documentation: https://realm.io/docs/java/latest/#debugging


Realm uses Android Gradle Transform API. It gives a possibility to manipulate compiled class files before they are converted to dex files.

More details inside io.realm.transformer.RealmTransformer and io.realm.transformer. BytecodeModifier classes which can be found in the realm's github.

What RealmTransformer does, among others, is:

  • replacing all accesses to fields of user's RealmObjects with the appropriate Realm accessors.

You can also check result classes inside folder app/build/intermediates/transforms/RealmTransformer/

Example of setter:

Line of your code:

gr.imageUrl = g.GlyphUrl;

will be replaced with something like this:

String var5 = g.GlyphUrl;
gr.realmSet$imageUrl(var5);

Example of getter:

String url = gr.imageUrl;

will be replaced with something like this:

String url = gr.realmGet$imageUrl();

Example use case

  1. You have created class GroupRealm. Realm using Transform API generates GroupRealmRealmProxy. This proxy class looks like this:

    public class GroupRealmRealmProxy extends GroupRealm implements RealmObjectProxy, GroupRealmRealmProxyInterface {
    private final GroupRealmRealmProxy.GroupRealmColumnInfo columnInfo;
    private final ProxyState proxyState;
    private RealmList<GroupRealm> childrenRealmList;
    private RealmList<ContentRealm> contentsRealmList;
    private static final List<String> FIELD_NAMES;

    GroupRealmRealmProxy(ColumnInfo columnInfo) {
    ...
    }

    public String realmGet$id() {
    this.proxyState.getRealm$realm().checkIfValid();
    return this.proxyState.getRow$realm().getString(this.columnInfo.idIndex);
    }

    public void realmSet$id(String value) {
    this.proxyState.getRealm$realm().checkIfValid();
    if(value == null) {
    this.proxyState.getRow$realm().setNull(this.columnInfo.idIndex);
    } else {
    this.proxyState.getRow$realm().setString(this.columnInfo.idIndex, value);
    }
    }

    public String realmGet$name() {
    this.proxyState.getRealm$realm().checkIfValid();
    return this.proxyState.getRow$realm().getString(this.columnInfo.nameIndex);
    }

    public void realmSet$name(String value) {
    this.proxyState.getRealm$realm().checkIfValid();
    if(value == null) {
    this.proxyState.getRow$realm().setNull(this.columnInfo.nameIndex);
    } else {
    this.proxyState.getRow$realm().setString(this.columnInfo.nameIndex, value);
    }
    }

    ...
    }

    You can observe that methods realmSet$name and realmGet$name don't have access to field name declared in the class GroupRealm. They use proxyState.

  2. Now, let's back to the usage of GroupRealm. When you debug your code:

    GroupRealm gr = db.where(GroupRealm.class).equalTo("id",g.GroupID).findFirst();
    if(gr==null){
    gr = db.createObject(GroupRealm.class,g.GroupID);
    }
    gr.imageUrl = g.GlyphUrl;
    gr.name = g.Title;
    gr.order = g.OrderNum;

    in a reality it's decompiled version looks like this:

    GroupRealm gr = (GroupRealm)realm.where(GroupRealm.class).equalTo("id", g.GroupId).findFirst();
    if(gr == null) {
    gr = (GroupRealm)realm.createObject(GroupRealm.class, g.GroupId);
    }

    String var7 = g.GlyphUrl;
    gr.realmSet$imageUrl(var7);
    var7 = g.Title;
    gr.realmSet$name(var7);
    int var8 = g.OrderNum;
    gr.realmSet$order(var8);

    First of all, gr is the instance of GroupRealmRealmProxy class. As you can see, setting of gr.name is replaced by gr.realmSet$name(var7). It means that the field name of GroupRealm is never used. The situation is analogous in the case of realmGet$.

While debugging you see your version of source code but actually you're using a modified version with injected methods realmSet$ and realmGet$.

Can't watch realmObjects in debug (Android Studio)

Check that field is not lazy. Lazy objects are loaded when they are retrieved.

Cant retrieve data correctly from realm

Mr Zeyad,
I went through Realm documentation for you. They have a well written document with an eample for your question.

They say,

Adding a watch in Android Studio on a RealmObject will display values of the fields. Unfortunately these values are wrong because the field values are not used. Realm creates a proxy object behind the scenes and overrides the getters and setters in order to access the persisted data in the Realm. Adding a watch for any of the accessors will yield correct values.

See the Image

In the image above the debugger has stopped on line 113. There are three watch values, the person variable and the person.getName() and person.getAge() accessors. The code from lines 107 to 111 alters the person instance by changing the name and age. These values are then persisted in a transaction. On line 113, where the debugger is currently paused, the person watch instance is reporting on field values and they are incorrect. The watch values that use the accessor for person.getName() and person.getAge() report values that are correct.

Please note, the .toString() method will output the correct values but the watch panel will not (when watching a variable which is a RealmObject).

Read More Here

Hope it helps!

Realm Find Queries Result in Empty Objects

The idea behind realm-java is that we are generating Proxy class inherits from user's model class, and override the setters and getters there.

It is totally normal that you see null values for the model's field in the debugger, since the Realm are not setting them. (zero-copy, Realm is trying to reduce the memory usage by managing the data in the native code and sharing them whenever it is possible.)

Because of this, when you want to access a Realm model's field, please always use setters and getters. Checking the generated Proxy class will help you to understand this, it is quite simple actually. It is located in the build directory named like MyModelRealmProxy.java

And also check this section of the documents, it would give you some idea about the standalone object and how to write them to Realm.

Realm relation field always null

I was thinking my objects must not be null directly but the point is Realm uses proxy for models and the proxy is not null actually.

Android Realm - findAll returns objects with null parameters

For managed realm objects, data is not copied to the fields, you obtain them through the proxy getter/setter calls.

Therefore, the fact that fields are null and toString() shows the values is completely expected and well-documented behavior.

To see the values, you have to add watches for the getter methods.

See the documentation.

Android - Realm How to get value

You cannot trust the debugging view in Android Studio for RealmObjects managed by Realm. They are just proxy objects for the true underlying data that are stored in native memory. This means that that fields will only show the default value for the field: 0 for int, null for String and references.

You can read more about it here: http://realm.io/docs/java/latest/#debugging

Android Realm - debugging

Realm proxies your model objects and is a zero-copy storage system so in order to inspect the value of a field you need to use the evaluate expression feature of the debugger.

We are considering the possibility to write a plugin for the debugger to show the values directly, but that is still at an investigation stage.

Realm-Android: field value and field getter value are not the same

I think the direct field access in your case is not transformed at this time.

You would get proper result if you change your current code:

realm.where(MessageBean.class)
.equalTo("userId", PreferenceUtils.getUserId(UIUtils.getContext()))
.or()
.equalTo("userId", PreferenceUtils.STRING_DEFAULT)
.equalTo("type", PushType.PERSONAL_ACTIVITY)
.or()
.equalTo("type", PushType.ARTICLE)
.equalTo("read", 0)
.findAll()
.get(0)
.read

with

RealmResults<MessageBean> results = realm.where(MessageBean.class)
.beginGroup()
.equalTo("userId", PreferenceUtils.getUserId(UIUtils.getContext()))
.endGroup()
.or()
.beginGroup()
.equalTo("userId", PreferenceUtils.STRING_DEFAULT)
.equalTo("type", PushType.PERSONAL_ACTIVITY)
.endGroup()
.or()
.beginGroup()
.equalTo("type", PushType.ARTICLE)
.equalTo("read", 0)
.endGroup()
.findAll();
MessageBean message = results.get(0);
int result = message.read; // <-- will return proper value

So you should store the results.get(0) first into a local variable, that way the Realm-Transformer will be able to detect it. (although to be fair, I think it should work even without this trick).

If you use Accessor method, then it uses the proxy accessor even without the Realm-Transformer running through this line of code.

It is key that in the debugger, you check for the obtained value, and not the field value in the object itself.



Related Topics



Leave a reply



Submit