onDataChange(), in addListenerForSingleValueEvent(), is only executed after my method returns

Asked

Viewed 1,131 times

3

I’m trying to check if a given email exists, with android using firebase I’m doing 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

Is running first

return retorno[0];

And then 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 first execute this function

 query.addListenerForSingleValueEvent() 
  • 1
  • I need to put her in a class.

  • When working with asynchronous Apis, such as Firebase, the developer is forced to think differently than the traditional sequential way. If a little more context were added to the question (where the method is used, for example) it would be easier to indicate alternatives to get the desired result.

  • Putting codes into Activity creates a lot of code repetition.

  • Have you seen my answer? Need some more information?

  • I did, sorry for the delay, I was away.

Show 1 more comment

2 answers

4

Queries made to Firebasedatabase have their results "delivered" asynchronously.

To receive the result it is necessary to specify a Listener who will receive it, in this case a Valueeventlistener.
That’s what you did when using the method addListenerForSingleValueEvent().

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

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

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

For this there are several solutions:

  • write all the code in the implementation inline 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 place this code and call them in Valueeventlistener methods

    query.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
    
            handleEmail(dataSnapshot.exists()):
    
        }
    
        @Override
        public void onCancelled(DatabaseError databaseError) {
            handleEmailError(databaseError);
        }
    });
    
  • Implement 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 use it like this query.addListenerForSingleValueEvent(new EmailResultListener());

The last two approaches allow the reuse of the code.

In short:

To receive the result it is necessary to make available to the Query object, through the method addListenerForSingleValueEvent(), an object that implements the Valueeventlistener interface.
Query will use this object to "deliver" the result by calling the method onDataChange() or, in the event of an error, onCancelled().
The implementation can be created inline (ex. 1 and 2) or in part (e.g. 3).
The code that will treat the result must be, or be called, within the onDataChange().

  • I will need to create an interface that implements the Valueeventlistener method, as I will be able to see your return?

  • You must make available to the Query object, through the method addListenerForSingleValueEvent(), an object that implements the Valueeventlistener interface. It will use that object to "deliver" the result called the method onDataChange() or onCancelled() in case of error. The implementation can be created inline (ex. 1 and 2) or in part(e.g. 3). The code that will handle the result("see your return") must be, or be called, within the onDataChange().

  • Yes the result will be in onDataChange(), i have already implemented the logic of ex. 1, I can’t figure out how it will catch the result out of the onDataChange(), knowing that he then executes the onDataChange().

  • You can’t take it out. What you would do if you returned true, has to be done in the method onDataChange(). Substitute retorno[0] = true; by that code. It is possible that you have to adjust other parts of the code. The method onDataChange() it’s like the method onPostExecute() of an Asynctask.

2

This happens because the firebase data recovery process 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 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) {
    }
});
  • 1

    Could you tell us if you solved your problem or not? Give us feedback.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.