Why onChildAdded is fired multiple times? - android

I have a problem with my code, I don't know if I get it because of onChildAdded or RecyclerView
Simply I want to display messages Inbox on RecyclerView that contains all messages I received from other users, each message contains the last message sent and information about senders such as his name and photo.
Like this
This is my structure
Messages
Receiver_UID
Sender_UID
push
message : "text"
I used 2 nested query to achieve that
so firstly I get sender UID, and then I get the last message from another query
dataQuery = FirebaseDatabase.getInstance().getReference("Messages").child(my_Id);
dataQuery.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot getalldata, String s) {
final String sender_UID = getalldata.getKey();
Query lastmess = FirebaseDatabase.getInstance().getReference("Messages").child(my_Id).child(sendUID);
lastmess.orderByKey().limitToLast(1).addChildEventListener(new ChildEventListener() { // last message
#Override
public void onChildAdded(DataSnapshot snap, String s) {
MessModel messages = snap.getValue(MessModel.class);
String Last_message = messages.getMessage();
getUserData(Last_message ,sender_UID);
}
#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) {
}
});
}
#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) {
}
});
And finally I get information of sender by this method
private void getUserData(final String last_message , String sendUID) {
FirebaseDatabase.getInstance().getReference("Users").orderByKey().equalTo(sendUID).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(final DataSnapshot data, final String s) {
listImessagesitem model = data.getValue(listImessagesitem.class);
name =model.getName();
uid = model.getUid();
//another information
result.add( new listImessagesitem( name ,last_message ,uid, ... another info));
useradapter = new listMessagesAdapter(getActivity(),result);
myList.setAdapter(useradapter);
useradapter.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) {
}
});
}
It works well the first time and when I make refresh as well
The problem is when someone, in the list of messages Inbox, sends me a new message. In this case, if I didn't make refresh, I see two senders with the same information and different last messages ... If he sent again, I see 3 senders with same previous case and so on.
How can I fix it?
I mean How can I add the new message to the user if he already exists in my inbox list
Any help would be appreciated, Thanks

What I understand from looking at your code is that u have nested ChildEventListener's. So every-time the top listener is invoked a new inner child listener is created causing duplicate data as it calls the getUserData method to update the adapter.
What you can do?
1: Check if the result list contains the object if not then add otherwise ignore.
2: From what I understand, I believe you are trying to create a Mail Client application where two users are sending emails to each other. So instead of the structure that you have in your db why not create a single node for both the users something like this user_1_ID+user_2_ID .
This reduces the complexity as both the users will read from the same node and the message object should have a timestamp field to determine the latest message.

Related

How to cut down repeated Firebase Listener

There are ten activity java code, seven activity need to firebase data. so i added firebaseListener(addChildEventListener). Because data is updated in each activity, each activity need to each Listener.
In my mind, it can work just one code...(because many people will say this is redundancy)
service, thred..? what i have to use.
mDatabase.child("Room-List").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
String key = dataSnapshot.getKey();
if(key.equals(roomName)) {
room = dataSnapshot.getValue(Room.class);
gap = room.gap;
nextTime = room.nextTime;
gameMode = room.gameMode;
break;
}
}
}
#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) { }
});
//Member-List - nickName, online, state, position
mDatabase.child("Room-List").child(roomName).child("Member-List").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
AllnickName = "";
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
Person person = dataSnapshot.getValue(Person.class);
if(person.online) {
online++;
AllnickName += person.nickName;
break;
}
}
TextView_person.setText(AllnickName);
TextView_roomName.setText(roomName + online);
}
#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) { }
});
}
There is no need for a Service or Tread to do this. When your app start you can spin up a Singletons in Android. In the Singleton you can host the ChildEventListener code and it can be accessed by any Activity who need the ChildEventListener Data.
If your Activity closes and restarted the Singleton will not be affected by that.
What is the purpose of Singleton?
The purpose of the Singleton class
is to control object creation, limiting the number of objects to only
one. The singleton allows only one entry point to create the new
instance of the class.
Since there is only one Singleton instance, any instance fields of a
Singleton will occur only once per class, just like static fields.
Singletons are often useful where you have to control the resources,
such as database connections or sockets.
If user or Android system closes your app the ChildEventListener and Singleton is also killed until user start your app again. If you need the ChildEventListener to be auto restarted in this case and so to speak "always be alive" you have to use a [Android Service][2]

Firebase onchildadded() returns same data 3 times in listview

