I'm starting to approach this wonderful world of Realm. I'm very happy of the results I'm getting and now I have one question to submit.
In my android app I've got a Fragment that displays data retrieved from Realm. The query condition is that the time this data refers to is in between the beginning and the end of today.
RealmResults<Appointment> results = realm
.where(MyObject.class)
.between("begin", rangeBegin, rangeEnd)
.between("end", rangeBegin, rangeEnd)
.findAllSorted("begin", Sort.ASCENDING);
This query is executed in the onStart() method helping me to exploit the live-update feature, which indeed works very well.
I've also added listeners for changes in order to optimize UI updates.
Now the question is: how does this live-update behave if the time conditions change? (Imagine I keep the app opened for more than one day without touching it or simply I keep the app opened for minutes around midnight)
From what I've seen it seems to do the same query done the very first time onStart() was executed.
Is there a way to have also live-updating query or should I re-run that query somewhere else outside onStart()?
Thank you in advance
Now the question is: how does this live-update behave if the time conditions change? (Imagine I keep the app opened for more than one day without touching it or simply I keep the app opened for minutes around midnight)
The query is pretty constant after you've set it up, so you'd need to execute a new query with different parameters for rangeStart and rangeEnd, and replace your other results.
This query is executed in the onStart() method helping me to exploit the live-update feature, which indeed works very well. I've also added listeners for changes in order to optimize UI updates.
Personally I'd advise to put the query in onCreateView() instead, and the Realm lifecycle management to onCreateView() and onDestroyView().
Also, you can avoid manually assigning RealmChangeListeners for displaying lists if you use RealmRecyclerViewAdapter (adapters 1.3.0 works with realm 1.2.0).
If you use RealmRecyclerViewAdapter, then just call adapter.updateData(newResults); and it'll update the view as needed.
Related
Context
I'm working on an offline first app, and I'm basically working on a screen that is supposed to show a list of store offers - discounts, coupons, things like that - I'm trying to rely on Realm queries via Flowables to fetch the data, and I want the app to be 100% honest with what we have on the Realm DB. I mean, that if something changes in the database the app should immediately refresh the UI, that's why I'm using Flowables.
Problem
This approach has been working fine so far, but I've stumbled across this specific situation where things are a bit tricky. I need to display this list of store offers like this:
private val offersFlowable: Flowable<RealmResults<RealmOffer>>
get() {
val realm = Realm.getDefaultInstance()
return realm.where(RealmOffer::class.java)
.equalTo("userId", CredentialManager.getInstance().getUserId())
.and()
.beginGroup()
.isNotNull("expiryDate").and().greaterThan("expiryDate", Date())
.endGroup()
.findAll()
.asFlowable()
}
The query itself works perfectly, I fetch the user's offers which haven't expired yet. The only problem is that it seems I need to recreate this query every time I want to refresh the data. Why? Because of the Date(). I open the Activity that renders this RecyclerView list of store offers, I instantiate the query and start listening for the changes - so far so good - but if I have a store offer that is about to expire in a minute or so, wait that full minute and do a Pull To Refresh, the expire offer doesn't go away. Again, because of the Date() because the current date was instantiated a minute ago and it is still that date.
If I kill the app or instantiate the Activity from scratch, everything works as expected because I'm instantiating the query again, therefore getting an up to date Date().
What I've tried so far
Instead of hardcoding that Date() arg there, having a currentDate property in my ViewModel - the place where this Flowable lives - I was kinda hoping that if the date arg was a reference to a computed Kotlin property that would do the trick, but it didn't work.
Same thing as above but I changed the expiryDate field to millis instead of a Date.
The Workaround
So, the only thing that works right now is just re-instantiating the Flowable query every time I do a PTR, that way I pass an up to date Date() arg and the query works as expected.
My Question
Is there any way to fix this without having to re-instantiate the query all the time?
Thanks! Appreciate all feedback! 🙇
I hope that I have understood your problem right, I would like to put a suggestion may be that help you. The current work around for sure is not a good option because of requesting the database again and again. so what in my opinion would be better for this situation is,
Before rendering the data on the RecyclerView, Do the following;
1 - get the expiry date & time of each item.
2 - get current date & time.
3 - Compare both of them and get the difference.
Now, if the difference is -ve value, it means the item is expired and you don't have to show it in recyclerView. Of course the item is available in ArrayList/List but it is not being rendered on recyclerView.
Otherwise Second Option
At the time of adding the store offers data to the database, just register a PendingIntent for every offer along with an AlarmManager and set the Expriy-time as the alarm time for the PendingIntent.
This way, when the offer expires, it will throw an alarm. So, here you could do the magic, when the PendingIntent get trigger, just update your database and recyclerview from there. This way you can handle every offer automatically.
Let me know if it helps.
Happy coding :)
I have a project that loads a list from the server. This data will eventually be stored into a database, but for now is stored in memory in a MutableLiveData. A RecyclerView's adapter is watching the data and displaying it. So far everything is working as expected, using a FAB the user can post a new entry which will go at the top of the list, on success I get a 200 and here's the main part where I'm getting lost...
When I want to add a single item to a list stored in a LiveData, the observer is unaware of the delta. I currently make a call to RecyclerView.Adapter.notifyDataSetChanged(), though the ideal in my case would be to call notifyItemInserted(0) or in other cases I can see various other notifications. What the best way to do this? The lifecycle architecture library appears to be very well thought of, I assume I'm missing something simple. I can't imagine having to manually perform a diff between the lists?
I am building an app that displays a bunch of information that the user can edit, and I am having trouble keeping the UI updating the data displaid so it is consistent with the new edits done at runtime.
I have implemented updateUI methods that basically look like:
void updateUI(){
((TextView) fieldDisplay).setText(fieldCurrentValue);
...
}
I know I can run this method in things like an AsyncTask or similar stuff that makes it execute continuously. But Im concerned about efficency since its not really necesary to update the UI all the time, but only when the user inputs a value wich is not that often.
What would be the best approach to this?
EDIT:
The question is how to make sure the an UI element shows the current value of a field, regardless of how or why that field is updated.
My case specifically is the with this. Im trying to make an RPG character sheet, and I have like a bazillion Skills, wich are affected by another lot of fields, such as Characteristics, Modifiers, Categories...
The application should behave so, whenever any of the many fields affects it changes, it should display the new value.
Now, since keeping track of what field affects what is part of the problem, if could update whenever any field whatsoever changes, but I dont know how to do that.
My queries with .equalTo() return out-of-date data when used with addListenerForSingleValueEvent, while removing .equalTo() causes the listener to return updated data. Any idea why?
.
I'm using the following query to fetch user's posts from Realtime Database with persistance enabled on Android:
mDatabase.child("posts").orderByChild("uid").equalTo(id)
where id is the id of the current user and each post stores its author's id as a field.
When .equalTo(id) is present, the new posts for the particular user are not returned in that query for the first few minutes. Even more, it seems to affect other queries for the same root ("posts") that contain .orderByChild. Eg, following would also fail to recognise the new post:
mDatabase.child("posts").orderByChild("archived")
Once I remove the .equalTo(id) the behaviour goes back to normal. I'm using addListenerForSingleValueEvent. Tried it also withaddValueEventListener which fires two events, one without the new post, one with it. Without .equalTo(id) both single and non-single listeners return the new post in the first callback. Restarting the app doesn't seem to help straight away - the first event stays out-of-date for the next few minutes. The new post is successfully fetched by different queries in other parts of the application (eg mDatabase.child("posts").child(id))
Any idea why .equalTo() causes such behaviour and how to avoid it (other than using non-single listener and ignoring first event)?
Note 1: same thing happens for .startAt(id).endAt(id)
Note 2: other parts of the Realtime Database are functioning normally, device is connected to the internet and new posts are containing the valid uid field matching the current user.
Update 26/10/2016
Calling mDatabase.child("posts").startAt(key).limitToFirst(4) also produces similar behaviour when trying to query a segment of the database (in our case to implement infinite scroll). It seems that explicitly adding .orderByKey() fixes that particular problem: mDatabase.child("posts").orderByKey().startAt(key).limitToFirst(4).
Though the issue outlined in the original question remains.
I've ran into the exact same problem as you, and after experimenting with almost everything, I've managed to solve it on my end.
I'm scanning barcodes and fetching foods that have the scanned barcode:
Query query = refFoods.orderByChild("barcode").equalTo(barcode);
query.addListenerForSingleValueEvent(new Value ... })};
On my rules i had
".indexOn": "['barcode']"
and after i changed it and took the "[]" out as in:
".indexOn": "barcode"
it started working delay free, where before it would take something like 5 minutes to udpate.
I have a ListView bound to a SimpleCursorAdapter, and I want it to refresh when I modify the database (by inserting, updating or deleting rows). cursor.notifyDataSetChanged() has no effect (it's called on the UI thread) and ListView.removeViewAt(int) throws an UnsupportedOperationException.
What am I supposed to do on Android to get such a basic behavior?
Note that the database is correctly affected and the modification is shown when I restart the activity. But restarting the activity is not an option here, and changing the ListView adapter is the last resource here, since it's a hack and can't guarantee a smooth transition
DISCLAIMER
Quite basic question, asked millions of times and answered zero.
Please, do not answer if you have never done this in your code, don't ask for mine, and don't bother with try this or try that. Only answer if you know how it's done
From API >= 11 the way to do this is using a CursorLoader, this is also included in the Android Compatibility Library, so you can also use this if you are targeting a previous Android version. CursorLoader will make the query in a background thread and return you the cursor. You will need to implement a ContentProvider. You can read the documentation to get an idea of how to use it. Basically you init a loader and then you restart it when you know data has changed. In the callback you just swap the cursor of your adapter.
Or you can just use requery() on the Cursor. The adapter will get automatically notified of the changes. This method is deprecated now and, of course, it's not the recommended way.