1

I have an issue with the following code : I don't understand why my for-loop doesn't loop before if (gameExists[0] == false){... is called.

    Button playButton = (Button) findViewById(R.id.play_button);
    playButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

            user = mFirebaseAuth.getCurrentUser();

            final String gameCateg = String.valueOf(categorySelected[0]);

            DatabaseReference allExistingGamesToMatchKeys = mFirebaseDatabase.getReference().child("gamestomatchkeys");
              allExistingGamesToMatchKeys.addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {
                    final boolean[] gameExists = {false};
                    Log.d("gameExists before loop ", String.valueOf(gameExists[0]));

                    for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
                        final String currentKey = postSnapshot.getKey();
                        //check player 2 is not the same as player 1 so that we don't match the same player
                        if(gameCateg.equals(postSnapshot.getValue().toString())){
                            mFirebaseDatabase.getReference().child("games").child(currentKey).child("player1").addListenerForSingleValueEvent(new ValueEventListener() {
                                @Override
                                public void onDataChange(DataSnapshot dataSnapshot) {
                                    String namePlayer1 = dataSnapshot.getValue(String.class);
                                    if(!(user.getUid().toString().equals(namePlayer1))){
                                        mFirebaseDatabase.getReference().child("gamestomatchkeys").child(currentKey).removeValue();
                                        mFirebaseDatabase.getReference().child("games").child(currentKey).child("player2").setValue(user.getUid());
                                    gameExists[0] = true;
                                    Log.d("gameExists in for loop ", String.valueOf(gameExists[0]));


                                    Intent intent = new Intent(getApplicationContext(), Game.class);
                                        intent.putExtra("gameIdKey", currentKey);
                                        startActivity(intent);
                                    }
                                }

                                @Override
                                public void onCancelled(DatabaseError databaseError) {
                                }
                            });
                            break;
                        }
                    }
                    if (gameExists[0] == false){
                        Log.d("gameExists in if", String.valueOf(gameExists[0]));

This is what I get in my logs, in this order :

gameExists before loop: false

gameExists in if: false

gameExists in for loop: true

I don't understand why I get

gameExists in if: false

before

gameExists in for loop: true

I want my loop to be called and entirely looped before if (gameExists[0] == false){..., what should I modify ?

Thank you !

Community
  • 1
  • 1
Alex9494
  • 1,588
  • 3
  • 12
  • 22

2 Answers2

1

It's because "gameExists in for loop" is not actually in the for loop. It's in a callback that is created in the for loop.

                       mFirebaseDatabase.getReference().child("games").child(currentKey).child("player1").addListenerForSingleValueEvent(new ValueEventListener() {
                            @Override
                            public void onDataChange(DataSnapshot dataSnapshot) {
                                String namePlayer1 = dataSnapshot.getValue(String.class);
                                if(!(user.getUid().toString().equals(namePlayer1))){
                                    mFirebaseDatabase.getReference().child("gamestomatchkeys").child(currentKey).removeValue();
                                    mFirebaseDatabase.getReference().child("games").child(currentKey).child("player2").setValue(user.getUid());
                                gameExists[0] = true;
                                Log.d("gameExists in for loop ", String.valueOf(gameExists[0]));


                                Intent intent = new Intent(getApplicationContext(), Game.class);
                                    intent.putExtra("gameIdKey", currentKey);
                                    startActivity(intent);
                                }
                            }

                            @Override
                            public void onCancelled(DatabaseError databaseError) {
                            }
                        });

Here you are creating a new instance of a ValueEventListener and you are overriding methods within it. Your are not executing the code within those methods at the point of instantiation. That code get called whenever your ValueEventListener decides to call the onDataChange() method.

I'm not entirely sure what this object is, or how it works:

mFirebaseDatabase.getReference().child("games").child(currentKey).child("player1")

But if I were you, I would start by seeing if I could get a DataSnapshot from it, and using that snapshot on whatever code you want to execute outside of the Listener.

If I had to guess, there might be a method like:

mFirebaseDatabase.getReference().child("games").child(currentKey).child("player1").getDataSnapshot();

that you can call. Then copy all the code from inside onDataChange() to outside of the listener.

(I have no idea if that method exits, but I would assume there is some way of getting the current DataSnapshot)

John Oberhauser
  • 396
  • 1
  • 3
  • 12
  • Im not sure to understand : onDataChange() is called when the listener is attached to my reference, and this is done between the opening and closing brackets of my for loop. So why shouldn't it be "looped" ? [From what I've seen](http://stackoverflow.com/questions/38017765/retrieving-child-value-firebase/38022714#38022714) I need a listener on my reference to get the value, there is nothing such as ".getDataSnapshot()". And in this listener I need to use the variable "currentKey" that I get from the for loop. – Alex9494 Feb 24 '17 at 22:20
  • there is no `getDataSnapshot()` method, if you want to get value it can only be `addListenerForSingleValueEvent` or `addValueEventListener()` – koceeng Feb 25 '17 at 00:17
1

To make it simple, Firebase Database request are outside of the code flow. Take a look at this:

// here is first point 
allExistingGamesToMatchKeys.addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        // here is second point
    }
    ...
}
// here is third point

Above example will be executed like this

--> first point --> third point

Then where is the second point? Second point will be executed whenever Firebase get data from online database, so it is outside the flow (but most of the time, it will be executed after the third point.

So in conclusion, if you need some code to be executed after Firebase requests is done, place it inside onDataChange()

You might look at this for reference

Community
  • 1
  • 1
koceeng
  • 2,169
  • 3
  • 16
  • 37
  • Thank you, I understand how it works. In onDataChange I want to call my "if" only after all my list has been looped. So I guess I need a condition to say that, such as if(this is the last element in the DataSnapshot list) or if(next element in the DataSnapshot list is null). I tried a few things such as if(currentKey == mFirebaseDatabase.getReference().child("gamestomatchkeys").limitToLast(1).toString()) but I don't get what I want. Do you know how to make such a condition ? – Alex9494 Feb 25 '17 at 12:32
  • The simplest way to do that is create an `int` value that contains `dataSnapshot` count from `"gamestomatchkeys"` request, and one more int value for the counter. And then inside `onDataChange` on `"games"` request, you add counter int by one. Then on the last part of the same `onDataChange`, you compare the first int value and the counter. If it match then you've reach the end – koceeng Feb 25 '17 at 14:43