Miller Mosquera
Miller Mosquera

Reputation: 119

Duplicate objects from Firebase loaded into ListView

When I add new message to a listview, that adds message that I have and the new message, so it puts the same information twice.

I want to load last messages in a listview with Firebase, I have the follow function into create():

firebase = new Firebase(FIREBASE_URL).child(FIREBASE_CHILD + "/" + sharedPreferences.getString("chatKey", null).toString() + "/room/");

firebase.child("messages").addValueEventListener(new ValueEventListener() {

    @Override
    public void onDataChange(DataSnapshot Snapshot) {

        if (Snapshot.getValue() != null) {
            Iterable<DataSnapshot> iterator = Snapshot.getChildren();
            itemMessage itemData;

            for(DataSnapshot value : iterator){
                itemData = value.getValue( itemMessage.class );
                mCDataAdatapter.add( itemData.getMessage().toString() );
            }

           mConversation.setAdapter( mCDataAdatapter );
        }
    }

And for add new message:

sendMessage.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {

        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(ChatActivity.this);

        SharedPreferences.Editor editor = sharedPreferences.edit();

       // editor.clear().commit();

        if( sharedPreferences.getString("chatKey", null) != null && sharedPreferences.getString("chatKey", null).toString() != "" ){

            messageLevel = new Firebase(FIREBASE_URL+sharedPreferences
                    .getString("chatKey", null)
                    .toString()+"/room/")
                    .child("messages");

            Map<String, Object> messageData = new HashMap<String, Object>();
            messageData.put( "message", message.getText().toString() );
            messageData.put( "userTo", 1 );
            messageData.put( "userFrom",2 );
            messageLevel
                    .push()
                    .setValue(messageData);

            mCDataAdatapter.add( message.getText().toString() );

            mConversation.setAdapter( mCDataAdatapter );

            message.setText("");

        }


    }
});

Upvotes: 2

Views: 3152

Answers (2)

pr0gramista
pr0gramista

Reputation: 9008

You shouldn't add messages any other way than by Firebase listener, but I guess you are doing it at line mCDataAdatapter.add( message.getText().toString() ); after sending the message. The reason is that Firebase will automatically call onDataChange when you add new message even on client who sent it.

I would also recommend using ChildEventListener, otherwise you should clear the whole ListView or filter the change to avoid same data. Are you doing it?

Upvotes: 0

Frank van Puffelen
Frank van Puffelen

Reputation: 598901

When you use addValueEventListener(), your handler will be invoked immediately with the current value of the node and subsequently each time the value of the node changes. Each time the handler is invoked, it gets a snapshot of the entire contents that you listen for. So if you start with 3 messages

message 1
message 2
message 3

Your onDataChange() will get invoked with these 3 message. If you then add a 4th message:

message 1
message 2
message 3
message 4

Your onDataChange() will get invoked with all 4 messages. If you stick to your current code, you will have to detect and remove the duplicates yourself.

Luckily the Firebase SDK also allows you to listen for child level changes by calling addChildEventListener(). From the linked documentation:

ref.addChildEventListener(new ChildEventListener() {
    public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
        Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());

        // A new comment has been added, add it to the displayed list
        Comment comment = dataSnapshot.getValue(Comment.class);
    }

    public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
        Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey());

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so displayed the changed comment.
        Comment newComment = dataSnapshot.getValue(Comment.class);
        String commentKey = dataSnapshot.getKey();
    }

    public void onChildRemoved(DataSnapshot dataSnapshot) {
        Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey());

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so remove it.
        String commentKey = dataSnapshot.getKey();
    }

    public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
        Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());

        // A comment has changed position, use the key to determine if we are
        // displaying this comment and if so move it.
        Comment movedComment = dataSnapshot.getValue(Comment.class);
        String commentKey = dataSnapshot.getKey();
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        Log.w(TAG, "postComments:onCancelled", databaseError.toException());
    }
});

If you use this approach, initially your onChildAdded() will be called 3 times, once for each item. Then when you add the 4th child, you onChildAdded() will be invoked again with just the 4th item.

Upvotes: 5

Related Questions