I am using Firebase for the chat feature in my Android application. What I am aiming to support is:
- live updates whenever a new message arrives or an existing message changes or gets deleted and
- infinite scroll backwards in the history of messages.
The first part seems easy. I create a firebase reference with a limit.
ref = new Firebase(FIREBASE_URL).child("chat").limitToFirst(50);
This reference is then used by an Adapter which adds a listener. And then modifies a List whenever data changes.
ref.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {...}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {...}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {...}
...
The second part is where I struggle to find the right approach. When the user arrives at the end of the list, I want to load older messages and add them to the list of messages (infinite scroll).
The preferred solution would be to simply call something like ref.changeLimitToFirst(100)
but such a method does not exist.
What would be the best approach to solve this with firebase?
Edit:
I deleted the solutions initially suggested by me because they did not help clarifying the problem. Instead, I will list new solutions below which I found since I originally asked the question.
If you know of a better, more elegant approach, please leave an answer!
- The easiest approach seems to be to remove the listener from the old Query; Then create a new Query with a new limit. An example for this approach can be found in this pull request for the Firebase-UI-Android lib. Downsides are:
- After adding the new listener you will receive child_added event for each child, not only for the ones you are trying to load. Therefore you have to check if a child already exists in the ArrayList before adding it. This means there will be a lot of iterations if your list gets longer.
- You don't know when loading is completed. A workaround for this is to add an additional value listener, which will be fired once the complete snapshot has been loaded (and thus loading of the new page is completed).
- The second approach would be to load each page separately by listening to the value event. Unfortunately, something like offset() does not exist. So the way to go is to remember the last item you loaded and pass it to the startAt method. Downsides:
- In order to receive updates you'd need to remove the old child listener and add a new one (because the limit changes) each time you'll load a new page. This listener will then receive child_added events which you have to filter out (see above).
- The position of the last item might change between the time when you load the first page and the time you load and add the second page. If the order of your data might change, you can not be sure that child you saved still is the correct reference for your next Query. In this case you need, again, a child listener to receive updates on the data you previously loaded.