Realm what is RealmObject.isValid()? - android

Hi everybody i have somes problems or bug..
Have 1 year working with Realm i understand realm use threads ok..
what does it mean make isValid() for example
User user extends RealmObject
user.isValid() ?
What is my result ?

user.isValid() returns whether this RealmProxy objects points to a "valid object" in the Realm, although it is easier to understand that an "object that hasn't been invalidated".
Two things invalidate a RealmObject:
closing the Realm instance it belongs to
deleting the object on any thread (if you're on an auto-updating looper thread)

Related

Realm Android: what is mean by isValid(), isLoaded(), isManaged()?

In Realm there are three methods isValid(), isLoaded(), isManaged().
I want to know which method should used when?
isValid
public final boolean isValid()
Checks if the RealmObject is still valid to use i.e., the RealmObject hasn't been deleted nor has the Realm been closed. It will always return true for unmanaged objects.
Two things invalidate a RealmObject:
- closing the Realm instance it belongs to
and deleting the object on any thread (if you're on an auto-updating
looper thread)
isLoaded
public final boolean isLoaded()
Checks if the query used to find this RealmObject has completed.
Async methods like RealmQuery.findFirstAsync() return an RealmObject that represents the future result of the RealmQuery. It can be considered similar to a Future in this regard.
Once isLoaded() returns true, the object represents the query result even if the query didn't find any object matching the query parameters. In this case the RealmObject will become a "null" object.
isManaged
public static boolean isManaged(E object)
Checks if this object is managed by Realm. A managed object is just a wrapper around the data in the underlying Realm file. On Looper threads, a managed object will be live-updated so it always points to the latest data. It is possible to register a change listener using addChangeListener(RealmModel, RealmChangeListener) to be notified when changes happen. Managed objects are thread confined so that they cannot be accessed from other threads than the one that created them.
Instances of Realm objects can be either managed or unmanaged.
Managed objects are persisted in Realm, are always up to date and thread confined. They are generally more lightweight than the unmanaged version as they take up less space on the Java heap.
Unmanaged objects are just like ordinary Java objects, they are not persisted and they will not be updated automatically. They can be moved freely across threads.
More info refer:https://realm.io/docs/java/4.3.3/api/io/realm/RealmObject.html
FROM DOCS
isValid()
Checks if the RealmObject is still valid to use i.e., the RealmObject hasn't been deleted nor has the Realm been closed. It will always return true for unmanaged objects.
Note that this can be used to check the validity of certain conditions such as being null when observed.
EXAMPLE :
// With RealmObject
yourRealmObject.isValid();
isLoaded()
Checks if the query used to find this RealmObject has completed.
Returns:
true if the query has completed, false if the query is in progress.
3.
isManaged()
Checks if this object is managed by Realm. A managed object is just a wrapper around the data in the underlying Realm file. On Looper threads, a managed object will be live-updated so it always points to the latest data. It is possible to register a change listener using addChangeListener(RealmModel, RealmChangeListener) to be notified when changes happen. Managed objects are thread confined so that they cannot be accessed from other threads than the one that created them.
If this method returns false, the object is unmanaged. An unmanaged object is just a normal Java object, so it can be parsed freely across threads, but the data in the object is not connected to the underlying Realm, so it will not be live updated.

RealmError: Realm Out of memory size

I am using Realm 3.0.0 as the DB of my Android app. It's like a questionnaire application, in which the user navigates inside the app a lot. When I use the app (go back and forth) continuously, I get the following error:
Fatal Exception: io.realm.exceptions.RealmError: Unrecoverable error. mmap() failed: Out of memory size: 1073741824 offset: 0 in /Users/cm/Realm/realm-java/realm/realm-library/src/main/cpp/io_realm_internal_SharedRealm.cpp line 109
at io.realm.internal.SharedRealm.nativeGetSharedRealm(SharedRealm.java)
at io.realm.internal.SharedRealm.(SharedRealm.java:187)
at io.realm.internal.SharedRealm.getInstance(SharedRealm.java:229)
at io.realm.internal.SharedRealm.getInstance(SharedRealm.java:204)
at io.realm.RealmCache.createRealmOrGetFromCache(RealmCache.java:124)
at io.realm.Realm.getDefaultInstance(Realm.java:210)
Now I know the main cause of this is not closing Realm instances. But I've already checked for that multiple times. And I am positive that I close every instance I open.
The app has many activities and fragments that all get a Realm instance on their onCreate and close it on their onDestroy. There are also other background network jobs that run to upload data that get Realm instances. These jobs close their Realm instances when they've finished running or when they cancel.
All of the above get their Realm instance thru injection via Dagger 2:
#Provides
#Nullable
static Realm realm(#Nullable RealmConfiguration configuration) {
if (configuration != null) {
Realm.setDefaultConfiguration(configuration);
return Realm.getDefaultInstance();
}
return null;
}
Configuration is also provided in the same Dagger Module.
To be more specific, a Questionnaire consists of many Question Fragments displayed in a ViewPager. Each Fragment gets injected with a realm. Many interactions in a given Question Fragment write data to the DB (some async, some blocking). These Fragments also query the database on onResume to get their updated Data. Some of this data is also copied out of Realm via realm.copyFromRealm(). Now at any given time of these happening, an upload job is most likely running and reading data from the DB and uploading it to a server. When an upload job finishes, it then writes to the DB.
I think I can have up to 7-12 fragment/activities holding a realm reference on the UI thread at a given moment. And 0-6 other references on 0-3 other threads (Background Jobs).
Moreover, I compact my realm DB via Realm.compactRealm(realmConfiguration) on every app launch (perhaps as a separate problem, this doesn't seem to do it's job consistently).
Above I've tried to describe my Realm usage descriptively without going into details. Now my problem is, when a user excessively uses the app (going back and forth between activities/fragments (realm injection + DB read query), uploading data (realm injection + DB read&write query)), I get the above posted Out of Memory Error.
I am also using Leak Canary, and it hasn't detected any leaks. (Not sure if it can anyway)
Am I using Realm in a way it's not supposed to be used? Should I close Realm instances onPause instead of onDestroy? Should I have only one realm instance in an activity and have all it's fragmetns (up to 5 in my case) use this instance? What kind of changes can I make in my app, and perhaps my app architecture to solve this problem?
I appreciate any help in trying to solve this problem.
EDIT: I'm sharing the realm open-close logic in my background threads.
All my jobs share the same realm usage, which is the following:
Realm is injected lazily via:
#Inject protected transient Lazy<Realm> lazyRealm;
The realm object reference is held at the private transient Realm realm; field. I am using Android Priority Job Queue. When the job is added:
#Override
public void onAdded() {
realm = lazyRealm.get();
realm.executeTransaction(realm1 -> {
//write some stuff into realm
});
realm.close();
}
And when the job is run realm is retreived once, and every possible ending of this method has a call to realm.close()
#Override public void onRun() throws Throwable {
synchronized (syncAdapterLock) {
realm = lazyRealm.get();
Answer answer = realm.where(Answer.class).equalTo(AnswerQuery.ID, answerId).findFirst();
if (answer == null) {
realm.close();
throw new RealmException("File not found");
}
final File photoFile = new File(answer.getFilePath());
final Response response = answerService.uploadPhotoAnswer(answerId, RequestBody.create(MediaType.parse("multipart/form-data"), photoFile)).execute();
if (!response.isSuccessful()) {
realm.close();
throw new HttpError(statusCode);
}
realm.executeTransaction(realm1 -> {
answer.setSyncStatus(SyncStatus.SYNCED.getCode());
});
}
realm.close();
}
}
As you can see, these background threads do close their realm instances properly as far as I'm concerned.
While it was true that all my background tasks did call realm.close(), one of them called it too late in it's lifecycle. That was my GPSService, which is a background service. The problem was that GPS service is initialized at the launch of the App as an Android Service, which is rarely destroyed. I was injecting a realm instance onCreate and closing it onDestroy. After the comments of #EpicPandaForce and reading his articles about using realm properly. I realized that this was the cause of the leak. A non-looper thread was keeping an open realm reference for an extremely long time, thus, the mmap was bloating every time a write transaction occures. Now that I moved the realm get/close to happen every time the service runs, my problem is fixed.
My take away is that one needs to treat background thread realm access very delicately. Thank you both for your quick responses and help!

