Home > Mobile >  Android Firebase Firestore query returns empty results from function, but the same code works when i
Android Firebase Firestore query returns empty results from function, but the same code works when i

Time:02-22

I'm having a trouble with firebase queries in Studio. I'm trying to abstract a simple query to get a list of object stored in a collection (in my case "users") I want to create a function stored in a Class that can be called by every fragment into the project. But I don' t find any method to do that, is to repeate the same instruction the only way to do that? Here is an example

db.collection("users") //get all the users
.get()
.addOnCompleteListener(task -> {
   if (task.isSuccessful()) {
       if(task.getResult() != null) {
           for (QueryDocumentSnapshot document : task.getResult()) { 
                usersList.add(document.toObject(User.class));
            } else {
                Log.w(LOGIN, "Error getting documents.", task.getException());
            }
        });

I write these lines of code every time I need them, but I want to create a method that return a List as in this example:

public static List<User> getUsers(FirebaseFirestore db) {
  List<User> usersList = new ArrayList<>();

  db.collection("users") //get all the users
  .get()
  .addOnCompleteListener(task -> {
    if (task.isSuccessful()) {
       if(task.getResult() != null) {
           for (QueryDocumentSnapshot document : task.getResult()) { 
                usersList.add(document.toObject(User.class));
            } else {
                Log.w(LOGIN, "Error getting documents.", task.getException());
            }
        });
    } else {
      //error
    }
  return usersList;
}

CodePudding user response:

Data is loaded from Firestore (and most modern cloud APIs) asynchronously, because it may take some time. Instead of blocking the app during that time, the main code continues to execute. Then when the data is available, your addOnCompleteListener callback is executed with that data.


The easiest way to see this is by adding some well-placed logging to your code:

public static List<User> getUsers(FirebaseFirestore db) {
  Log.i(LOGIN, "Starting getUsers");

  db.collection("users") //get all the users
  .get()
  .addOnCompleteListener(task -> {
    Log.i(LOGIN, "Got data");
  })
  Log.i(LOGIN, "Returning from getUsers");
}

When you run this code, you get the following output:

Starting getUsers

Returning from getUsers

Got data

This is probably not the order you expected, but it completely explains why the code that calls getUsers never sees the data: by the time your return usersList runs, the data hasn't loaded yet and usersList.add(document.toObject(User.class)) has never been called.


The solution is always the same: any code that needs the data from the database, must either be inside the completion callback, be called from there, or be synchronized by some other means.

A simple example is to create a custom callback function:

public interface GetUsersCallback {
    void onCallback(List<User> users);
}

You then pass that to getUsers, which can then call it once it's gotten and processed the results from the database:

public static void getUsers(FirebaseFirestore db, GetUsersCallback callback) {
                                               //            
  • Related