1

Kind of hard to explain. Basically, I have a a list called data, and I need to add to that list. This is the code I use to do that...

 public static List<WorldBrowseData> getData() {

    data = new ArrayList<>();

    ref.addListenerForSingleValueEvent(new ValueEventListener() {

        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {

            for(DataSnapshot children : dataSnapshot.getChildren()) {

                WorldBrowseData current = new WorldBrowseData();
                Log.v("CHILDREN",children.toString());
                current.image = children.child("image_url").getValue().toString();
                //Log.v("IMAGE",current.image);
                data.add(current);
                Log.v("DATA IN METHOD",data.toString());

            }

        }

        @Override
        public void onCancelled(FirebaseError firebaseError) {

        }
    });

    Log.v("DATA IS",data.toString());
    return data;

}

When I log "DATA IN METHOD", it returns the updated, correct list. But when I log (and return) "DATA IS", it's just an empty list.

Also, it logs "DATA IS" before it logs "DATA IN METHOD", so basically, it gets called, returns data, then updates data, when I need it to update then return.

What am I doing wrong?

mmark
  • 1,204
  • 12
  • 19
  • Different Firebase client platform, same problem: http://stackoverflow.com/questions/27049342/asynchronous-access-to-an-array-in-firebase/27050749#27050749 – Frank van Puffelen May 05 '15 at 00:23

3 Answers3

2

The onDataChange method is executed asynchronously. In your case, the order of execution is the following:

 1) data = new ArrayList<>();
 2) ref.addListenerForSingleValueEvent(...
 3) Log.v("DATA IS",data.toString()); (data is not modified yet)
 4) Log.v("DATA IN METHOD",data.toString());(when the event is called)

You'll need to execute your code inside onDataChange method in order to have the last version of the data Array.

You can modify the current method in order to get the "data" after it is populated:

You'll need to create a listener:

public interface OnDataLoadedListener{
  public void onFinishLoading(List<WorldBrowseData> data);
  public void onCancelled(FirebaseError firebaseError);
}

Then, your method would be:

public static void getData(final OnDataLoadedListener listener) {

    ref.addListenerForSingleValueEvent(new ValueEventListener() {

        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            data = new ArrayList<>();
            for(DataSnapshot children : dataSnapshot.getChildren()) {

                WorldBrowseData current = new WorldBrowseData();
                Log.v("CHILDREN",children.toString());
                current.image = children.child("image_url").getValue().toString();
                //Log.v("IMAGE",current.image);
                data.add(current);
                Log.v("DATA IN METHOD",data.toString());

            }

            if(listener != null) listener.onFinishLoading(data);
        }

        @Override
        public void onCancelled(FirebaseError firebaseError) {
          if(listener != null) listener.onCancelled(firebaseError);
        }
    });    

}

You'll need to call the method:

getData(new OnDataLoadedListener(){
   public void onFinishLoading(List<WorldBrowseData) data){
     //data is populated
   }

   public void onCancelled(FirebaseError firebaseError){
    //manage the error
   }
});
mmark
  • 1,204
  • 12
  • 19
  • What do you mean by "excute your code inside onDataChange"? – user3642742 May 04 '15 at 21:32
  • if you want to perform any operation with "data" after onDataChange is executed you'll need to place that code inside that method, not after the call to ref.addListenerForSingleValueEvent(..) as you currently have – mmark May 04 '15 at 21:36
  • Is there anyway to return the updated data from the getData method? – user3642742 May 04 '15 at 21:38
  • If you don't want to block the current thread you'll have no option but wait to onDateChange to complete its execution. Otherwise you'll have to use a [CounDownLatch](http://developer.android.com/reference/java/util/concurrent/CountDownLatch.html) but i personally don't recommend it – mmark May 04 '15 at 21:41
  • How do I tell it to waitt for onDataChange to finish? – user3642742 May 04 '15 at 21:47
  • i edited the answer with an example of how implement it without blocking the thread – mmark May 04 '15 at 21:52
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/76940/discussion-between-user3642742-and-urudroid). – user3642742 May 04 '15 at 22:22
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/77002/discussion-between-user3642742-and-mmark). – user3642742 May 05 '15 at 14:41
0

I don't know about addListenerForSingleValueEvent but usually a listener is not necessarily called as soon as it is set, it is not designed for that. It is usually called on an Event. So you have defined your listener, and then logged "DATA IS". Your event is triggered later and your data is then updated.

Damien
  • 141
  • 1
  • 6
0

The code block in ValueEventListener doesn't run until an event (Single Value Event) is fired. That is why you see the behavior you are seeing. All that is happening by calling ref.addListenerForSingleValueEvent is telling the system to call the method on an event.

WindsurferOak
  • 4,861
  • 1
  • 31
  • 36