Ad

Cannot Get Data In The Variable When Firebase Firestore Is Called Using Methord Return Value

When I get data from Cloud Firestore in android. It returns an empty variable. It should return the value of the flag true or false.

public boolean checkIfUserAlreadyExists(final String email) {
        final boolean[] flag = {false};
        db.collection(context.getString(R.string.db_collection_users))
                .get()
                .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                    @Override
                    public void onComplete(@NonNull Task<QuerySnapshot> task) {
                        if (task.isSuccessful()) {
                            for (QueryDocumentSnapshot document : task.getResult()) {
                                if (document.getData().get(context.getString(R.string.db_field_email)).toString() != null && document.getData().get(context.getString(R.string.db_field_email)).toString().equals(email)) {
                                    Toast.makeText(context, "User Already exits", Toast.LENGTH_SHORT);
                                    Log.e(TAG, "User already exists" + document.getData().get("email").toString());
                                    flag[0] = true;
                                } else {
                                    Log.e(TAG, "User do not exists");
                                }
                            }
                        } else {
                            Log.e(TAG, "Error getting documents.", task.getException());
                        }
                    }
                });
        return flag[0];
    }

flag[0] should return true but it returns false when called in another activity.

Ad

Answer

addOnCompleteListener works asynchronously.

That is you are returning flag[0] immeadiately after the OnCompleteListener is set. By that time the onComplete() would not have called and hence the flag[0] still have the initial value. This compiler expected this type of error that's why it's warned you that you can not change non final variables inside the callback method. But you bypassed it in a strange way by making it an array 😀

You have can solve this in multiple ways


Solution 1

Instead of returning the value, access the value from the callback

illustration

public void checkIfUserAlreadyExists(final String email) {
    db.collection(context.getString(R.string.db_collection_users))
        .get()
        .addOnCompleteListener(new OnCompleteListener < QuerySnapshot > () {
            @Override
            public void onComplete(@NonNull Task < QuerySnapshot > task) {
                if (task.isSuccessful()) {


                    for (QueryDocumentSnapshot document: task.getResult()) {

                        if (document.getData().get(context.getString(R.string.db_field_email)).toString() != null && document.getData().get(context.getString(R.string.db_field_email)).toString().equals(email)) {
                            Toast.makeText(context, "User Already exits", Toast.LENGTH_SHORT);
                            Log.e(TAG, "User already exists" + document.getData().get("email").toString());

                            // User exists.
                            userTheResult(true);
                            return;

                        } else {
                            Log.e(TAG, "User do not exists");
                        }
                    }

                    // No user exists.
                    userTheResult(false);

                } else {
                    Log.e(TAG, "Error getting documents.", task.getException());
                }
            }
        });
}


Solution 2

You can use this class to make a thread waiting until the result is obtained. This way you don't have to change your code much. What you have to do is simply add the class ThreadLockedTask<T> as it is to your project and use it as in the example given.

import java.util.concurrent.atomic.AtomicReference;

/**
 * @author Ahamad Anees P.A
 * @version 1.0
 * @param <T> type
 */
public class ThreadLockedTask<T> {

    private AtomicReference<ResultWrapper<T>> mReference;

    public ThreadLockedTask() {
        mReference = new AtomicReference<>(new ResultWrapper<T>());
    }

    public T execute(Runnable runnable) {
        runnable.run();
        if (!mReference.get().mIsSet)
            lockUntilSet();
        return mReference.get().mResult;
    }

    private void lockUntilSet() {
        synchronized (this) {
            while (!mReference.get().isSet()) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    public void setResult(T result) {
        synchronized (this) {
            ResultWrapper<T> wrapper = mReference.get();
            wrapper.setResult(result);
            wrapper.setIsSet(true);
            notify();
        }
    }

    public static class ResultWrapper<T> {
        private boolean mIsSet;
        private T mResult;

        public boolean isSet() {
            return mIsSet;
        }

        public T getResult() {
            return mResult;
        }

        void setIsSet(boolean isCompleted) {
            this.mIsSet = isCompleted;
        }

        void setResult(T result) {
            this.mResult = result;
        }
    }

}


Usage

public boolean checkIfUserAlreadyExists(final String email) {
    ThreadLockedTask < Boolean > task = new ThreadLockedTask < > ();
    boolean flag = task.execute(new Runnable() {
        @Override
        public void run() {
            db.collection(context.getString(R.string.db_collection_users))
                .get()
                .addOnCompleteListener(new OnCompleteListener < QuerySnapshot > () {
                    @Override
                    public void onComplete(@NonNull Task < QuerySnapshot > task) {
                        if (task.isSuccessful()) {

                            for (QueryDocumentSnapshot document: task.getResult()) {

                                if (document.getData().get(context.getString(R.string.db_field_email)).toString() != null && document.getData().get(context.getString(R.string.db_field_email)).toString().equals(email)) {
                                    Toast.makeText(context, "User Already exits", Toast.LENGTH_SHORT);
                                    Log.e(TAG, "User already exists" + document.getData().get("email").toString());

                                    task.setResult(true);
                                    return;

                                } else {
                                    Log.e(TAG, "User do not exists");
                                }
                            }
                        } else {
                            Log.e(TAG, "Error getting documents.", task.getException());
                        }

                        task.setResult(false);
                    }
                });
        }
    });

    return flag;
}
Ad
source: stackoverflow.com
Ad