Alex9494
Alex9494

Reputation: 1778

Android for loop not looping ? Firebase

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 !

Upvotes: 1

Views: 680

Answers (2)

koceeng
koceeng

Reputation: 2163

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

Upvotes: 1

John Oberhauser
John Oberhauser

Reputation: 466

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)

Upvotes: 1

Related Questions