Cramery
Cramery

Reputation: 39

Can't add element to List in inner class/loop

I want to save all document IDs found in a List "names". The list is created as a global class-variable. The code is running and the documentIds are all added to the list "names", but as soon as the code leaves the inner class (OnComplete) it gets deleted again.

I know that java treats inner-classes like regular classes. But since it's an Override-class, I cant pass anything back from the class. How can I still access the change of the List?

public class HomeFragment extends Fragment {

    List<String> names = new ArrayList<String>();  

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        return inflater.inflate(R.layout.fragment_home, null);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        addElements();
    }

    public void addElements(){
        db.collection("Recipes").get()
                .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                    @Override
                    public void onComplete(@NonNull Task<QuerySnapshot> task) {
                        if (task.isSuccessful()){
                            for (QueryDocumentSnapshot document : task.getResult()){
                                names.add(document.getId());
                            }
                        }else
                        {
                            //Log
                        }

                    }
                });
    }
}

Upvotes: 0

Views: 122

Answers (1)

Tyler V
Tyler V

Reputation: 10910

Firebase database calls are asynchronous. Editing a class variable from within the OnCompleteListener (an anonymous inner class) is fine, but you have to understand that the code in onComplete will not execute immediately.

The call to db.collection(...).get().addOnCompleteListener(...); takes your anonymous listener class and starts a background process to fetch the data (on a different thread). The function returns immediately. Once the background process has finished fetching the data, then it calls the onComplete method on the listener you gave it (hundreds to thousands of ms later).

For example, if you add checks as noted below, it will first print the greeting from addElements with names size = 0, then later print the greeting from onComplete with names size > 0 (assuming the query returned data). After that has happened, you can use names wherever needed (the values will stay there as long as the Fragment isn't destroyed), but your code has to be set up to be able to wait for that to happen.

public void addElements(){
    // This runs 1st
    System.out.println("Starting addElements");

    db.collection("Recipes").get()
            .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                @Override
                public void onComplete(@NonNull Task<QuerySnapshot> task) {
                    if (task.isSuccessful()){
                        for (QueryDocumentSnapshot document : task.getResult()){
                            names.add(document.getId());
                        }
                    }else
                    {
                        //Log
                    }

                    // This runs 3rd! in the future when the database call is complete
                    System.out.println("HELLO FROM ONCOMPLETE");
                    printNamesSize();

                    // If you are showing "names" in a UI element, you would
                    // most likely need to call a method to update the 
                    // UI element here, once it's gotten the names
                }
            });

    // This runs 2nd, but names will be empty here (hasn't run onComplete yet)
    System.out.println("HELLO FROM ADDELEMENTS");
    printNamesSize();

    // If your code immediately tries to use names after calling addElements
    // it will still be empty, it hasn't finished the database call yet
}

void printNamesSize() {
    System.out.println("  names size = " + names.size());
}

Upvotes: 1

Related Questions