Search code examples
javaandroiddatabaseapirealm

Realm does not save attributes, object is not managed although copied to Realm


I have an issue with realm. I receive a custom object from an API. I assign this object to a POJO object using retrofit. Within this object I have an ArrayList of the ToDoItemobject which extends RealmObject.

I receive the data correctly with all attributes, everything gets correctly assigned. I run it through my synchronization algorithm and save it to realm in a writing transaction. But when retrieving the data after realm.commit(); the attributes of the objects are all 0 or null.

The method isManaged()is always false, even after the writing transaction, which I don't understand because in the official documentation is states that a POJO can be converted to a managed object using the copyToRealm method.

I already tried a number of things: creating the GetItemResponseClass as RealmObject, but not possible since it has to extend JSONObject to correctly receive the data from the API. I also tried to write the whole list directly to realm but the result was the same.

As a side note, it can be that my method syncPendingLists has some logic errors, but I couldn't debug it yet, since the attributes were always o and null. Thanks for any help.

Here my code from the Activity:

public class MainActivity extends AppCompatActivity{

@Override
protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Realm.init(this);
    RealmConfiguration config = new RealmConfiguration.Builder().name("myrealm.realm").build();
    Realm.setDefaultConfiguration(config);


    realm = Realm.getDefaultInstance();
    RealmResults<Counter> counterList = realm.where(Counter.class).findAll();

    //setting up counterObject
    if (counterList.isEmpty()) {
        counterObject = new Counter();
        COUNTER = counterObject.getCounter();
    } else {
        counterObject = counterList.get(0);
        COUNTER = counterObject.getCounter();
    }

    initializeLists();

    //Adding the Fragment
    FragmentManager fm = getFragmentManager();
    FragmentTransaction ft = fm.beginTransaction();
    ft.add(R.id.fragment_container, new DoneListFragment(), "DoneListFragment");
    ft.add(R.id.fragment_container, new PendingListFragment(), "PendingListFragment");
    ft.commit();

    RetrofitClient retrofitClient = new RetrofitClient();
    Retrofit retrofit = retrofitClient.getClient();

    mAPIInterface = retrofit.create(ToDoistAPIInterface.class);

}


