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);
Related
On application start the ChildEventListener is getting fired the amount of child present in the firebase database. For instance, if I have already 3 children present in the database, the listener will get invoke 3 times when I start my application. s I have defined the code for the listener in my service class and I am starting the service from my Splash class.
From SplashScreen.class
startService(new Intent(this , BackgroundService.class));
From BackgroundService.class
listenToJob() method, is in onStartCommand() method of Service
private void listenToJob (){
String collectionName = "rideRequest";
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference(collectionName);
ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot snapshot, #Nullable String previousChildName) {
Toast.makeText(BackgroundService.this, "Hey, new child is added.", Toast.LENGTH_SHORT).show();
OnChildCreated(snapshot);
}
#Override
public void onChildChanged(#NonNull DataSnapshot snapshot, #Nullable String previousChildName) {
}
#Override
public void onChildRemoved(#NonNull DataSnapshot snapshot) {
}
#Override
public void onChildMoved(#NonNull DataSnapshot snapshot, #Nullable String previousChildName) {
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
};
myRef.addChildEventListener(childEventListener);
}
I don't what cause this behavior. Isn't onChildAdded() method should only be invoked when a new child is added to the database? I have 3 children already present in the database and when I open my app this onChildAdded() method is getting invoke 3 times.
I want this method to fire up only when child is added to the database!
According to the Firebase documentation on listening for child events, the onChildAdded:
is triggered once for each existing child and then again every time a new child is added to the specified path.
So what you're seeing is the expected behavior.
If you want to only be informed of new children, you need to have something that defines when a child is "new". For example, if you're using Firebase's built-in push() method to generate the child nodes, you can use the keys that generates to only get child nodes that are generated after you start listening with:
String key = myRef.push().getKey()
myRef.orderByKey().startAt(key).addChildEventListener(childEventListener);
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
I know that when we retrieve data from Firebase ,it will be asynchronous, so ussally i will put all the code inside addChildEventListener, like example i want to sort userList below. But i am confused, if the List is really big, like million Users, so it means the method sortUser(user) will be called million times ? Can anyone explain this to me, I'm new to firebase
myRef.child("User").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
User user= dataSnapshot.getValue(User.class);
userList.add(user);
sortUser(userList);
}
#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) {
}
});
You currently use a ChildEventListener, which means your onChildAdded gets called for each child node immediately and then later whenever a new child is added. This indeed can be a lot of invocations.
If you use a ValueEventListener, its onDataChange will only be called once for the initial data (no matter how many child nodes there are), and then once for each change.
By adding a ValueEventListener to your current set up, you can keep things simple: add the child nodes to the lit like you're already doing, but only sort in onDataChange.
myRef.child("User").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
sortUser(userList);
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
};
Firebase will only synchronize the data for the User node once, even when you have two listeners on it.
You should probably retrieve the data sorted server side by using order-by methods and then listen to that one.
var userRef = firebase.database().ref('posts').orderByChild('Users');
If I guess correctly, you would not need separate sorting client side.
You can also filter data. Do refer the docs
I am new to Firebase Real-time Database and don't have an idea that how to fetch only the recent updated object from the table.
I have tried using "ChildEventListener" but when I initialize the Listener for the first time it fetches the last row from the database.
It should not be fetched if it was not updated.
I want object only when it is updated or newly added.
I have done this,
databaseReference.child("last_chat").orderByChild("s_id").equalTo(preference.getUserData().getId()).limitToLast(1).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Log.e("dataSnapShot", dataSnapshot.toString() + " " + s);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
Log.e("dataSnapShotChagned", dataSnapshot.toString() + " " + s);
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Log.e("dataSnapShotRemoved", dataSnapshot.toString());
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e("onCalcnelle", databaseError.toString());
}
});
Thank you in advance.
I have solved the issue.
I just need to put my code inside the OnChildChanged instead of putting my code in OnChildAdded.
Log dataSnapShot shown when first running the application, whereas as soon as I change or update object dataSnapShotChagned is shown.
Thus it worked for me, OnChildAdded method is called every time while initializing the listener where OnChildChanged method is called only when there is a change in a particular object.
Thanks a lot all for the help.
public ValueEventListener addValueEventListener (ValueEventListener listener)
Also: Google Play services
Add a listener for changes in the data at this location. Each time time the data changes, your listener will be called with an immutable snapshot of the data.
Firebase Doc
Hope this helps.
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.