Firebase Database users know that there are two basic listeners for listening Data: ValueEventListener
and ChildEventListener
. It works great when we listen for one object, but becomes quite difficult when we listen for some collection.
To specify question, let's imagine that we have HackerNews feed, and we listen e.g. "posts" object in Firebase.
Of course we have RecyclerView
in our app for displaying posts and I think good idea would be using FirebaseUI, but the problem is that we want to make more abstract application in case of changing server side or tests. So we would use some adapters, but this is another question.
We have two listeners as I mentioned, question is which is better?
When we use ValueEventListener
we will get whole collection, but in case of any changes, like one user changed content of the post, we will have to reload whole data, which means more bytes sending via expensive network transfer. Another problem is when we use multilisteners, and here is example:
Post has userId, but we want to display his name, so in onDataChanged
method we have fetch user data, like this:
postsReference.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { for (DataSnapshot data : dataSnapshot.getChildren()) { Post post = data.getValue(Post.class); usersReference.child(post.getUserId()).addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { // Here we have user data } @Override public void onCancelled(FirebaseError firebaseError) { } }); } } @Override public void onCancelled(FirebaseError firebaseError) { } });
You can see that now we have to add each post to RecyclerView
separately, which would give us clue that maybe we should use ChildEventListener
.
So now when we use ``ChildEventListener the problem is the same - we have to add each post to RecyclerView
separately but when someone changes post content, firebase sends to us only this one post, which means less data via network.
We don't like adding post separately to RecyclerView
, because e.g.: - It's hard to add loading indicator, cause we don't know when all data comes. - Users gets constantly refreshing view, while new posts comes instead of whole lists becomes visible. - It's hard to sort that collection, we will have to do that maybe in adapter.
Question
What are best practices for using firebase with collection, and maybe better solutions than I wrote above?
EDIT
Data scheme would look like this:
"posts" : { "123456" : { "createdAt" : 1478696885622, "content" : "This is post content", "title" : "This is post title", "userId" : "abc" }, "789012" : { "createdAt" : 1478696885622, "content" : "This is post content 2", "title" : "This is post title 2", "userId" : "efg" } } "users" : { "abc" : { "name" : "username1" }, "efg" : { "name" : "username2" } }
EDIT 2
I made a mistake -> Firebase is not fetching whole data in ValueEventListener
when something has changed. It gets only "delta", here is proof.
There are several concerns in this question (namely performance, progress indicator, handling new data such as sorting them). Naturally you should come up with a solution that takes into consideration the priority of your requirements. IMO both ValueEventListener
and ChildEventListener
have their use cases:
ChildEventListener
is generally the recommended way of sync'ing lists of objects. This is even mentioned in the documentation on working with lists:
When working with lists, your application should listen for child events rather than the value events used for single objects.
This is because your client only receives the changed child with the specific update (addition or removal) as opposed to the whole list on every update. Therefore, it allows a more granular level of handling of the updates to the list.
ValueEventListener
can be more useful when you need to process the entire list upon modification of a child. This is the case when you have to sort the list in your RecyclerView
. It's much more easier to do this by getting the entire list, sorting it and refresh the data set of the view. On the other hand, using a ChildEventListener
, it's more difficult to do the sorting because you only have access to one specific child in the list per update event.
From a performance point of view, given that even ValueEventListener
is aware of the "delta" while sync'ing the update, I would tend to think of it as less efficient only on the client side, because the client has to do the processing on the entire list, but this is still much better than having the inefficiency at the network side.
Regarding constant refresh and progress indicators, the most important thing to note is that the Firebase database is a realtime database, hence a constant feed of realtime data is an inherent characteristic. If update events are not needed, you can simply use the addListenerForSingleValueEvent
method to read the data only once. You can also use this if you want to display a progress indicator upon loading the first snapshot:
// show progress indicator postsReference.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { // load initial data set // hide progress indicator when done loading } ... });
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With