genericname
genericname

Reputation: 177

firestore read is asynchronous and I want synchronous behaviour

I want to store locally the data I am reading from the cloud. To achieve this I am using a global variable(quizzes) to hold all the data.

For this, when I am building my Quiz objects, I need to make sure that before I am creating them, the relevant data has been already downloaded from the cloud. Since when reading data from firestore, it happens asynchronously.

I didn't enforced this (waiting for the read to finish) before -I just used onSuccess listeners, and I encountered synchronization problem because the reading tasks weren't finished before I created my Quiz objects with the data from the cloud.

I fixed this with a very primitive way of "busy waiting" until the read from the cloud is complete. I know this is very stupid, a very bad practice, and making the application to be super slow, and I am sure there is a better way to fix this.

 private void downloadQuizzesFromCloud(){


    String user_id = FirebaseAuth.getInstance().getCurrentUser().getUid();
    final FirebaseFirestore db = FirebaseFirestore.getInstance();
    CollectionReference quizzesRefrence = db.collection("users").document(user_id).collection("quizzes");

    Task<QuerySnapshot> task = quizzesRefrence.get();
    while(task.isComplete() == false){
        System.out.println("busy wait");
    }


    for (QueryDocumentSnapshot document : task.getResult()) {
        Quiz quizDownloaded = getQuizFromCloud(document.getId());
        quizzes.add(quizDownloaded);
    }

}

I looked online in the documentation of firestore and firebase and didn't find anything that I could use. (tried for example to use the "wait" method) but that didn't help.

What else can I do to solve this synchronization problem?

Upvotes: 2

Views: 4035

Answers (2)

M.AQIB
M.AQIB

Reputation: 383

You can make your own callback. For this, make an interface

public interface FireStoreResults {
    public void onResultGet();
}

now send this call back when you get results

public void readData(final FireStoreResults){
    db.collection("users").document(user_id).collection("quizzes")
    .get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
            @Override
            public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
                for (QueryDocumentSnapshot document : task.getResult()) {
                     Quiz quizDownloaded = getQuizFromCloud(document.getId());
                     quizzes.add(quizDownloaded);
                 }
                results.onResultGet();
            }
        }).addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                results.onResultGet();
            }
        });   
}

Now in your activity or fragment

new YourResultGetClass().readData(new FireStoreResults(){
             @Override
             public void onResultGet() {
                    new YourResultGetClass().getQuizzes(); //this is your list of quizzes
                    //do whatever you want with it
            }

Hope this makes sense!

Upvotes: 1

Luca Murra
Luca Murra

Reputation: 1888

I didn't understand if you tried this solution, but I think this is the better and the easier: add an onCompleteListener to the Task object returned from the get() method, the if the task is succesfull, you can do all your stuff, like this:

private void downloadQuizzesFromCloud(){


String user_id = FirebaseAuth.getInstance().getCurrentUser().getUid();
final FirebaseFirestore db = FirebaseFirestore.getInstance();
CollectionReference quizzesRefrence = db.collection("users").document(user_id).collection("quizzes");

quizzesRefrence.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
        @Override
        public void onComplete(@NonNull Task<QuerySnapshot> task) {
            if (task.isSuccesful()) {
               for (QueryDocumentSnapshot document : task.getResult()) {
                    Quiz quizDownloaded = getQuizFromCloud(document.getId());
                    quizzes.add(quizDownloaded);
               }
             }
         });
}
}

In this way, you'll do all you have to do (here the for loop) as soon as the data is downloaded

Upvotes: 1

Related Questions