-1

I wrote a function that is supposed to get a user id, access all the user's attributes in the firestore database, and then create and return the object. But the function skips over the entire block of "addOnSuccessListener", so it returns a null object.

  public static Player getPlayer(String playerID) {

    final Player[] p = new Player[1];
    DocumentReference playerRef = fStore.collection("users").document(playerID);

    playerRef.get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
        @Override
        public void onSuccess(DocumentSnapshot documentSnapshot) {
            if (documentSnapshot.exists()) {
                // getting all the attributes of the player from the database
                String age = documentSnapshot.getString("age");
                String city = documentSnapshot.getString("city");
                String email = documentSnapshot.getString("email");
                String fullName = documentSnapshot.getString("fullName");
                String gender = documentSnapshot.getString("gender");
                String nickName = documentSnapshot.getString("nickName");
                String password = documentSnapshot.getString("password");
                String phone = documentSnapshot.getString("phone");
                
                // creates a new player
                p[0] = new Player(fullName, nickName, email, password, phone, city, gender, age);
            } else {
                Toast.makeText(context.getApplicationContext(), "Player not found", Toast.LENGTH_LONG).show();
            }
        }
    })
            .addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    Toast.makeText(context.getApplicationContext(), "Failed to fetch data", Toast.LENGTH_LONG).show();
                }
            });

    return p[0];
}
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Lioo7
  • 27
  • 4
  • Welcome to Stack Overflow. Please read [ask]. Do not post images of code or error messages, copy/paste the relevant code into your question and remember to format as code. – Jim Garrison Dec 26 '21 at 02:21
  • 1
    Does this answer your question? [Why does my function that calls an API return an empty or null value?](https://stackoverflow.com/questions/57330766/why-does-my-function-that-calls-an-api-return-an-empty-or-null-value) – Tyler V Dec 26 '21 at 05:57
  • 1
    Data is loaded from Firestore (and most modern cloud APIs) asynchronously, which means that your `return p[0]` runs before `p[0] = new Player...` ever executes. If you set breakpoints on these lines and run in a debugger, or add logging before the lines, you can most easily see this. The solution is always the same: any code that needs the data from Firestore, must be inside `onSuccess`, be called from there, or be otherwise synchronized. See https://stackoverflow.com/a/48500679 and https://stackoverflow.com/a/51002413. – Frank van Puffelen Dec 26 '21 at 15:07

1 Answers1

1

It skips over it because OnSuccessListener hasn't ran yet its a callback. It won't run until it has something from FireStore. You are immediately returning something that hasn't been retrieved yet.

So how do you fix it? You will need some kind of callback feature. You can use RxJava, ListenableFuture, or you could also just reuse the OnSuccessListener callback.

  public static void getPlayer(String playerID, OnSuccessListener<Player> listener ) {

    final Player[] p = new Player[1];
    DocumentReference playerRef = fStore.collection("users").document(playerID);

    playerRef.get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
        @Override
        public void onSuccess(DocumentSnapshot documentSnapshot) {
            if (documentSnapshot.exists()) {
                // getting all the attributes of the player from the database
                String age = documentSnapshot.getString("age");
                String city = documentSnapshot.getString("city");
                String email = documentSnapshot.getString("email");
                String fullName = documentSnapshot.getString("fullName");
                String gender = documentSnapshot.getString("gender");
                String nickName = documentSnapshot.getString("nickName");
                String password = documentSnapshot.getString("password");
                String phone = documentSnapshot.getString("phone");
                
                // creates a new player
                p[0] = new Player(fullName, nickName, email, password, phone, city, gender, age); 
                //HERE
                //Send your player to your listener arg.
                listener.onSuccess(p[0]);
            } else {
                Toast.makeText(context.getApplicationContext(), "Player not found", Toast.LENGTH_LONG).show();
            }
        }
    })
            .addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    Toast.makeText(context.getApplicationContext(), "Failed to fetch data", Toast.LENGTH_LONG).show();
                }
            });

      return;
  }

How you call getPlayer now

 getPlayer("id",(player)->{
     player.doSomething();
     //HERE is your player received from FireStore
 });
avalerio
  • 2,072
  • 1
  • 12
  • 11
  • Thank you for your detailed explanation. So I guess now I have to wrap the code that calls the getPlayer function as well because it does not wait till it adds the players to the array list, so I get an empty list. – Lioo7 Dec 26 '21 at 04:43
  • 1
    For future reference anything you are retrieving that isn't hardcoded, will most likely need to be handled in an async fashion like above. Accessing data in files, databases, and cloud services all can require more time than allotted by the `MainLopper`, i.e. you can't just wait for stuff. – avalerio Dec 26 '21 at 05:00