0

UPDATE:

At this point I can not create any new Realm-based objects. Each and every one fails with the same error, no matter if I clean, rebuild, etc. To test this, I created a new class "Sample":

package com.reddragon.intouch.model;

import java.util.UUID;

import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;

public class Sample extends RealmObject {
    @PrimaryKey
    private String id;
    private String sampleField;

    public Sample() {
        this(UUID.randomUUID().toString());
    }
    private Sample(String id) {
        this.id=id;
    }
    public String getId(){
        return id;
    }

    public String getSampleField() {
        return sampleField;
    }

    public void setSampleField(String sampleField) {
        this.sampleField = sampleField;
    }
}

And in my MainActivity.java, I try and create a new instance:

    try {
        MediaDataMgr.get().addSample(new Sample());
        Timber.d("Lifecycle: was able to add Sample");
    } catch (Exception e) {
        Timber.d("Got exception instantiating Sample: %s", e.getMessage());
    }

This addSample() method uses a similar approach to the two classes in this project that DO work:

public String addSample(Sample s) {
    boolean success = true;
    Sample sample;
    Realm realm;
    String retVal = null;
    boolean mainThread = Thread.currentThread().equals(Looper.getMainLooper().getThread());
    if (mainThread) {
        realm = mUIThreadRealm;
    } else {
        realm = Realm.getDefaultInstance();
    }
    try {
        realm.beginTransaction();
        sample = realm.createObject(Sample.class,s.getId()); //<--CRASH!!!!
        sample.setSampleField(s.getSampleField());
    } catch (Exception e ) {
        Timber.d( "Exception adding a Sample: %s", e.getMessage());
        success = false;
    } finally  {

        if ( success ) {
            realm.commitTransaction();
            retVal = s.getId();
        } else {
            realm.cancelTransaction();
        }
        if (!mainThread) {
            realm.close();
        }
    }
    return retVal;
}

I am completely stuck now on this project.

UPDATE:

I completely commented all references to the 'Contact' object in my app, and then deleted Contact.java from my project. I did a full Rebuild, then ran it, and everything worked.

I then created a new class Contact.java and entered the same fields etc. as before, and uncommented references to it in the rest of my project. I did a rebuild and ran - and got the same error.

I then refactored the name of the Contact class to ContactSharingInfo, thinking there may be class name clash somewhere. Rebuild and run and again - same error, this time referencing the new class name.

ORIGINAL POST:

I am using gradle plugin and annotations processor 5.9.1. I created a new class ("Contact.java"), which initially worked ok. I then adjusted the class (removed a couple of fields, added a new field), and I started receiving this error. I have tested this on a Samsung S7 Edge (API 26) as well as several emulators. Same problem.

I have done all manner of clean, rebuild, invalidate caching and restart, etc. No help. I reviewed bug #3819 and #4579, but nothing in there has helped. I have disabled instant run. No help.

The stack trace is:

realmSet$id:111, com_reddragon_intouch_model_ContactRealmProxy (io.realm)
<init>:30, Contact (com.reddragon.intouch.model)
<init>:26, Contact (com.reddragon.intouch.model)
<init>:84, com_reddragon_intouch_model_ContactRealmProxy (io.realm)
newInstance:96, DefaultRealmModuleMediator (io.realm)
createObjectInternal:1048, Realm (io.realm)
createObject:1024, Realm (io.realm)
addContact:877, MediaDataMgr (com.reddragon.intouch.model)

The code in question that causes this (addContact() in the MediaDataMgr class where I centralize Realm access) is:

    public String addContact(Contact c, int status) {
        boolean success = true;
        Contact contact;
        Realm realm;
        String retVal = null;
        boolean mainThread = Thread.currentThread().equals(Looper.getMainLooper().getThread());
        if (mainThread) {
            realm = mUIThreadRealm;
        } else {
            realm = Realm.getDefaultInstance();
        }
        try {
            realm.beginTransaction();
            contact = realm.createObject(Contact.class,c.getId()); // <--CRASH HAPPENS HERE
            contact.setEmailAddress(c.getEmailAddress());
            contact.setDisplayName(c.getDisplayName());
            contact.setStatus(status);

        } catch (Exception e ) {
            Timber.d( "Exception adding a contact: %s", e.getMessage());
            success = false;
        } finally  {

                if ( success ) {
                    realm.commitTransaction();
                    retVal = c.getId();
            } else {
                realm.cancelTransaction();
            }
            if (!mainThread) {
                realm.close();
            }
        }
        return retVal;
    }

