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$.


There is nothing to be done about this. Because of the "clever" thing that Realm is doing, the debugger is completely prevented from doing what it is supposed to. You'll have to rely on a lot of Log.d statements.

I'm sorry. That's just the reality of it.


The fields are null. You access the properties through a native method that replaces all field access. Previously (before 0.88.0) it used to create a dynamic proxy that overrode your getters and setters to use their native proxy implementation.

The fields don't have values. But as you can see, the Realm object has the values just fine: it says so in the toString() value.

Tags:

Android

Realm