This question already has answers here:
Firebase child_added only get child added
(5 answers)
Closed 3 years ago.
I'm using Firebase for my android project.
There is something that I don't understand. I'm using a method addChildEventListener and for what i know this method should be triggered when a new child is added/deleted/changed and go on...
For some reason when my Activity is load up, this method triggers.
Isn't need to be triggered when a new child is added...?
Code
refToVideos.getReference(Params.VIDEOS).child(currentUser.getUid()).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot snapshot, String s) {
long countComments = (long)snapshot.child(Params.COUNTCOMMENTS).getValue();
int countComment = ((int) countComments);
String frameURL = (String)snapshot.child(Params.FRAMEURL).getValue();
String genre = (String)snapshot.child(Params.GENRE).getValue();
long like = (long)snapshot.child(Params.LIKES).getValue();
int likes = ((int)like);
String uploadDate = (String)snapshot.child(Params.UPLOADDATE).getValue();
String userProfile = (String)snapshot.child(Params.USERPROFILE).getValue();
String userUID = (String)snapshot.child(Params.USERUID).getValue();
String userName = (String)snapshot.child(Params.USERNAME).getValue();
String videoID = (String)snapshot.child(Params.VIDEOID).getValue();
String videoName = (String)snapshot.child(Params.VIDEONAME).getValue();
String videoURL = (String)snapshot.child(Params.VIDEOURL).getValue();
long view = (long)snapshot.child(Params.VIEWS).getValue();
int views = ((int)view);
Video video = new Video(videoID,userUID,genre,videoName,videoURL,frameURL,userName,userProfile,likes,
views,countComment,uploadDate);
videosList.add(video);
if(adapter != null)
adapter.notifyDataSetChanged();
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
From the documentation for onChildAdded:
This callback is triggered once for each existing child and then again every time a new child is added to the specified path.
So when you attach a listener, its onChildAdded method is 'immediately' called for any existing children in the location it listens to. This is how the API is defined, and you can't change it.
If you only want to hear about new children that were added after the moment you attach the listener, you'll have to have something like a timestamp in your child nodes that identifies whether they're 'new'.
Also see these previous questions about the topic:
Firebase child_added only get child added
How to only get new data without existing data from a Firebase?
Firebase: Get New Child Added
Related
here is my function to get data :
public void retrievedata(){
FirstRef.child(obj.getsEventID()).orderByChild("date").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s)
{
if (dataSnapshot.exists())
{
DisplayMessages(dataSnapshot);
}
}
#Override
public void onChildChanged(#NonNull DataSnapshot dataSnapshot, #Nullable String s)
{
}
#Override
public void onChildRemoved(#NonNull DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
private void DisplayMessages(DataSnapshot dataSnapshot) {
Iterator iterator = dataSnapshot.getChildren().iterator();
String Article = (String) ((DataSnapshot) iterator.next()).getValue();
String Key = (String) ((DataSnapshot) iterator.next()).getValue();
String Organisateur = (String) dataSnapshot.child("name").getValue().toString();
String date = (String) dataSnapshot.child("date").getValue().toString();
Date resultdate = new Date(Long.parseLong(date));
String date2 = DateFormat.format(resultdate).toString();
ListOfArticles.add(0,new ListItemTypeOne(Key, Article, Organisateur, date2));
adapter.notifyDataSetChanged();
}
Let's suppose I have 10 articles, they are kept in the disk memory thanks to :
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
Now, while I was offline someone added 2 more articles which makes 12.
If I go online, and execute the function "retrieve data", will it simply call the onchildadded with the 10 child in the memory and the 2 new child from the firebase database or will it download all of the 12 childs from firebase ?
When you attach a listener for data that is already present on the device, Firebase immediately satisfies the listener with the data that it has on the device.
So the Firebase client will immediately call your onChildAdded with the 10 child nodes that we persisted earlier.
It then sends a request to the server to get the most up to date version. It does this with a so-called delta-sync, meaning that it by sending a hash value of the local state, which the server compares to the current state in the database. The server then sends the delta back, which the Firebase client then uses to update its internal snapshot and the state on disk.
If there are any changes, the Firebase client then fires the correct local events to allow your application to update to the new state. So in the case where two child nodes were added, it will call onChildAdded for each of those.
If you were to use a listener with a limit, say limitToLast(10), then the Firebase client would also call onChildRemoved for the two children that are no longer within that query (since they were pushed out by the new children).
The previously cached 10 children will be loaded from disk and not transferred from the server again.
Here is my database and i want to arrange them by time...
My Code
mTimeReference = FirebaseDatabase.getInstance().getReference().child("Messages").child(UID).child(userId);
mTimeReference.orderByChild("Time").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
String time = dataSnapshot.child("Time").getValue().toString();
String message = dataSnapshot.child("Message").getValue().toString();
LastSeen lastSeeen = new LastSeen();
long lastTime = Long.parseLong(time);
String lastSeenTime = lastSeeen.lastSeen(lastTime, getActivity());
if(lastSeenTime == null) {
holder.setTime("Just Now");
}
else {
holder.setTime(lastSeenTime);
}
holder.setMessage(message);
linearLayoutManager.setStackFromEnd(true);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
I have an activity where all my friends ids are shown. I want them to be shown in the order of the time in the database (the last they texted).
My present code isn't working.
If you can't figure out the problem at least tell me the how to put an item of a RecyclerView on top. Like if i want to put the friend B on top how do I do it?
You can either order the chats clientside or use an indexed firebase adapter.
For that you need to create an additional structure in your database, as so:
root
- Chats...
- Messages...
- UserActiveChats
- UserId <- your id
- FriendUserId: TimeStamp
- FriendUserId: TimeStamp
Then you have to change your 'Add-Chat-Message (however you implemented it)' function to a WriteBatch which:
Creates the ChatMessage (as now, at /Messages/userId/friendId/..)
Creates/Sets the value at
/UserActiveMessages/userId/friendId:ServerValue.TimeStamp
This way, you can create a query at /UserActiveMessages/myUserId and order it by value (timestamp), which represents the last time they sent a message to you. That way you will have an ordered list of the latest users who sent you a message. Then all you need to do is use a indexed FirebaseRecyclerAdapter to display the actual chats instead of just their userId.
keyQuery = rootReference.child('/UserActiveMessages/userId').orderByValue();
chatsRef = rootReference.child('/Messages/userId/');
FirebaseRecyclerOptions<Chat> options = new FirebaseRecyclerOptions.Builder<Chat>()
.setIndexedQuery(keyQuery, chatsRef, Chat.class)
.build();
see how to implement the rest here: https://github.com/firebase/FirebaseUI-Android/blob/master/database/README.md#using-firebaseui-with-indexed-data
As i am creating the chat app, for it i am using the Firebase.
It works perfectly, only one problem which i am getting that each time i am getting whole list of data from Firebase, when new item is added or deleted into it.
What i want that , only newly added record should come from Firebase , not whole list of data.
Please check my below code for it
ArrayList<ChatMessage> MY_ARRAYLIST= new ArrayList<>();
MsgViewHolder viewModel = ViewModelProviders.of(this).get(MsgViewHolder.class);
LiveData<DataSnapshot> liveData = viewModel.getDataSnapshotLiveData();
liveData.observe(this, dataSnapshot -> {
if (dataSnapshot != null) {
MY_ARRAYLIST.clear(); // I NEED TO CLEAR THE ARRAY-LIST TO GET THE REFRESHED DATA..I DO NOT WANT TO LOAD WHOLE LIST ON EACH TIME
if (dataSnapshot.exists())
for (DataSnapshot ds : dataSnapshot.getChildren()) {
ChatMessage bean = ds.getValue(ChatMessage.class);
assert bean != null;
MY_ARRAYLIST.add(bean);
}
}
});
As in above code i am using MY_ARRAYLIST.clear() to clear data, to get the newly added record.On each time from DataSnapshot , i am getting the whole list of data, whenever new record added or deleted.Is their any method to get only newly added data NOT whole List from Firebase?
You can use addChildEventListener() to you DatabaseReference which will notify you on different callback method when any child is add/removed/updated/deleted or moved
databaseRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
// If any child is added to the database reference
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
// If any child is updated/changed to the database reference
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
// If any child is removed to the database reference
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
// If any child is moved to the database reference
}
#Override
public void onCancelled(FirebaseError firebaseError) {
Log.e("FirebaseListAdapter", "Listen was cancelled, no more updates will occur");
}
});
The DataSnapshot in each callback will provide you the information of the child
This is by design, in a real-time system there is no concept of the "latest" data because it's always changing. However, if you want to only display items added to the list after the page has loaded, you can do the following:
var newItems = false;
var eventsList = new Firebase('https://*****-messages.firebaseio.com/');
eventsList.on('child_added', function(message) {
if (!newItems) return;
var message = message.val();
$.notification(message.message);
});
eventsList.once('value', function(messages) {
newItems = true;
});
above answer original by (anant)
(my) another solution
if you have control over your database schema you can add a 'datetime' element in your object and store the value of the time just before adding it to database in Epoch format, then you can simply get the list of objects in newly added order with limit like this.
ds.orderBy("datetimeSent", Direction.DESCENDING).limit(10);
I think you can use this listener onChildAdded() I provided a link to the documentation.
Listen for child events
You can try Query for query on database,add on child as date and time and query based on that data filed after every result update the query to last sync date and time
I am making a public chat app using Firebase Real-time Database, but stuck on this problem. Like normal chat apps, I am displaying only few texts to the user and when/if the user scrolls the List is updated and more messages are displayed.
I have a listener to my Firebase reference, which is only displaying (say 10 messages):
int j = 10;
myRef.child("Chat").limitToLast(j).addChildEventListener(new com.google.firebase.database.ChildEventListener() {
#Override
public void onChildAdded(com.google.firebase.database.DataSnapshot dataSnapshot, String s) {
// Map<String,Object> datemsg = (Map<String,Object>)dataSnapshot.getValue();
Map<String,Object> msg = (Map<String,Object>)dataSnapshot.getValue();
String text = msg.get("Text").toString();
values.add(text);
#Override
public void onChildChanged(com.google.firebase.database.DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(com.google.firebase.database.DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(com.google.firebase.database.DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Now I have a scroll Listener that changes the value for j to j+10 but then the ChildEvenListener is not triggered and the new messages are not added to the listview.
I also tried using queries, but again having the same problem, after updating the value, how should I update the adapter?
No new child is added, thus the childevent listener is not triggered, but I want to change the size of
limitToLast(j)
and re-populate the list.
Any help would be appreciated.
Firebase queries are immutable. If you change the value you pass into limitToLast(), it becomes a new query.
So you must attach a new listener to the new query, and then repopulate the list with the items from that listener.
If you keep track of the key (dataSnapshot.getKey()) for each existing item in your list, you can optimize the updating of the list by checking if each item from the new listener is already present in the list.
I am trying to understand how the two are related. From the docs:
onChildAdded event is triggered once for each existing child and then again every time a new child is added to the specified path. The listener is passed a snapshot containing the new child's data.
And that:
The onChildRemoved event is triggered when an immediate child is removed. It is typically used in conjunction with onChildAdded and onChildChanged events. The snapshot passed to the event callback contains the data for the removed child.
So techincally i was expecting that the two events are triggered separately based on their roles: that is, onChildAdded will be triggered when i add new data while onChildRemoved is when i delete a child from the nodes.
However, when i add data this is what i log:
14:47:39.649 31305-31305/com.myapp D/addData_: onChildRemoved called
14:47:39.649 31305-31305/com.myapp D/addData_: onChildAdded called
onChildRemoved is called first before onChildAdded!! What's worse is that the data disappears from my listview. Someone help me understand what it is i'm doing wrong.
This is my data structure:
And my firebase ref:
ref = FirebaseDatabase.getInstance().getReference().child("sales/" + getId() + "/" + getDateTime());
//limit to the last data
final Query lastSale = ref.limitToLast(1);
//add on child event listener
lastSale.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
//get the data from firebase and add it to our model
MyModel model = dataSnapshot.getValue(MyModel.class);
String firebase_key = dataSnapshot.getKey();
Log.d("addData_", "onChildAdded called");
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
Log.d("addData_", "onChildChanged called");
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Log.d("addData_", "onChildRemoved called");
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
Log.d("addData_", "onChildMoved called");
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
I commented out his line and it worked!
//limit to the last data
final Query lastSale = ref.limitToLast(1);