The onDataChange (), in addListenerForSingleValueEvent (), only runs after my method returns

3

I'm trying to check if a given email exists, with android using firebase I'm doing it this way:

public boolean existeEmail(String email) {

    final boolean[] retorno = new boolean[1];
    final DatabaseReference databaseArtists = FirebaseDatabase.getInstance().getReference();
    Query query = databaseArtists.child(colecao).orderByChild("email").equalTo(email);

    query.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            if (dataSnapshot.exists())
                retorno[0] = true;
            else
                retorno[0] = false;
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    });
    return retorno[0];
}

Problem

You are running first

return retorno[0];

Then it executes:

    query.addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        if (dataSnapshot.exists())
            retorno[0] = true;
        else
            retorno[0] = false;
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {

    }
});

and is always returning false. How do I run this function first

 query.addListenerForSingleValueEvent() 
    
asked by anonymous 04.08.2018 / 16:49

2 answers

3

Queries made to FirebaseDatabase have their results delivered asynchronously.

To receive the result you need to indicate a listener that will receive it, in this case a ValueEventListener . This is what you did when you used the addListenerForSingleValueEvent () method.

The code inside the parentheses only implements the interface and creates a new object.
Only when the Query object has gotten the result is that the code inside of the onDataChange() or onCancelled() methods is executed.

Since the time required to get the result is greater than that required to execute the existeEmail() method, retorno[0] is returned with the value it was initialized.

Thus, the treatment of the result has to be done within the methods of implementing the ValueEventListener interface.

For this you have several solutions:

  • write all the code in the inline implementation of the interface

    query.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            if (dataSnapshot.exists())
    
                //todo o código aqui
            else
                //todo o código aqui
        }
    
        @Override
        public void onCancelled(DatabaseError databaseError) {
            //todo o código aqui
        }
    });
    
  • write methods where you put this code and call them in the ValueEventListener methods

    query.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
    
            handleEmail(dataSnapshot.exists()):
    
        }
    
        @Override
        public void onCancelled(DatabaseError databaseError) {
            handleEmailError(databaseError);
        }
    });
    
  • Implement the ValueEventListener separately

    public class EmailResultListener() extends ValueEventListener{
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            if (dataSnapshot.exists())
    
                //todo o código aqui, ou chamar método da classe
            else
                //todo o código aqui, ou chamar método da classe
        }
    
        @Override
        public void onCancelled(DatabaseError databaseError) {
            //todo o código aqui, ou chamar método da classe
        }
    }
    

    and then use it query.addListenerForSingleValueEvent(new EmailResultListener());

The last two approaches allow reuse of the code.

In short:

To receive the result it is necessary to make available to the Query object, using the addListenerForSingleValueEvent() method, an object that implements the ValueEventListener interface. Query will use this object to "deliver" the result by calling the onDataChange() method or, on error, onCancelled() . The implementation can be created inline (eg 1 and 2) or the part (eg 3). The code that will handle the result must be, or be called, within onDataChange() .

    
08.08.2018 / 22:59
2

This happens because the process of recovering the data from the firebase runs in the background, or better in the background, so the return will always be false. You can enable a dialog before starting the background processing, and disabling it at the end of the run.

dialog.setVisibility(View.VISIBLE);
query.addListenerForSingleValueEvent(new ValueEventListener() {
     @Override
     public void onDataChange(DataSnapshot dataSnapshot) {

        if ( dataSnapshot.exists() ){
            // Notifica usuário
            ...
            // Atualiza tela
            ...
        }

        dialog.setVisibility(View.VISIBLE);

    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
    }
});
    
09.08.2018 / 13:20