This question already has answers here:
Cannot retrieve field values from realm object, values are null in debugger
(5 answers)
Closed 5 years ago.
I am trying to implement a generic realm wrapper. So, i can pass an object with its class to either add/update or get. I am using the realm browser to confirm that i am saving the data correctly, but when i perform a getAll query, i receive an array with the correct amount with the correct data structure but all the fields are in default values or null.
Here is my code :
Add RealmModel: (UserRealmModel)
#Override
public void putAll(Collection<RealmObject> realmModels) {
mRealm = Realm.getDefaultInstance();
mRealm.beginTransaction();
mRealm.copyToRealmOrUpdate(realmModels);
mRealm.commitTransaction();
}
GetAllRealmModels: (UserRealmModel)
#Override
public RealmResults getAll(Class clazz) {
return Realm.getDefaultInstance().allObjects(clazz);
}
I also tried:
#Override
public RealmResults getAll(Class clazz) {
return Realm.getDefaultInstance().where(clazz).findAll();
}
clazz = UserRealmModel.class
Output:
But whats interesting that in the debug view as shown in the screenshot, the toString method shows the correct data!
Help please :)
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.
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!
Related
I stored an array of customized class like "Patient" in a Document Field. It stored properly but now when I am trying to download it to android studio it seems like this in the log statement:-
[{"appointmentnumber"=26, "name"="Neeraj Aggarwal",
"phonenumber"="+917988081391"}, {appointmentnumber=27, name=Aaskah
Sharma, phonenumber=+917988081391}, {appointmentnumber=28, name=Mohit
sskm, phonenumber=+917988081391}]
It seems like an Array but I am not being able to apply any property of array on this. Like when I tried to get this array's first element like ArrayName[0] then it shows error like:
java.lang.ClassCastException: java.util.HashMap cannot be cast to com.example.reception.Patient
(reception my project name and Patient as you know is customized class name)
Also I don't know how to get the value of a specific element in the array stored as a fieldvalue. Like we can extract a specific value of map by documentsnapshot.get("Fieldname.Keyname"), is there any method like this to get the element on a specific position in an array.
Firebase solved my problem. Their answer is like this:-
Firebase Support
10:56 AM (29 minutes ago)
to me
Hello Neeraj,
I'm Ian, here to answer any questions you may have when it comes to using Firestore for Android.
If you use the snapshot.get() method on a field containing an array of objects, the Android SDK will return that value as a Java List of HashMap objects. There's currently no way to override this behavior, so you may need to manually get each key-value pair and set the value to a Patient object.
With that said, it is possible to convert the entirety of the document snapshot into a Java object in one go, without needing to use a method like snapshot.get(). Assuming your Doctor class looks something like this:
public class Doctor {
private List<Patient> patients;
//other variables
//Constructor must be empty
public Doctor() {}
public List<Patient> getPatients() {
return patients;
}
public void setPatients(List<Patient> patients) {
this.patients = patients;
}
You can convert your document snapshot into the Doctor object with just one line of code:
Doctor doctor = snapshot.toObject(Doctor.class);
From there, you can get your Java List of patients, and then perform your computation on the patient data as intended.
So all the trick was just to use toObject method.
I'm trying to figure out the best way to set up a RealmObject with a RealmResult as one of its fields.
For example, let's say I have two RealmObjects, Goal and Achievement. The Goal object contains fields that define a query of Achievement's the user wants to track (e.g. date range the achievement was created, type of achievement, etc) and has custom methods to extract statistics from those Achievements.
What is the best way for Goal to contain this RealmResult of Achievements? Here are some ways I've thought of doing this:
Have a persisted RealmList field in Goal and update it anytime a field is changed that would change the resulting query. But how would this RealmList get updated if a new Achievement gets added to the realm?
Use #Ignore annotation on a RealmResult<Achievement> field within Goal. Anywhere in Goal where mResult is used, first check if null and requery if needed. This seems like I will be doing a lot of unneccessary querying if I'm using something like a RecyclerView that refetches the object in getItem().
Have a wrapper class that contains a Goal object and the RealmResult<Achievement> as fields. Add a listener to Goal so that anytime a relevant field changes the RealmResult can be requeried.
I'm leaning towards the last one as the cleanest way to keep a valid RealmResult. Am I missing an easier way to accomplish this?
Okay so I'm trying to implement a wrapper class (which I think is similar to the DAO abstraction #EpicPandaForce was mentioning, but I'm not super familiar with that)
public class GoalWrapper {
private RealmResults<Achievements> mResults;
private Goal mGoal;
private Realm mRealm;
public GoalWrapper(Realm realm, Goal goal) {
mRealm = realm;
mGoal = goal;
// TODO: does this need to be removed somewhere? What happens when GoalWrapper gets GC'd?
goal.addChangeListener(new RealmChangeListener<RealmModel>() {
#Override
public void onChange(RealmModel element) {
// rerun the query
findResultForGoal();
}
});
findResultForGoal();
}
/**
* Run a query for the given goal and calculate the result
*/
private void findResultForGoal() {
mResults = mRealm.where(Achievement.class)
.greaterThanOrEqualTo("date", mGoal.getStartDate())
.lessThanOrEqualTo("date", mGoal.getEndDate())
.equalTo("type", mGoal.getAchievementType())
.findAll();
calculateStats();
}
private void calculateStats() {
// Get relevant stats from mResult...
}
}
I haven't tested this code yet but I plan to have a RecyclerView.Adapter with an ArrayList of GoalWrapper objects.
My one concern is that I never remove the listener on mGoal. Do I even need to remove it? What happens in the case that the ArrayList gets GC'ed? I would think that the Goal field and resulting listeners attached to it all get GC'ed as well.
I have a recyclerview which shows a list of students.
All of the students are held in a realmlist in the adapter.
When the user can ask for a data refresh, than the server sends the list of students back to the user.
What i am doing now is to download all of the information from the server , store it in the db , than retrieving it from the database(via realmresults) and than converting the realmresult to realmlist.
My question is how to properly update the UI?
I have seen in the documentation that realmlist have a managed mode where they are updating the ui automatically..
What is this managed mode? What does it means?
How do i use the realmlist to keep it in a managed state?
And what is the right way(aka best practice) to use realmlists?
Note that i cannot hold my information as realmresult directly because im performing some manipulation on the data that i dont want it to be saved.
Managed Object vs. Standalone
The standalone RealmObject/RealmList is created through the Object's constructor or the Realm.copyFromRealm() method. The data accessing in the standalone object won't go through the underline storage engine, instead, it behaves just like normal object. So the standalone object won't be refreshed when data changes. Examples for standalone object:
MyModel myModel = new MyModel(); // Standalone
MyModel model = realm.where(MyModel.class).findFirst(); // This is managed object.
MyModel standaloneModel = realm.copyFromRealm(model); // The returned value is standalone object.
MyList myList = new MyList(); // Standalone
The managed RealmObject/RealmList are accessing data though Realm's underlying storage engine. They are created when you do query from Realm, or the return from the copyToRealm() (and its variant methods). Like:
MyModel model = realm.where(MyModel.class).findFirst();
MyModel model = new MyModel(); // This is a standalone object.
model = realm.copyToRealm(modle); // The returned value is managed by Realm now.
MyList myList = realm.where(MyModel.class).findFirst().getMyList();
How to properly update the UI
The suggestion is using Realm's change listeners. See https://realm.io/docs/java/latest/#notifications
And what is the right way(aka best practice) to use RealmList?
This is a bit confusing, but Realm is introducing RealmCollection into the next major release (v0.89.0). See https://github.com/realm/realm-java/pull/2345.
Note that i cannot hold my information as realmresult directly because im performing some manipulation on the data that i dont want it to be saved.
If the RealmList is in managed mode, the data changes will be saved as well. If you don't want some data to be saved, you could consider to use #Ignore annotation on those fields. See https://realm.io/docs/java/latest/#ignoring-properties
Update on 04072016
RealmList vs. RealmResults:
RealmList is a list of RealmObject saved as a field of a RealmObject. It represents the one-to-many relationship in Realm.
RealmResults is the results of query.
Both of them (if RealmList in managed-mode) will be auto-refreshed when data changes.
You can get a RealmResults from a RealmList by RealmList.where() like:
RealmResults results = myRealmList.where().findAll();
This answer will be a bit out-of-date after Realm v0.89.0 released, because of new RealmCollection.
This question already has answers here:
Cannot retrieve field values from realm object, values are null in debugger
(5 answers)
Closed 5 years ago.
When doing find queries for objects I'm getting "empty" objects (non-null, but not populated). However, in the debugger I can see the data for the object in the object description (see below). I've also verified the data is there using the Realm Browser. I've tried different find queries, querying with filter criteria, using the same Realm object for inserts/queries, using different Realm objects for inserts/queries, refreshing the Realm, etc.
If I Log fields in the RealmObject I see the proper data print out. However, I'm trying to convert these models into other models for use in RxJava per https://realm.io/news/using-realm-with-rxjava/.
Here's some sample code where reproduced the issue. Below that is a screenshot when breaking at verifyRealm.close().
RealmTester realmTester1 = new RealmTester();
realmTester1.setFirstName("Tester1");
realmTester1.setLastName("ABC");
RealmTester realmTester2 = new RealmTester();
realmTester2.setFirstName("Tester2");
realmTester2.setLastName("XYZ");
Realm insertRealm = Realm.getDefaultInstance();
insertRealm.refresh();
insertRealm.beginTransaction();
insertRealm.copyToRealm(realmTester1);
insertRealm.copyToRealm(realmTester2);
insertRealm.commitTransaction();
insertRealm.close();
Realm verifyRealm = Realm.getDefaultInstance();
RealmResults<RealmTester> verifyTesters = verifyRealm.where(RealmTester.class).findAll();
verifyRealm.close();
I have a screenshot of the debugger at: http://i.stack.imgur.com/1UdRr.png
I'm using v0.82.1. Any thoughts on why the models here aren't populating?
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.
My code:
AppCFG appCFG = new AppCFG();
if(jsonToParse != null) {
Realm realm = Realm.getInstance(AppController.getInstance());
appCFG.setOid(ParserJsonMethods.getOid(jsonToParse));
appCFG.setBaseResourceUrl(jsonToParse.optString(AppCFGContract.BASE_RESOURCE_URL));
appCFG.setClientName(jsonToParse.optString(AppCFGContract.CLIENT_NAME));
appCFG.setBucketName(jsonToParse.optString(AppCFGContract.BUCKET_NAME));
appCFG.setConfigUpdatedOn(StringConvertions.stringDateToMillis(jsonToParse.optString(AppCFGContract.CONFIGURATION_UPDATED_ON)));
appCFG.setDefaultOutputVideoMaxFps(jsonToParse.optInt(AppCFGContract.DEFAULT_OUTPUT_VIDEO_MAX_FPS));
appCFG.setLatestPackagePublishedOn(StringConvertions.stringDateToMillis(jsonToParse.optString(AppCFGContract.LATEST_PACKAGE_PUBLISHED_ON)));
appCFG.setOnboardingPassed(jsonToParse.optBoolean(AppCFGContract.ONBOARDING_PASSED));
appCFG.setOnboardingUsingPackage(jsonToParse.optString(AppCFGContract.ONBOARDING_USING_PACKAGE));
appCFG.setPrefferedFootageOID(jsonToParse.optString(AppCFGContract.PREFFERED_FOOTAGE_OID));
appCFG.setTweaks(jsonToParse.optString(AppCFGContract.TWEAKS));
appCFG.setUploadUserContent(jsonToParse.optString(AppCFGContract.UPLOAD_USER_CONTENT));
appCFG = parseMixedScreen(appCFG, jsonToParse);
realm.beginTransaction();
realm.copyToRealmOrUpdate(appCFG);
realm.commitTransaction();
The result of pulling all results:
All fields are empty.. but somehow the json is attached to the object.. What am I doing wrong??
EDIT:
When I do result.getBaseResourceUrl() after pulling all results and opening this result (the result from the image), for instance, I get back a good answer which is:http://blah.blah. but when I try to get results from realm based on baseResourceUrl = "http://blah.blah" I get back nothing...
Realm uses a zero-copy architecture with proxy objects. This means that all your data is always kept inside our internal storage engine in C++ and not actually copied to Java. This also means that Realm doesn't really use the Java variables and as such they always have uninitialised value when looking at them through a debugger (null for objects, 0 for ints, "" for strings)
You can see your object is really a <yourtype>RealmProxy. What this proxy class does is overriding all getters and setters to access the data in C++ instead of Java. So if you use the normal getters you can access you data. The proxy also creates a proper toString() method which is why your popup shows the correct output.