And the Contact class referenced in the stack trace is:

public class Contact extends RealmObject implements CardListable {
    @PrimaryKey
    private String id;
    private String displayName;
    private String emailAddress;
    private String pathToProfilePic; // This will always be a URI, but we have to store it as a string and convert to URI at runtime.
    @Ignore
    private int status = STATUS_UNKNOWN;

    public Contact() {
        this(UUID.randomUUID().toString());
    }

    private Contact(String id) {
        this.id = id;
    }

    public String getId(){
        return id;
    }

    public String getDisplayName() {
        return displayName;
    }

    public void setDisplayName(String displayName) {
        this.displayName = displayName;
    }

    public String getEmailAddress() {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public String getPathToProfilePic() {
        return pathToProfilePic;
    }

    public void setPathToProfilePic(String pathToProfilePic) {
        this.pathToProfilePic = pathToProfilePic;
    }

    public String getFirstLineDesc() {
        return displayName;
    }

    public String getSecondLineDesc() {
        return emailAddress;
    }
}

When debugging into the com_reddragon_intouch_model_ContactRealmProxy.java class, I find that the exception occurs because the variable 'proxyState' is null when the method public String realmSet$id() is called:

    public void realmSet$id(String value) {
        if (proxyState.isUnderConstruction()) { //<-- CRASH HAPPENS HERE
            // default value of the primary key is always ignored.
            return;
        }

        proxyState.getRealm$realm().checkIfValid();
        throw new io.realm.exceptions.RealmException("Primary key field 'id' cannot be changed after object was created.");
    }

Which leads me to believe that the initialization of proxyState in realm$injectObjectContext() is not being called.

This same approach to creating a new object is working fine with two other classes in this same project, and I verified that realm$injectObjectContext() IS being called there. Here is the stack trace in the same type of construction for my Media.java class (which works):

realm$injectObjectContext:105, com_reddragon_intouch_model_MediaRealmProxy (io.realm)
<init>:52, Media (com.reddragon.intouch.model)
<init>:49, Media (com.reddragon.intouch.model)
<init>:99, com_reddragon_intouch_model_MediaRealmProxy (io.realm)
newInstance:99, DefaultRealmModuleMediator (io.realm)
createObjectInternal:1048, Realm (io.realm)
createObject:1024, Realm (io.realm)
addMedia:267, MediaDataMgr (com.reddragon.intouch.model)

So something about how the code is generated for the Contact class must be different from the other ones that work - and that "something" is confusing Realm to not call that method.

What to do about it?

tfrysinger
  • 1,306
  • 11
  • 26

1 Answers1

0

SOLUTION:

In my case I was an unwitting victim of Lint refactoring. It turns out I had run some Lint processes across my classes, and one of the suggested changes was to change certain constructors to be declared private. Apparently Realm doesn't like this in the model classes that take default args in the constructor. So, for example the Contact.java class had this constructor:

public class Contact extends RealmObject implements CardListable {
    @PrimaryKey
    private String id;
    private String displayName;
    private String emailAddress;
    private String pathToProfilePic; // This will always be a URI, but we have to store it as a string and convert to URI at runtime.
    @Ignore
    private int status = STATUS_UNKNOWN;

    public Contact() {
        this(UUID.randomUUID().toString());
    }

    private Contact(String id) { //<--NOTE PRIVATE CONSTRUCTOR
        this.id = id;
    }

Changing to this:

public Contact() {
    this(UUID.randomUUID().toString());
}

public Contact(String id) { //<--NOTE THIS IS NOW PUBLIC
    this.id = id;
}

and doing a clean+rebuild solved the problem.

tfrysinger
  • 1,306
  • 11
  • 26