2

I am running code similar to the problem in this question except, I am fetching arrays from each document and adding their elements to a global ArrayList. After that, outside of the collection.get(), I perform an operation on that array and return. However, If I Log.d the global ArrayList size from within the onSuccess listener, it gives the correct answer but if I do so outside of the collection.get() it always gives 0. My problem is that I cannot return from within the onSuccess because this will not return for my whole function and so I need to do it after the collection.get() where the ArrayList has not been populate, presumably since the get() operation returns a Task<> which is an asynchronous operation and so it doesn't wait for it to complete. Here is my code:

public static boolean Validator(final String sometext) {
    FirebaseFirestore db = FirebaseFirestore.getInstance();

    final ArrayList<String> mArrayList = new ArrayList<>();

    db.collection("my_collection").get()
        .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
            @Override
            public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
                if (queryDocumentSnapshots.isEmpty()) {
                    Log.d(TAG, "Validator onSuccess: LIST EMPTY");
                    return;
                } else {

                    // Convert the Query Snapshot to a list of Document Snapshots, in this
                    // case, one document for each my_collection
                    List<DocumentSnapshot> my_collections = queryDocumentSnapshots.getDocuments();


                    for (int i = 0; i < my_collections.size(); i++) {
                        ArrayList<String> mArrayitems = (ArrayList<String>) my_collections.get(i).get("array");

                        for (int j = 0; j < mArrayitems.size(); j++) {
                            Log.d(TAG, "Adding array: " + mArrayitems.get(j).toString());
                            mArrayList.add(mArrayitems.get(j).toString());
                        }
                    }
                    Log.d(TAG, "Validator onSuccess: array list built");
                }
            }
        })
        .addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                Log.d(TAG, "Validator onFailure: " + e);
            }
        });

  // check if the given sometext matches a array in the list
  Log.d(TAG, "array list size: " + mArrayList.size());
  if (mArrayList.size() > 0) {
    for (int i = 0; i < mArrayList.size(); i++) {
        if (sometext.endsWith(mArrayList.get(i))) {
            return true
        }
      }
   }
   return false;
 }

EDIT:
I'm trying to do this with a callback which looks like:

// constructor
public Validator(String sometext) {
    this.valid = false;
    this.sometext = sometext
}

public interface myCallback{
    void onCallback(boolean valid);
}

public boolean validate() {

  readData(new MyCallback() {
        @Override
        public void onCallback(boolean valid) {
            setValid(valid);
        }
    });
  return this.valid;
 }

private void readData(final MyCallback myCallback) {
    FirebaseFirestore db = FirebaseFirestore.getInstance();

    db.collection("my_collection").get()
            .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
                @Override
                public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
                    if (queryDocumentSnapshots.isEmpty()) {
                        Log.d(TAG, "Validator onSuccess: LIST EMPTY");
                    } else {

                        List<DocumentSnapshot> my_collections = queryDocumentSnapshots.getDocuments();

                        ArrayList<String> mArrayList = new ArrayList<>();

                        for (int i = 0; i < my_collections.size(); i++) {
                            ArrayList<String> mArrayItems = (ArrayList<String>) my_collections.get(i).get("array");
                            for (int j = 0; j < mArrayItems.size(); j++) {
                                Log.d(TAG, "Adding array: " + mArrayItems.get(j).toString());
                                mArrayList.add(mArrayItems.get(j).toString());
                            }
                        }

                        boolean result = false;

                        Log.d(TAG, "array list size: " + mArrayList.size());
                        if (mArrayList.size() > 0) {
                            for (int i = 0; i < mArrayList.size(); i++) {
                                if (sometext.endsWith(mArrayList.get(i))) {
                                    result = true;
                                    Log.d(TAG, "MATCH FOUND");
                                    break;
                                }
                            }
                        }

                        myCallback.onCallback(result);

                    }
                }
            })
            .addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    Log.d(TAG, "Validator onFailure: " + e);
                }
            });
}

