2

I am using firebase, and trying to query data properly using firebase functions. A solution for querying data from many different parts of the database that I've been using, which I am highly against and am really unsure about, is simply adding a listener for single value event to the entire database, like so:

DatabaseReference myRef = FirebaseDatabase.getInstance().getReference();
myRef.addListenerForSingleValueEvent(...)).

What I would like to do is use proper querying for the parts of the database I'm listening in on, and remember data outside of those value event listener functions, like so:

Query lastMessageQuery = myRef.child("chat_messages").child(currChatID).orderByKey().limitToLast(1);
lastMessageQuery.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot lastMessageSnapshot) {
        for(DataSnapshot currMessageSnapshot: lastMessageSnapshot.getChildren()) {
            Message lastMessage = currMessageSnapshot.getValue(Message.class);

            if (lastMessage != null) {
                MessageViewBox currMessageView;
                if (isGroup) {
                    currMessageView = new MessageViewBox(
                            lastMessage.getMessage_text(), chatName, chatIcon, lastMessage.getTime_sent(), currChatID, true
                    );
                } else {
                    currMessageView = new MessageViewBox(
                            lastMessage.getMessage_text(), chatName, chatIcon, lastMessage.getTime_sent(), otherUserID, false
                    );
                }
                messageFields.add(currMessageView);
            }
        }
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {

    }
});

Collections.sort(messageFields);
L_MessageListingAdapter messagesAdapter = new L_MessageListingAdapter(
        mView.getContext(), R.layout.a_person_item, messageFields);
mListView.setAdapter(messagesAdapter);

The issue I'm running into is that by the time I want to fill the listview with my messageFields ArrayList, the list has forgotten all of its elements.

As of now, I know of four working solutions.

  1. I can do what I described initially, add a value event listener to my entire database, go to the snapshot containing all messages for a current chat, and then iterate through to the end. Clearly this is undesirable and would scale horribly

  2. I could save the last message id for each chat in a separate part of the database, use the approach of listening in on the entire database, and then only have one value to iterate through to get the last message information for each chat. But again, I don't believe this is necessary because the query function I outlined above works perfectly.

  3. I could call:

    Collections.sort(messageFields);
    L_MessageListingAdapter messagesAdapter = new L_MessageListingAdapter(
        mView.getContext(), R.layout.a_person_item, messageFields);
        mListView.setAdapter(messagesAdapter);
    

    inside of the query function after adding the item to the ArryList, BUT this repeats this segment of code for each chat, it reloads the list for as many chats that I have to go through, which again, is undesirable, and the biggest issue I have with this approach is that it doesn't address the larger issue I'm having, which isn't only a problem for this particular situation, but many situations that I'm running into.

  4. [DESIRED] So this brings me to the most logical issue I need to solve, which is the issue of keeping the data queried alive outside of the query function.

I'm almost positive that (4) is possible, and it seems like it would be the best practice, but I'm just unsure of how to do achieve this in an elegant way. I tried making the ArrayList private, private static, public, and public static. None of this is working.

Hopefully the question is clear, and I appreciate any advice that would solve this problem.

Haidar Hammoud
  • 83
  • 3
  • 12

1 Answers1

3

You cannot add those objects of Message class to your messageFields list inside onDataChange() method and see the same list populated outside this method. The list will be always empty due to the asynchronous behavior of the onDataChange() method. This means that this method is called even before you are getting the data from the database. So, a quick fix would be to use that list inside the method or, try to handle this asynchronous method by reading the last part of my answer from this post.

If you understand Kotlin, this article will also help:

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • 1
    I was beginning to realize that this was indeed the issue, and that the data wasnt actually being forgotten but rather my list was being created before the data was even fetched. I've looked at your solution and it will take some restructuring of my code to produce the desired outcome, but in general, is adding a single listener to the entire database a very bad practice? Or is it OK depending on what you do within that event listener? – Haidar Hammoud Jan 26 '18 at 08:38
  • 1
    You can add as many listeners as you need in your project but with one condition, to remove them accordingly to the life-cycle of your activity. You need to add a listener **only** to the nodes that you want to listen. It is bd to listen to entire database, it will be a waste of resources and bandwith. [Here](https://stackoverflow.com/questions/46443244/removing-continuous-firebase-listeners-on-fragment-change) is explained where to add/remove the listeners. – Alex Mamo Jan 26 '18 at 08:45
  • Alright thanks for the clarification and pointer, really appreciate it. – Haidar Hammoud Jan 26 '18 at 08:48