Thread issues with realm

I have a problem with my threads.
I have my fragment where I retrieve 2 realm objects. When I send these 2 objects to another fragment (dialog fragment), and try to connect them with another object I get the error: Realm access from incorrect thread, Realm objects can only be accessed on the threads where they were created.
How can I solve (workaround) this?
I found out that this answer solves my problem. ( not sure if its the correct way to do it )
youRealmObject = realm.copyFromRealm(youRealmObject);
Android Realm - Accessing Realm Object from Service

Realm Find Queries Result in Empty Objects [duplicate]

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.

Realm or Paper for JPA in Android?

I'm developing an Android app with Android Annotations. For persistence, I firstly used a Content Provider (very complex) on top of SQLite. Then, I discovered Realm. It seemed very cool until I had to be notified for insertions to make my RecyclerView dynamic. To be notified of insertions, I made a singleton class I called RealmProxy with a proxy method for copyToRealm(), and an interface to implement to be a RealmListener. I called registered listeners in my copyToRealm() method passing them the added RealmObject, so I could populate my SortedList (support library list designed for RecyclerView) RecyclerView Adapter. I also used my RealmListener interface to send new Objects over network as soon as they are saved.
After compiling and running, I got and IllegalStateException (Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.) because I get the Realm instance from UI thread but I send them over network in a background thread obviously. Why do I get this error ? Whenever my JSON serialization library LoganSquare, based on Jackson calls a getter on my RealmObject in the background to send over network, this exception is thrown. This made me hate Realm thread policy and the fact that fine grained notifications aren't built-in. Also, Realm doesn't allow me to define any custom method. I can't even implement Comparable in a Realm Object.
When I saw Paper (thanks to Android Arsenal and Pushbullet) today, I was very interested in a no headaches JPA solution. It seems very simple, without restriction for Lists, Maps, and any class not extending a special class (Realm requires extending RealmObject and using RealmList instead of generic List which my json<>java didn't liked, forcing me to copy lists).
EDIT:
I discovered SnappyDB today. It uses the same serialization library (Kryo) as Paper, it seems to be very similar to Paper, with more features for keys management.
So my question is the following:
Should I search for workarounds and continue to use Realm, if yes, which workarounds, or should I use Paper, or SnappyDB instead ? Did anyone used Paper or SnappyDB for android?
All the best
If your question is about how to update your Object in UI thread when it gets changed in background, it is actually quite simple.
For example in your UI thread you can do this:
private Dog dog;
private RealmChangeListener listener = new RealmChangeListener() {
#Override
// This will be called when the commitTransaction gets called
// in the background thread
public void onChange() {
// It would changed to "EFG" automatically in next UI loop after
// you updated it in the background thread.
String name = dog.getName();
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
dog = realm.where(Dog.class).equalTo("id", 42).findFirst();
// Assume it is "ABC" now
String name = dog.getName();
// Register the listener
realm.addChangeListener(listener);
}
And update the dog in th background service like:
// This realm will be a different instance created in this thread.
dog = realm.where(Dog.class).equalTo("id", 42).findFirst();
realm.beginTransaction();
dog.setName("EFG");
realm.commitTransaction();
The IllegalStateException is because of:
The only rule to using Realm across threads is to remember that Realm, RealmObject or RealmResults instances cannot be passed across threads. When you want to access the same data from a different thread, you should simply obtain a new Realm instance (i.e. Realm.getInstance(Context context) or its cousins) and get your objects through a query. The objects will map to the same data on disk, and will be readable & writeable from any thread!
See see doc here
And you probably need RealmBaseAdapter which can make building a ListView with Realm pretty easy. You can find example here.
JPA is not a solution, it's a definition for Java Persistence. Once you choose JPA, you need to find an implementation. In the Java world, the most widely used implementation is Hibernate. Also, you can use Hibernate ORM without using JPA.
On Android, OrmLite provides an implementation for a subset of JPA. But, as it's only a subset, you may as well skip JPA and use the equivalent Ormlite annotations. I use JPA implemented by Hibernate on my server apps, and Ormlite with no JPA on Android. I definitely recommend Ormlite.

Categories

Resources