It seems like my RealmObject values are being hidden by the RealmProxy class, but can be set from the proxyclass.

My model is pretty straight forward as you can see.

public class GroupRealm extends RealmObject {
    @PrimaryKey
    public String id;
    @Index
    public String name;
    public String imageUrl;
    public int order;

    public GroupRealm parent;
    public RealmList<GroupRealm> children;
    public RealmList<ContentRealm> contents;
}

This is how i am setting the values(db is a valid Realm, and everything is in a transaction that commits fine):

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;

The image below is what I get when i query the db latter on.(same variable name not same place in code)

In my android.library where my RealmObjects are defined project I have the necessary plugins.

apply plugin: 'com.android.library'
apply plugin: 'realm-android'

and on the project level I am setting the correct dependencies:

dependencies {
    classpath 'com.android.tools.build:gradle:2.1.0'
    classpath "io.realm:realm-gradle-plugin:0.90.1"
    // NOTE: Do not place your application dependencies here; they belong
    // in the individual module build.gradle files
}

I am out of ideas. If I try to access anything I retrieve the GroupRealm as expected but all of the public properties exposed through the proxy class return null!

Solution 1

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

Solution 2

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.

Solution 3

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.

Solution 4

The answers above are all right if you directly use an RealmObject retrieved from your Realm. With Managed RealmObject (Objects "directly" connected with your Realm, so the "Real Instance" of the object inside your Realm which you can Modify only inside RealmTransaction and which changes will affect all other Managed RealmInstance instantly) you can't see their values inside of the debugger because of the proxy.

Anyway you can work around this by using a NO MANAGED object, so by COPYING the RealmObject from the realm:

MyRealmObject obj = getRealmObjectFromRealm();
if(obj != null){
    obj = mRealm.copyFromRealm(obj);
}

This way you will see all properties of your realm object inside the debugger. Obviously if you need to use a Managed Realm Object inside your code, when you are debugging you need to change your code by creating another "MyRealmObject" instance which is a copy from the Realm of the other "MyRealmObject".

This way you will see all objects properties inside the debugger (:

Hope this is helpful, Greetings & have a nice coding!

:D

Solution 5

This is because of the Realm proxies model which is zero-copy storage.

You can use Kotlin Realm extension, Vicpinm library https://github.com/vicpinm/Kotlin-Realm-Extensions

If you still want to use in Java then you achieve it by:-

Realm.getDefaultInstance().copyFromRealm(realmObject)