The problem I'm having is that in validate() I want to return the correct valid value after the callback is finished because it currently returns the incorrect value before the callback has finished.

Paradox
  • 4,602
  • 12
  • 44
  • 88
  • It seems like you know what you need to do - only use the result from the get from within the callback, or kick off other work from the callback that uses the result. – Doug Stevenson May 08 '18 at 16:54
  • @DougStevenson My problem is I can't return from within the callback because that just returns for onSuccess, not for Validator. And if I use the variable outside of the callback, I get the wrong value. – Paradox May 08 '18 at 16:56
  • Possible duplicate of [Firestore - object with inner Object](https://stackoverflow.com/questions/48499310/firestore-object-with-inner-object) – Alex Mamo May 08 '18 at 16:57
  • Please check the duplicate to see why is this happening and how you can solve this using a custom callback. – Alex Mamo May 08 '18 at 16:57
  • You shouldn't be returning values from callbacks. Just call another method and pass the result into it from the callback. – Doug Stevenson May 08 '18 at 16:59
  • Did my answer from the duplicate actually helped you? – Alex Mamo May 09 '18 at 08:03
  • @AlexMamo I've updated my question after trying to use the callback but It's still returning false. I've updated my question to show you what I mean. – Paradox May 10 '18 at 00:59
  • That's not correct. Please follow the exact stepts from that post but instead of using `UserAccountSettings` use your list. It will be helpful for you if I'll write you some code? – Alex Mamo May 10 '18 at 09:24
  • @AlexMamo I'm not sure where I made the mistake. Should I set the callback for the queryDocumentSnapshots instead of the mArrayList? If you don't mind, would you be able to write some code? – Paradox May 10 '18 at 13:52
  • Before writing some, please try to change your code. You cannot use `return valid;` outside the callback. It will always be `false`. You need to use it only inside, right? – Alex Mamo May 10 '18 at 14:00
  • @AlexMamo I can't return valid from within the onCallback method though since it's void. I need Validator() to return a boolean value so I don't know how else to do it? – Paradox May 10 '18 at 14:17
  • I see you added as an argument an `ArrayList mArrayList` and not a boolean. If you need a boolean, change to a boolean or use two arguments. – Alex Mamo May 10 '18 at 14:24
  • @AlexMamo Edited question to show more of my code and use boolean. But I'm still having the issue of validate() returning the wrong value of valid. I need to return the valid value after onCallback has completed. – Paradox May 10 '18 at 14:40
  • Inside this method call `setValid(valid);`, the `valid` is looks good and I'm sure that it works but you cannot use `return this.valid;`. Is the same thing as in the first place. – Alex Mamo May 10 '18 at 14:55
  • @AlexMamo I tried making validate() void and have it only do the setValid(valid) within the onCallback, then from the calling class, it does a validate() then an isValid() but it still gave the same problem of a false valid. – Paradox May 10 '18 at 15:03
  • Yes and this is because of the reason above. – Alex Mamo May 10 '18 at 15:05

1 Answers1

0

I was able to solve my issue by first using @AlexMamo's solution here and then listen for the valid variable being changed by using this solution here.

public class ValidatorVariable {
    private boolean valid;
    private ChangeListener listener;

    public boolean isValid(){
        return valid;
    }

    public void setValid(boolean valid){
        this.valid = valid;
        if (listener != null) listener.onChange();
    }

    public ChangeListener getListener(){
        return listener;
    }

    public void setListener(ChangeListener listener){
        this.listener = listener;
    }

    public interface ChangeListener {
        void onChange();
    }
}

which is used with:

ValidatorVariable vv = new ValidatorVariable();
        vv.setListener(new ValidatorVariable.ChangeListener() {
            @Override
            public void onChange() {
                if (vv.isValid()){
                    // do stuf
                }
            }
        });
Paradox
  • 4,602
  • 12
  • 44
  • 88