My problem is the same with title,firebase onchildadded() returns same data 3 times in listview.I tried many things but still couldnt found solution.
My code is there;
public void ReceiveMessages(){
childEventListener = message_dbref.child(user_id).child(user2_id).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Message message = dataSnapshot.getValue(Message.class);
message_dbref.removeEventListener(childEventListener);
user_dbref.removeEventListener(valueEventListener);
messageArrayList.add(message);
messageAdapter.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) {
}
});
}
So you should not be using a childEventListener because it fires whenever any child of the parent node changes.
You can check if you're updating other children of the parent node on which your childEventListener is set, along with others. That must be the case, for it to fire 3 times.
So you should rather focus on using a singleValueEventListener or a valueEventListener on the same reference you're using right now.
Read more about childEventListeners here.
Understand more about reading and writing on Firebase here.

RecyclerView swipe to add older Firebase Data

I am facing a problem with adding older data to RecyclerView with SwipeRefreshLayout so, here is how i retrieve from Firebase and put data into RecyclerView
private void messageList() {
referenceMessages.orderByKey().limitToLast(10).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
MessagesInfo messages = dataSnapshot.getValue(MessagesInfo.class);
result.add(messages);
adapter.notifyDataSetChanged();
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
MessagesInfo messages = dataSnapshot.getValue(MessagesInfo.class);
result.remove(messages);
adapter.notifyDataSetChanged();
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
as you can i see, I set limit of retrieved messages to last 10, what means the 10 newest messages will be displayed. So far everything works great, the messages are displayed like that
-20thmessage(the oldest)-
-21stmessage-
-...-
-29thmessage-
-30thmessage(the newest)-
now i use SwipeRefreshLayout like that
private void swipe(){
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
referenceMessages.orderByKey().endAt("-LHUhkub4iaZNNzmtUcs").limitToLast(10).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
MessagesInfo messages = dataSnapshot.getValue(MessagesInfo.class);
recyclerView.scrollToPosition(0);
result.add(0, messages);
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) {
}
});
}
});
}
and it retrieves data properly, i mean previous 10 messages before hardcoded endAt() but after swiping places data not properly. If i dont use index 0
new data is placed at the botoom of latest message and it looks like that
-30thmessage(the newest)-
-10thmessage-
-...-
-19thmessage-
-20thmessage(the newest)-
and when i use index 0 data is added from the top of already loaded messages but like that
-20thmessage(the oldest)-
-19thmessage-
-...-
-11ththmessage-
-10th-
so it is added upside-down. Hope you understand me :) any inputs are highly appreciated
You might try clearing the list first and then add the new messages.
Android SwipeRefreshLayoutBasic

How can I get value of this child?

It's not current id that user logged... it's other uid from other user when other user send a request.
From what I understand, you want to get to know whenever you receive a friend request. I see that you've created a Friend_req node and have each user's keys inside it. This is good. I assume that you do have a minimal grasp of Firebase and flattening of data.
With this assumption, my answer is that you need a childEventListener on the node that you need to track for friend requests. The childEventListener has a onChildAdded() method that downloads data whenever a new child is added to the code ( in your case, a new friend request ). Here's a basic implementation.
FirebaseDatabase.getInstance().getReference().child("Friend_req")
.child(yourUserKey).addChildEventListener(new ChildEventListener() {
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
//Get notified on friend request
}
#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) {
}
});

Firebase: Retrieving data sorted by child value

My structure is like following
- Posts
- keu23jndf
- dateTime:"2376464"
- name:"abc"
- key34njf
- dateTime:"4333434"
- name:"xyz"
Now, I want to retrieve sorted data by dateTime in descending order.
Thanks
Ok so first of all I would suggest you to not use a client sided timestamp but instead use the ServerValue.TIMESTAMP. In most cases this is going to be the better setup. If you know what you are doing and you want it exactly like that, forget this and just read on.
The problem if you want to sort from newest to oldest is that Firebase does (as far as I know) not support this.
A workaround would be to use the negative value of the Firebase ServerValue.TIMESTAMP.
This is how I do it. It may not be the perfect or right way, but it does work
private void prepareUpload() {
//mDatabase is a reference to the root of the Firebase Database
final DatabaseReference timestampReference = mDatabase.child("timestamp");
final String timestampKey = timestampReference.push().getKey();
timestampReference.child(timestampKey).setValue(ServerValue.TIMESTAMP);
timestampReference.child(timestampKey).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.getValue() != null) {
Long timestamp = 0 - Long.parseLong(dataSnapshot.getValue().toString());
upload(/*Starting upload with new timestamp here (this is just a dummy method)*/);
timestampReference.child(timestampKey).removeValue();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e(TAG, databaseError.getMessage());
}
});
}
Now you have to set your Firebase Database rules as follows:
"Posts": {
".indexOn" : "date",
...
}
Finally you just have to query your 'Posts' somehow like this:
mDatabase.child("Posts").orderByChild("dateTime").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
//Do stuff with your data
}
#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) {
}
});
Hope this helps!

Categories

Resources