0

I'm trying to update a variable each time i press my button from my Firestore database, but the problem is that whenever I start my application I get null at the first time, and when I hit the button for second time it works correctly.

it seems that my variable isn't initializing correctly at the first time that i press my button. I don't know exactly.

private String name;
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.friendsystem_add);


    //[Initializing UI Components]
    emailToAdd = findViewById(R.id.friend_rq_name);


    //[Getting variable]
    Button send = findViewById(R.id.friend_rq_send);
    send.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {

            final String email = emailToAdd.getText().toString();
            //check if user exists or not
            doesExist(email);

        }

    Toast.makeText(AddFriend.this, name, Toast.LENGTH_SHORT).show();

and this is my method:

    private void doesExist(String email) {
    //sending our friend request to database

    db.collection("Users")
            //query
            .whereEqualTo("email", email)
            //getting results
            .get()
            .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                @Override
                public void onComplete(@NonNull Task<QuerySnapshot> task) {
                    //check if task is successfull
                    if (task.isSuccessful()) {
                        for (QueryDocumentSnapshot document : task.getResult()) {

                            //getting target account name
                            name = document.getString("name");

                        }
                    }
                }
            });
}

Here is my full code: https://gist.github.com/arshiyanine/dc16f187d5bcaaaefddd8e38fd8f82c1 Thank you.

Harry
  • 163
  • 10

1 Answers1

0

Data is loaded from Cloud Firestore asynchronously, and the code after you start loading continues to run. Then when the data has been loaded, the SDK calls your completion handler. This means that the data is only available inside the onComplete method. To see what this means, run your code with a few log statements like this:

System.out.println("Before starting query");
db.collection("Users")
  .whereEqualTo("email", email)
  .get()
  .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
      @Override
      public void onComplete(@NonNull Task<QuerySnapshot> task) {
          System.out.println("Got documents");
      }
  });
System.out.println("After starting query");

If you run this code, the output is:

Before starting query

After starting query

Got documents

This is probably not the order you expected. But it explains perfectly why the toast doesn't show the name from the database: the data hasn't been loaded yet!

This means that any code that needs the data from the database needs to be (called from) inside the onComplete() method. The simplest solution is to simply display the toast in that method:

private void doesExist(String email) {
    db.collection("Users")
      .whereEqualTo("email", email)
      .get()
      .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
          @Override
          public void onComplete(@NonNull Task<QuerySnapshot> task) {
              if (task.isSuccessful()) {
                  for (QueryDocumentSnapshot document : task.getResult()) {
                      name = document.getString("name");
                  }
                  Toast.makeText(AddFriend.this, name, Toast.LENGTH_SHORT).show();
              }
          }
      });
}

This is very simple, but makes your doesExist less reusable. If that's a concern, consider creating a custom callback; similar to the Task, but then dedicated to your use-case here. A simple example of this:

public interface GetUserNameCallback {
  void onUserExists(String name);
  void onUserDoesNotExist();
}
private void doesExist(String email, UserExistsCallback callback) {
    db.collection("Users")
      .whereEqualTo("email", email)
      .get()
      .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
          @Override
          public void onComplete(@NonNull Task<QuerySnapshot> task) {
              if (task.isSuccessful()) {
                  String name;
                  for (QueryDocumentSnapshot document : task.getResult()) {
                      name = document.getString("name");
                  }
                  callback.onUserExists(name);
              }
              else {
                  callback.onUserDoesNotExist();
              }
          }
      });
}

And then invoke it like:

doesExist(email, new UserExistsCallback() {
  @Override
  public void onUserExists(String name) {
    Toast.makeText(AddFriend.this, name, Toast.LENGTH_SHORT).show();
  }
  @Override
  public void onUserDoesNotExist() {
    Toast.makeText(AddFriend.this, "User "+email+" does not exist", Toast.LENGTH_SHORT).show();
  }
});

For more examples of this, see:

Community
  • 1
  • 1
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807