I am adding some data into realm database after every 15 sec through a service.
After a whole night, the size of app become 350mb due to realm, its confirm..
But if i delete that data from realm, after some condition, the data deleted but the size of app still shown 350mb.
The question is why the app size is not shrinking now.
Realm currently doesn't automatically reclaim the space used by your database, but it will be reused if you later add data again.
If you wish to free the disk space you can use Realm.compactRealm(): https://realm.io/docs/java/latest/api/io/realm/Realm.html#compactRealm-io.realm.RealmConfiguration-
However, a file size of 350 MB sounds like quite a lot. Are you really inserting that much data?
Realm realm = null;
RealmConfiguration config = new RealmConfiguration.Builder(MyApplication.appInstance().getApplicationContext())
.deleteRealmIfMigrationNeeded()
.build();
try {
realm = Realm.getInstance(config);
} catch (Exception e) {
}
RealmResults<MqttLocationModel> result2 = realm.where(MqttLocationModel.class).equalTo("uid", objectId).findAll();
Log.e("Delted object id",""+objectId);
Log.e("Delted DB Size",""+result2.size());
realm.beginTransaction();
result2.clear();
realm.commitTransaction();
RealmResults<MqttLocationModel> result3 = realm.where(MqttLocationModel.class).equalTo("uid", objectId).findAll();
Log.e("Delted final DB Size",""+result3.size());
realm.close();
realm.compactRealm(config);
Your service executes on a background thread, so you probably have a single realm open on a BACKGROUND THREAD for the whole duration of the sync procedure, thus creating multiple versions of the Realm.
If this is true, you should either close and reopen the Realm after a transaction, or use waitForChange ().
Related
By secondary .realm file I mean a realm file which is not the default.realm file.
I have two .realm files - one being the standard default.realm and the other being say aux.realm.
Things work as they should under normal circumstances, but when I perform a heavy operation (multiple tables undergo .deleteAllFromRealm() and re-sync everything) while this happens on a worker thread, the user is still free to perform any UI activities, whenever any interaction is performed involving the aux.realm instance, the app shuts with an ANR.
With some extensive debugging I found that the getAuxRealmInstance takes a lot of time to pass the instance, even though the value for it should be cached. This is in spite of the fact that its configuration already loaded lazily. Hence, it is unclear as to why it takes so much time?
I also though it might be an issue of transactions as there can be only one active transaction at a time, but what i'm not sure is that is the rule valid also through files, like can two realm files have their own transactions running in parallel?
My aux.realm file:
private const val FILE_NAME = "auxiliary.realm"
private val auxiliaryConfiguration = lazy {
RealmConfiguration.Builder()
.name(FILE_NAME)
.schemaVersion(AuxiliarySchemaVersionMappings.CURRENT_SCHEMA_VERSION)
.modules(AuxiliaryRealmModule())
.initialData {
Log.d("AuxRealm", "running initial data migration: ")
// initial version..
// migrate the AppMetaData table from base realm to aux realm
}
}
.migration(AuxiliaryRealmMigration())
.build().also { Log.d("AuxRealm", "configuration created: ") }
}
fun getAuxiliaryRealmInstance(): Realm{
return Realm.getInstance(auxiliaryConfiguration.value)
}
fun getAuxiliaryRealmInstanceAsync(callback: Realm.Callback): RealmAsyncTask{
return Realm.getInstanceAsync(auxiliaryConfiguration.value, callback)
}
PS: The ANR goes away if I load the aux realm instance in async, which as mentioned above, points to the same problem.
Env variables: Realm: 5.4.2, Kotlin 1.2.51
I solved this problem by caching the realm instance:
private Realm auxiliaryRealmInstance;
fun getAuxiliaryRealmInstance(): Realm{
return auxiliaryRealmInstance == null ? Realm.getInstance(auxiliaryConfiguration.value) : auxiliaryRealmInstance;
}
This workaround should not be necessary because here it's written that caching will not make anything more efficient. But I did not notice any disadvantages so far.
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!
I am currently using Realm database on my Android application to retrieve data according to user defined filters and to synchronize with a remote database. After making several requests the app works pretty fine (it consumes about 30 MB of memory, what is ok, taking into account I am using other components and services like maps, etc.). However, when closing the app (swipe on recent apps list) and restarting it, after two or three queries the memory consumption starts increasing considerably (above 100 MB), so it takes too long to make a query and finally the app crashes because of an OutOfMemoryError. Tracking the application instances on Android Studio I see a lot of instances of io.realm.internal.NativeObjectReference.
According to realm documentation I have tried two things, without success:
Getting a Realm instance with Realm.getDefaultInstance() in a class constructor, which is responsible to query the Realm database, and closing the database with mRealm.close()from onDestroy method on main activity.
Getting a Realm instance with Realm.getDefaultInstance() right before starting a database operation, and closing it right after the operation ends. With asynchronous operations I make something like this
mRealm = Realm.getDefaultInstance();
mRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
//perform transaction
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
mRealm.close();
//notify calling function
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
mRealm.close();
}
});
with non-asynchronous operations I make something like this
mRealm = Realm.getDefaultInstance();
RealmResults<DealLocal> dealLocals = mRealm.where(DealLocal.class).findAll();
mRealm.close();
I use Realm.getDefaultInstance() in two classes: one to synchronize with the remote database, and the other one to make the user defined queries. However, at this moment all the calls are made from the main activity, and I guarantee only one operation on the database is made at the same time (i.e. I synchronize first and then make the user defined queries). When a query takes too long because of an excessive memory usage and I kill the app (swipe on recent apps list), the memory usage does not go back to normal.
How would be the correct way to handle the realm instances and operations on my application in order to avoid this problem?
I am trying to create a database for my android application using Realm. I need to have data that is pre-populated when the app is installed. Setting a Realm Migration as part of the RealmConfiguration does not run when the version of the database is 0 (defaults to 0 initially). How can I add data the first time the application is setup?
Realm Java 0.89 introduced a method that allows for specifying a transaction to be run when a Realm database is created for the first time. This method, RealmConfiguration.Builder.initialData(Realm.Transaction transaction), is called as part of setting up the RealmConfiguration Builder.
For example
RealmConfiguration config = new RealmConfiguration.Builder(context)
.name("myrealm.realm")
.initialData(new MyInitialDataRealmTransaction()),
.build();
What I am doing right now that works is to check if this is the first time my app is installed and create a new object.
if (Preferences.freshInstall(getApplicationContext())) {
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
Category inbox = new Category("Inbox", "#445566");
realm.copyToRealm(inbox);
realm.commitTransaction();
Preferences.notNew(getApplicationContext());
}
There should be a better way to do this using Realm Migrations
The initial data transaction setup, as shown by #Benjamin in Realm Java works! I only wish that it was present in Realm Cocoa, as well.
I've created an issue for this, in the Github tracker here, #3877.
This question already has answers here:
"realm migration needed", exception in android while retrieving values from realm db
(5 answers)
Closed 5 years ago.
Whenever I change the model like adding more fields, the app crash with io.realm.exceptions.RealmMigrationNeededException error. This can only be resolved when I uninstalled and reinstalled the app.
Any suggestion to do migration? I am using only the default instance.
If you don't have any problem in loosing your old data then you can delete Realm Configuration and create new one.
Realm realm = null;
try {
realm = Realm.getInstance(MainActivity.this);
} catch (RealmMigrationNeededException r) {
Realm.deleteRealmFile(MainActivity.this);
realm = Realm.getInstance(MainActivity.this);
}
OR
RealmConfiguration config2 = new RealmConfiguration.Builder(this)
.name("default2")
.schemaVersion(3)
.deleteRealmIfMigrationNeeded()
.build();
realm = Realm.getInstance(config2);
you have to do Migration if you don't want to loose your data please see this example here.
You should be able to find the information you need here:
https://realm.io/docs/java/latest/#migrations
Just changing your code to the new definition will work fine, if you
have no data stored on disk under the old database schema. But if you
do, there will be a mismatch between what Realm sees defined in code &
the data Realm sees on disk, so an exception will be thrown.
Realm migrations in 0.84.2 are changed quite a bit, the key points on making a realm (0.84.2) migration work for me were understanding that:
The schemaVersion is always 0 when your app has a realm db without
specifying the schemaVersion. Which is true in most cases since you
probably start using the schemaVersion in the configuration once you
need migrations & are already running a live release of your app.
The schemaVersion is automatically stored and when a fresh install of your app occurs and you are already on schemaVersion 3, realm
automatically checks if there are exceptions, if not it sets the
schemaVersion to 3 so your migrations aren't run when not needed.
This also meens you don't have to store anything anymore in
SharedPreferences.
In the migration you have to set all values of new columns when the type is not nullable, ...
Empty Strings can be inserted but only when setting convertColumnToNullable on the column