Do you know how to prevent exception when using
Synchronization synchronization = query.findAllSorted("endDate", Sort.DESCENDING).first();
if database is empty?
I'm trying to get the last synchronization in Synchronization table. But if there isn't any record in table, I'm catching an exception.
What's the best way to do that ?
Thank you.
You can do the following:
RealmResults<Synchronization> results = query.findAllSorted("endDate", Sort.DESCENDING);
Synchronization obj = (result.size() > 0) ? result.first() : null;
Although I do agree that it could be a bit easier.
Related
I'm trying to implement a simple chat application on web sockets in Clean Architecture. I had to choose a db for caching all information, so I decided to use Realm, because I heard it was pretty good database for any kind of mobile applications. But when I actually faced the Realm, it turned out to be really painful experience for me to implement caching logic with it.
All problems come from applying transaction to database which then must be synced on all threads working with Realm. There seems to some kind of synchronization problem with my code. For example, I want to save my object to Realm and then query it out of.
Here I have two simple functions to save and to get chat:
fun getBackgroundLooper(): Looper {
val handlerThread = HandlerThread("backgroundThread")
if (!handlerThread.isAlive)
handlerThread.start()
return handlerThread.looper
}
fun saveChat(chat: Chat): Completable {
val realmChat = ChatMapper.domainToCache(chat)
return Completable.create { e ->
val realm = Realm.getDefaultInstance()
realm.executeTransactionAsync({
it.insertOrUpdate(realmChat)
}, {
realm.close()
e.onComplete()
}, {
realm.close()
e.onError(it)
})
// Subscribe on background looper thread
// to be able to execute async transaction
}.subscribeOn(AndroidSchedulers.from(getBackgroundLooper()))
}
fun getSingleChat(chatId: String): Single<Chat> {
return Single.defer {
val realm = Realm.getDefaultInstance()
realm.isAutoRefresh = true
val realmChat = realm.where(RealmChat::class.java)
.equalTo("id", chatId).findFirstAsync()
if (realmChat.isValid) {
realmChat.load()
val chat = ChatMapper.cacheToDomain(realmChat)
realm.close()
Single.just(chat)
}
realm.close()
Single.error<Chat>(ChatNotExistException())
// Subscribe on background looper thread
// to be able to execute auto refreshing
}.subscribeOn(AndroidSchedulers.from(getBackgroundLooper()))
}
So, when I try to run simple code like this
remote.getChat().flatMap {
cache.saveChat(it) //save chat to realm
.andThen(cache.getSingleChat(it.id)) //then query it by id
}
I always get no matter of what ChatNotExistException, but if I try to run query again in another attempt or after restarting the application, then the chat object gets found
I also tried many different approaches to execute this code:
I tried to use realm.refresh() in getSingleChat or not use it at all.
I tried to query chat synchronously with findFirst() and findAll() instead of findFirstAsync().
I tried querying chat on current thread without .subscribeOn().
I tried to use realm.executeTransaction() instead of async transactions.
I tried to add thread sleep between saving and querying, so that transaction may take some time to get applied and I need to wait before attempting to query the chat
I'm begging anybody to explain me what am I doing wrong and how to make this code working. I can't change the architecture of my application and use Realm objects as my view models, I need to find solution in these conditions.
But when I actually faced the Realm, it turned out to be really painful experience for me to implement caching logic with it.
Reading the docs regarding best practices help. For example, the default idea is that you define a RealmResults using an async query on the UI thread, add a change listener to it, and observe the latest emission of the database.
There is no "caching" involved in that beyond saving to the database and observing the database. Any additional complexity is added by you and is completely optional.
All problems come from applying transaction to database which then must be synced on all threads working with Realm.
All looper threads automatically make the Realm auto-refresh, therefore if addChangeListener is used as intended in the docs, then there is no need for trickery, Realm will manage the synchronization between threads.
I want to save my object to Realm and then query it out of.
realm.executeTransactionAsync({
No reason to use executeTransactionAsync when you are already on a background thread.
try(Realm realm = Realm.getDefaultInstance()) {
realm.executeTransaction((r) -> {
// do write here
});
}
realm.where(RealmChat::class.java)
If you do import io.realm.kotlin.where, then you can do realm.where<RealmChat>().
.findFirstAsync()
No reason to use findFirstAsync() instead of findFirst() when you are already on a background thread. Also no reason to use load() when you're on a background thread, because you should be using findFirst() in the first place anyway.
You are also most likely missing a return#defer Single.just(chat) to actually return the chat if it's found. That is most likely what your original problem is.
With the handler thread things you're doing though, you might want to consider taking a look at this project called "Monarchy", as it intends to set up the ability to run queries on a background looper thread while still observing the results. It is labelled stagnant but the ideas are sound.
I am new to Realm and I have a problem.
I was able to make some writes using realm.
The problem is that when I try get the RealmResults using
mResults = mRealm.where(Player.class).findAllAsync();
it returns nothing. But when I use
mResults = mRealm.where(Player.class).findAll();
it returns the records.
Can I have some help? Thanks!
As pointed out in the comments, findAllAsync() always returns an empty result. That's what makes it asynchronous! Register a RealmChangeListener to receive the asynchronous result.
I'm building an android app using the Android Parse SDK, which gets all data from Parse at initialisation and stores it locally. Later, it will only update those entities (ParseObjects) which need so. I'm not getting any return from some Pin() operations, and similarly no callback when I use PinInBackground() and variants. Same happens with Unpin().
My code is something like the following. I have a list of ControlDate, a ParseObject which contains updated_at and updated_locally_at for each Parse data table. I use it to decide if I should query a given table (reducing number of queries). I iterate over this list when I perform a data update, in an IntentService, like this:
protected void onHandleIntent(Intent intent) {
for(ControlDate d : mUpdateQueue) { // mUpdateQueue is the list of ControlDate
if(d.entityNeedsUpdate()) { // returns true if updated_at > updated_locally_at
updateEntity(d);
}
}
private boolean updateEntity(ControlDate d) {
String tableName = d.getTableName();
Date lastLocalUpdate = d.getLastLocalUpdate();
ParseQuery<ParseObject> qParse = ParseQuery.getQuery(tableName);
qParse.whereGreaterThan("updated_at", lastLocalUpdate);
try {
// update the entities
List<ParseObject> entities = qParse.find();
ParseObject.pinAll(entities); // SOMETIMES GETS STUCK (no return)
// update the associated ControlDate
d.setLastLocalUpdate(new Date()); // updated_locally_at = now
d.pin(); // SOMETIMES GETS STUCK (no return)
}
catch(ParseException e) {
// error
}
}
}
Those operations SOMETIMES do not return. I'm trying to find a pattern but still no luck, apparently it started happening when I added pointer arrays to some of the entities. Thus, I think it may be due to the recursive nature of pin(). However it is strange that it sometimes also gets stuck with ParseObjects which do not reference any others - as it is the case with d.pin().
Things I've tried:
changing the for loop to a ListIterator (as I am changing the list of ControlDates, but I don't think this is necessary);
using the threaded variants (eg.: PinInBackground()) - no callback;
pinning each entity individually (in a loop, doing pin()) - a lot slower, still got stuck;
debugging - the thread just blocks here: http://i.imgur.com/oBDjpCw.png?1
I'm going crazy with this, help would be much appreciated!
PS.: I found this https://github.com/BoltsFramework/Bolts-Android/issues/48
Its an open issue on the bolts library, which is used in the Android SDK and may be causing this (maybe?). Anyway I cannot see how I could overcome my problem even though the cause for the pin() not returning could be an "unobserved exception" leading to a deadlock.
I am trying to delete the last Object from the Realm.io database based on a query, like so:
Realm realm = Realm.getInstance(this);
final RealmResults<RealmCustomLocation> databaseLocations = realm.where(RealmCustomLocation.class).findAllSorted("timeStamp", RealmResults.SORT_ORDER_DESCENDING);
if(databaseLocations.size() >= 4){
realm.beginTransaction();
databaseLocations.removeLast();
realm.commitTransaction();
}
This is exactly like what is written at the Realm.io instructions about deletion:
realm.beginTransaction();
result.removeLast();
realm.commitTransaction()
But when I execute the code it always breaks with a RealmException
io.realm.exceptions.RealmException: Removing object is not supported.
Then I looked at the source code of RealmResults.java and I find this:
So no wonder it keeps crashing, removeLast() does nothing, only throw an error!
So my question is: How can I remove an object from the database then?!
I am using realm.io 0.77 (compile 'io.realm:realm-android:0.77.0') on Android.
I appreciate your help on this!
I have contacted Realm.io support, awaiting an answer. For the meantime:
RealmCustomLocation location = databaseLocations.get(databaseLocations.size() - 1);
location.removeFromRealm();
works equivalent to
databaseLocations.removeLast()
so it can be used as a workaround.
Edit: Support told me that they are fixing it for future versions and recommended to use the workaround I posted for in the mean time.
If you want to delete all objects, then I would create a while loop like this:
while (location.size() > 0) {
location.removeLast();
}
I am getting memory exception anonymously, i don't know which block of code causes it. but i know its from realm. Is it because i'm using single insert instead of bulk insert.
realm.beginTransaction();
summary = realm.createObject(ActivitySummary.class);
JSONObject activity = activities.getJSONObject(i);
JSONArray datas;
summary.setActID(activity.getInt("actID"));
summary.setActName(activity.getString("actName"));
summary.setSourceID(activity.getString("sourceID"));
datas = activity.getJSONArray("data");
for (int j = 0; j < datas.length(); j++) {
JSONObject data = datas.getJSONObject(j);
Date endTime = !data.getString("endTime").equals("null") ? new Date(data.getLong("endTime")) : new Date();
summary.setEndTime(endTime);
summary.setLogID(data.getString("logID"));
summary.setDate(data.getInt("date"));
summary.setStartTime(!data.getString("endTime").equals("null") ? new Date(data.getLong("startTime")) : new Date());
summary.setValue(data.getString("value1"));
summary.setValue2(data.getString("value2"));
realm.commitTransaction();
}
Exception 12-23 10:40:10.302: A/libc(13101): Fatal signal 11 (SIGSEGV) at 0xd1d1d1cd (code=1), thread 13101 (com.lifestyle) –
You definitely need to close your Realm instances by calling realm.close()
If you are using Realm on multiple threads, than you might have stumbled on Realm.io bug where it was sending update notification messages to dead threads/Realms.
It had been fixed in version 0.76.0 by adding this piece of code in close() method:
if (handler != null) {
handlers.remove(handler);
}
Updating to newest version fixed this same error for me.
You need to keep track of all your Realm.getInstance() and realm.close() - they have to be called exactly same number of times for each thread. Even though the Realm object itself is only one per thread, calling getInstance() iterates it's ref counter by one, and close() decreases ref counter by one. Realm will be dismissed when ref counter gets to 0.
There is a bug in the above code. A beginTransaction() must have a matching commitTransaction() and vice versa. It is recommended to use executeTransaction() to ensure that. So you either have to move the beginTransaction() inside the loop or the commitTransaction() outside the loop. It's not entirely clear if that's your memory problem though.