public void getRemoteItems() {

    final ArrayList<ToDoItem> onlineItems = new ArrayList<ToDoItem>();
    JSONArray array = new JSONArray();
    array.put("items");

    String auxMessage = array.toString();
    mAPIInterface.getItems(RetrofitClient.TOKEN, "*", auxMessage).enqueue(new Callback<GetItemsResponseClass>() {
        @Override
        public void onResponse(Call<GetItemsResponseClass> call, Response<GetItemsResponseClass> response) {

            GetItemsResponseClass itemsResponseClass = new GetItemsResponseClass();
            itemsResponseClass = response.body();
            remoteItemsList = itemsResponseClass.getItems();

            boolean test = remoteItemsList.get(0).isManaged(); //returns false
            boolean test1 = remoteItemsList.get(0).isValid(); //returns true refers to singleton RealmObject

            syncPendingLists(pendingItemList, remoteItemsList);

        }
        @Override
        public void onFailure(Call<GetItemsResponseClass> call, Throwable t) {
            Snackbar.make(floatingButton, "Ups - Couldn't sync items, next time, I promise", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show();
        }
    });
}


 private void initializeLists() {
        RealmResults<ToDoItem> realmToDoItemPendingList = realm.where(ToDoItem.class).equalTo("checkedOffline", false).findAll();
        initializingArrayListFromDB(realmToDoItemPendingList, pendingItemList);
        RealmResults<ToDoItem> realmToDoItemDoneList = realm.where(ToDoItem.class).equalTo("checkedOffline", true).findAll();
        initializingArrayListFromDB(realmToDoItemDoneList, doneItemList);
    }
 private void initializingArrayListFromDB(RealmResults<ToDoItem> realmToDoItemPendingList, ArrayList<ToDoItem> arrayList) {
        int h;
        for (h = 0; h < realmToDoItemPendingList.size(); h++) {
            arrayList.add(realmToDoItemPendingList.get(h));
        }
    }

 public void syncPendingLists(ArrayList<ToDoItem> offlinePendingList, ArrayList<ToDoItem> onlinePendingList) {
     //is my sync algorithm, the important part is the for loop at the end of this method
        boolean hasMatch = false;
        boolean itemChanged = false;

        Date offlineDate = null;
        Date onlineDate = null;

        if (!offlinePendingList.isEmpty()) {
            for (ToDoItem item1 : offlinePendingList) {
                if (item1.getId() < 10000) {
                    try {
                        createNewRemoteItem(item1);
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                } else {
                    for (int i = 0; i < onlinePendingList.size(); i++) {

                        if (item1.getId() == onlinePendingList.get(i).getId()) {
                            hasMatch = true;
                            onlinePendingList.remove(onlinePendingList.get(i));

                            //Compare Fields
                            if (!item1.getContent().equals(onlinePendingList.get(i).getContent())) {
                                itemChanged = true;
                            }
                            if (item1.getPriority() != onlinePendingList.get(i).getPriority()) {
                                itemChanged = true;
                            }
                            if (!item1.getDate_string().equals(onlinePendingList.get(i).getDate_string())) {
                                itemChanged = true;
                            }

                            if (itemChanged == true) {

                                //Format edit dates to date
                                DateFormat format = new SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH);
                                try {
                                    offlineDate = format.parse(item1.getDateAdded());
                                } catch (ParseException e) {
                                    e.printStackTrace();
                                }
                                try {
                                    onlineDate = format.parse(onlinePendingList.get(i).getDateAdded());
                                } catch (ParseException e) {
                                    e.printStackTrace();
                                }
                                //compare dates to see which was last edited
                                if (offlineDate.compareTo(onlineDate) > 0) {
                                    try {
                                        deleteRemoteItem(onlinePendingList.get(i), "item_delete");
                                        createNewRemoteItem(item1);
                                    } catch (JSONException e) {
                                        e.printStackTrace();
                                    }
                                } else if (offlineDate.compareTo(onlineDate) < 0) {
                                    addOrUpdateToDB(item1);
                                }
                            }
                        }
                        if (!hasMatch) {
                            deleteObjectFromDB(item1);
                        }
                    }
                }
            }
        }
        for (ToDoItem onlineItem1 : onlinePendingList) {
            boolean isManaged1 = onlineItem1.isManaged(); //returns false, which is ok since it is not yet in the realm db
        onlineItem1.setLocalId(counterObject.getCounter());
            addOrUpdateToDB(onlineItem1);
            boolean asdf = onlineItem1.isManaged(); //it returns false, but it should return true
            incrementCounter(counterObject);
        }
        initializeLists();
        getPendingListFragment().refreshFragment();

    }

 private void addOrUpdateToDB(ToDoItem newItem) {
        boolean test2= newItem.isManaged(); //returns false
        realm.beginTransaction();
        realm.copyToRealmOrUpdate(newItem);

        //realm.copyToRealm(newItem); //I tried this method as well, but no difference
        realm.commitTransaction();
        boolean test3= newItem.isManaged(); //returns false, and here is the problem, it should return true, shouldn't it?
    assignValuesToToDoItem(itemWithValues, newItem);
        saveCounterToDB(counterObject);
    }
    }

Here my class code of ToDoItem:

public class ToDoItem extends RealmObject implements Parcelable {
public static final Creator<ToDoItem> CREATOR = new Creator<ToDoItem>() {
    @Override
    public ToDoItem createFromParcel(Parcel in) {
        return new ToDoItem(in);
    }

    @Override
    public ToDoItem[] newArray(int size) {
        return new ToDoItem[size];
    }
};
@PrimaryKey
private long localId;
private String content;
private boolean checkedOffline = false;
private int priority;
private String date_string;
private String temp_id;
private long id;
private String date_added;


public ToDoItem(String name) {
    this.content = name;
}

public ToDoItem() {
}

protected ToDoItem(Parcel in) {

    localId = in.readLong();
    content = in.readString();
    checkedOffline = in.readByte() != 0;
    priority = in.readInt();
    date_string = in.readString();
    temp_id = in.readString();
    id = in.readLong();
    date_added=in.readString();
}

public int getPriority() {
    return priority;
}

public void setPriority(int priority) {
    this.priority = priority;
}

public boolean isCheckedOffline() {
    return checkedOffline;
}

public void setCheckedOffline(boolean checkedOffline) {
    this.checkedOffline = checkedOffline;
}

public Long getId() {
    return id;
}

public void setId(long id) {
    this.id = id;
}

public void setRemote_id(Long remote_id) {
    this.id = remote_id;
}

public String getContent() {
    return content;
}

public void setContent(String content) {
    this.content = content;
}

public boolean isDone() {
    return checkedOffline;
}

public String getDate_string() {
    return date_string;
}

public void setDate_string(String date_string) {
    this.date_string = date_string;
}

public long getLocalId() {
    return this.localId;
}

public void setLocalId(long i) {
    this.localId = i;
}

public String getTemp_id() {
    return temp_id;
}

public void setTemp_id(String temp_id) {
    this.temp_id = temp_id;
}

public String getDateAdded() {
    return date_added;
}

public void setDateAdded(String dateAdded) {
    this.date_added = dateAdded;
}


@Override
public int describeContents() {
    return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeLong(localId);
    dest.writeString(content);
    dest.writeByte((byte) (checkedOffline ? 1 : 0));
    dest.writeInt((priority));
    dest.writeString(date_string);
    dest.writeString(temp_id);
    dest.writeLong(id);
    dest.writeString(date_added);
}

@Override
public String toString() {
    return "localId: " + localId + "; content: " + content;
}
}

And here the code for the GetItemsResponseClass:

public class GetItemsResponseClass extends JSONObject {


private String sync_token;

@SerializedName("temp_id_mapping")
private HashMap<String, Long> temp_id_mapping;

private boolean full_sync;

@SerializedName("items")
private ArrayList<ToDoItem> items;


public GetItemsResponseClass(){

}

public String getSync_token() {
    return sync_token;
}

public void setSync_token(String sync_token) {
    this.sync_token = sync_token;
}

public HashMap<String, Long> getTemp_id_mapping() {
    return temp_id_mapping;
}

public void setTemp_id_mapping(HashMap<String, Long> temp_id_mapping) {
    this.temp_id_mapping = temp_id_mapping;
}

public boolean isFull_sync() {
    return full_sync;
}

public void setFull_sync(boolean full_sync) {
    this.full_sync = full_sync;
}

public ArrayList<ToDoItem> getItems() {
    return items;
}

public void setItems(ArrayList<ToDoItem> items) {
    this.items = items;
}
}

EDIT: Apparently it is a desired behavior that the object does not get saved with its attributes. Consequently to assign the values you have to use getters and setters. I added the following method, however even when debugging with a watch, as stated in the official documentation the values do not get assigned:

private void assignValuesToToDoItem(ToDoItem itemWithValues, ToDoItem newItem) {
    realm.beginTransaction();
    newItem.setContent(itemWithValues.getContent()); //the content variable stays null
    newItem.setCheckedOffline(itemWithValues.isDone()); //stays false
    newItem.setPriority(itemWithValues.getPriority());
    newItem.setDate_string(itemWithValues.getDate_string());
    newItem.setTemp_id(itemWithValues.getTemp_id());
    newItem.setId(itemWithValues.getId());
    newItem.setDate_added(itemWithValues.getDate_added());
    realm.commitTransaction();
}

I added this line assignValuesToToDoItem(itemWithValues, newItem); in the main activity in the method private void addOrUpdateToDB(ToDoItem newItem) {...}

Same result...


Solution

  • I found out 2 very important things:

    1. The attributes are saved, however in the debugging window they appear to be 0, false or null
    2. Even putting a Debugging Watch does not show the correct values.
    3. To see the real value how it is in the database you have to add a Watch and put the watch directly on the getters of the object. In my case I added a Watch and typed in "newItem.getContent()". With this line I got the title of my object. However just putting a Watch with "newItem" shows "null".