0
public class DataService {

    private static DataService ourInstance = new DataService();
    private DatabaseReference mDatabase;


    public static DataService getInstance() {
        return ourInstance;
    }

    public ArrayList<UserDatabase> getFriendList() {

        mDatabase = FirebaseDatabase.getInstance().getReference().child("users");
        final ArrayList<UserDatabase> list = new ArrayList<>();

        mDatabase.addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                UserDatabase userDatabase = dataSnapshot.getValue(UserDatabase.class);
                list.add(userDatabase);
            }

            @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) {}
        });

        return list;
     }
}

I am trying to fetch my users database from the users node. It's acting really weird. When I set a breakpoint at the onChildAdded function, it will retrieve all the users and add it to the list.

However, if I don't set a breakpoint there, it is not getting anything and the list size is 0. Does anyone have any idea what is going on? Thanks in advance!

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • But then it is maybe getting called again and overwritten. Can you not move `list` to be a field? – Scary Wombat Sep 13 '16 at 08:01
  • i dont think it is being overwritten. when i set a breakpoint, at the return list after all the process, it returns the right data. but if not, it just doesnt even go to the onChildAdded function – Fahri Shihab Sep 13 '16 at 08:04
  • I'm not really familar with android/firebase but when is the listener called? – Steffen Harbich Sep 13 '16 at 08:15

1 Answers1

6

Data is loaded and synchronized from Firebase asynchronously.

It's easiest to see what's causing your problem if you add a few well-placed logging statements:

public ArrayList<UserDatabase> getFriendList() {

    mDatabase = FirebaseDatabase.getInstance().getReference().child("users");
    final ArrayList<UserDatabase> list = new ArrayList<>();

    System.out.println("Attaching listener");
    mDatabase.addChildEventListener(new ChildEventListener() {
        @Override
        public void onChildAdded(DataSnapshot dataSnapshot, String s) {
            System.out.println("Got data");
        }

        public void onChildChanged(DataSnapshot dataSnapshot, String s) {}
        public void onChildRemoved(DataSnapshot dataSnapshot) {}
        public void onChildMoved(DataSnapshot dataSnapshot, String s) {}
        public void onCancelled(DatabaseError databaseError) {}
    });

    System.out.println("Returning list");
    return list;
 }

The output will print in this order:

Attaching listener

Returning list

Got data

That is probably not the order you expected. But it does explain why the list of empty if you run the code as is.

The reason for this is that Firebase loads and synchronizes the data asynchronously. Like most modern web APIs it doesn't wait for the result to return (that would lead to a horrible user-experience), but instead lets the program continue and calls back into your code when it gets (new) data.

The way to deal with this behavior is to reframe the flow of your program from "first get the list of friends, then do xyz with it" to "whenever the list of friends changes, do xyz with it".

For example say that you simply want to print how many friends there are. Your current approach is probably:

ArrayList<UserDatabase> friends = getFriendList();
System.out.println("There are "+friends.size()+" friends");

With Firebase one way to handle this is to move the operation into getFriendList():

public void getFriendList() {

    mDatabase = FirebaseDatabase.getInstance().getReference().child("users");
    final ArrayList<UserDatabase> list = new ArrayList<>();

    mDatabase.addChildEventListener(new ChildEventListener() {
        @Override
        public void onChildAdded(DataSnapshot dataSnapshot, String s) {
            UserDatabase userDatabase = dataSnapshot.getValue(UserDatabase.class);
            list.add(userDatabase);
            System.out.println("There are "+friends.size()+" friends");
        }

        public void onChildChanged(DataSnapshot dataSnapshot, String s) {}
        public void onChildRemoved(DataSnapshot dataSnapshot) {}
        public void onChildMoved(DataSnapshot dataSnapshot, String s) {}
        public void onCancelled(DatabaseError databaseError) {}
    });

 }
Community
  • 1
  • 1
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • There is a [new question](http://stackoverflow.com/q/39518283/4815718) that is essentially a dup of this issue. I answered it and provided a link here. Feel free to close the new question as a dup, if you like. – Bob Snyder Sep 15 '16 at 19:06