Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait until Firestore data is retrieved to launch an activity

I've made a List of POJO Objects that get values from my Cloud Firestore database. This List is itself a Class having in mind to retrieve attributes everytime I create a new List. The thing is that the List is not filled in the on create activity but time after. I've tried adding loops, tasks, waits, but not getting the result desired. Here is how I retrieve my data satisfactorally:

private void retrieveData(){
        reference = firestore.collection("attractions");
        reference.addSnapshotListener(new EventListener<QuerySnapshot>() {
            @Override
            public void onEvent(@Nullable QuerySnapshot queryDocumentSnapshots,
                                @Nullable FirebaseFirestoreException e) {
                attractionList.clear();
                for (DocumentSnapshot snapshot : queryDocumentSnapshots) {
                    Attraction attraction = snapshot.toObject(Attraction.class);
                    addAttractions(attraction);
                }
            }
        });
    }

I retrieve the data everytime I create a new Object:

public AttractionList() {
        firestore.setFirestoreSettings(settings);
        reference = firestore.collection("attractions");
        attractionList = new ArrayList<>();
        retrieveData();
    }

Then, if I check the List size on my Activity on create:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_attraction);
        Toolbar toolbar = findViewById(R.id.tb_attraction);
        setSupportActionBar(toolbar);

        //Creating POJO List and retrieving data
        attractionList = new AttractionList();
        Log.e("List Size: ", ""+attractionList.getAttractionList().size());
        //List Size: 0
    }

But if I use the same Log time after, in any button:

@Override
    public void onBackPressed(){
        Log.e("List Size: ", ""+attractionList.getAttractionList().size());
        //List Size: 16
    }

I don't really want to retrieve data in every single Activity or creating intents to bring the Object between Activities. There is any way I could fix this?

PS: This is my first post. Sorry If I've haven't done it on the right way. Thank's a lot.

like image 447
Rubzero Avatar asked Jun 01 '18 19:06

Rubzero


1 Answers

You cannot use something now that hasn't been loaded yet. Firestore loads data asynchronously, since it may take some time for this. Depending on your connection speed and the state, it may take from a few hundred milliseconds to a few seconds before that data is available. If you want to pass a list of objects of type Attraction to another method, just call that method inside onSuccess() method and pass that list as an argument. So a quick fix would be this:

FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference attractionsRef = rootRef.collection("attractions");

attractionsRef.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
    @Override
    public void onComplete(@NonNull Task<QuerySnapshot> task) {
        if (task.isSuccessful()) {
            List<Attraction> attractionsList = new ArrayList<>();
            for (QueryDocumentSnapshot document : task.getResult()) {
                Attraction attraction = document.toObject(Attraction.class);
                attractionsList.add(attraction);
            }
            methodToProcessTheList(attractionsList);
        }
    }
});

So remember, onSuccess() method has an asynchronous behaviour, which means that is called even before you are getting the data from your database. If you want to use the attractionsList outside that method, you need to create your own callback. To achieve this, first you need to create an interface like this:

public interface MyCallback {
    void onCallback(List<Attraction> attractionsList);
}

Then you need to create a method that is actually getting the data from the database. This method should look like this:

public void readData(MyCallback myCallback) {
    attractionsRef.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
        @Override
        public void onComplete(@NonNull Task<QuerySnapshot> task) {
            if (task.isSuccessful()) {
                List<Attraction> attractionsList = new ArrayList<>();
                for (QueryDocumentSnapshot document : task.getResult()) {
                    Attraction attraction = snapshot.toObject(Attraction.class);
                    attractionsList.add(attraction);
                }
                myCallback.onCallback(attractionsList);
            }
        }
    });
}

In the end just simply call readData() method and pass an instance of the MyCallback interface as an argument wherever you need it like this:

readData(new MyCallback() {
    @Override
    public void onCallback(List<Attraction> attractionsList) {
        //Do what you need to do with your list
    }
});

This is the only way in which you can use that attractionsList object outside onSuccess() method. For more informations, you can take also a look at this video.

like image 72
Alex Mamo Avatar answered Oct 21 '22 05:10

Alex Mamo