2

So the issue I am having is that the loadUserNameFromFirestore() method executes the return statement before the firestore db.collection.get() query finishes executing. I have put logs after the firestore db.collection.get() query, and before the method return statement.

private String loadUserNameFromFirestore(Appointment appointment) {

    final StringBuilder userFullName = new StringBuilder();
    String userUid = appointment.getAppointment_doctor_uid();

    db.collection(Constants.FIRESTORE_DOCTOR).document(userUid)
            .get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
        @Override
        public void onComplete(@NonNull Task<DocumentSnapshot> task) {
            DocumentSnapshot documentSnapshot = task.getResult();
            userFullName.append(documentSnapshot.getString(Constants.FIRESTORE_STAFF_TITLE)).append(" ");
            userFullName.append(documentSnapshot.getString(Constants.FIRESTORE_STAFF_FNAME)).append(" ");
            userFullName.append(documentSnapshot.getString(Constants.FIRESTORE_STAFF_LNAME));
            Log.d("DEBUG_APP", userFullName.toString());
        }
    })
            .addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                }
            });

    Log.d("DEBUG_APP", "Final: " + userFullName.toString());

    return userFullName.toString();
}

Below are the logs created from the above code.

10-03 02:43:30.561....: Final:  
10-03 02:43:31.044....: Dr Naila Alam

As you can see above, the log before the return statement, "First: ", was executed first, then the log after the firestore query, "Dr Naila Alam".

Is there a way to make sure the return statement doesn't get called until after the firestore finishes executing. I would really appreciate any help.

jdcles12
  • 23
  • 3
  • You cannot return something now that hasn't been loaded yet. So please check the duplicate to see why do you have this behaviour and how can you solve this using a custom callback. – Alex Mamo Oct 03 '18 at 12:04
  • Sorry, I didn't see the duplicate. This actually worked! thank you! However, when I invoke the callback in an OnClick method, I am having troubles putting the data in an intent, using intent.putExtra(callBackData). The startActivity() would be called first, before putting the data to the intent, thus I would not see the callback data in my second activity. @AlexMamo – jdcles12 Oct 03 '18 at 16:27
  • Good to hear that it worked :) This seems to be another issue. In order to follow the rules of this comunity, please post another fresh question, so me and other users can help you. – Alex Mamo Oct 03 '18 at 16:33
  • No problem, thanks – jdcles12 Oct 04 '18 at 00:35

1 Answers1

1

As you can see, your request to firebase is an asynchronous operation so the code don't wait that it finish but continue his route. The listener that you attach work on another thread so your GUI don't freeze. This is why you can't run a method that return a value getting that by a firebase call.

You must modify your code to call method to retrieve value and, in onComplete method, call another method to set the value getted.

In example: (Assuming you need the username to show it on GUI)

public void setTextUsername(String username)
    this.textViewUsername.setText(username);
    //here you can do all you need with username value
}

public void loadUserNameFromFirestore(Appointment appointment){
    String userUid = appointment.getAppointment_doctor_uid();

    db.collection(Constants.FIRESTORE_DOCTOR).document(userUid)
        .get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {

    @Override
    public void onSuccess(@NonNull Task<DocumentSnapshot> task) {
        StringBuilder userFullName = new StringBuilder();
        DocumentSnapshot documentSnapshot = task.getResult();
        userFullName.append(documentSnapshot.getString(Constants.FIRESTORE_STAFF_TITLE)).append(" ");
        userFullName.append(documentSnapshot.getString(Constants.FIRESTORE_STAFF_FNAME)).append(" ");
        userFullName.append(documentSnapshot.getString(Constants.FIRESTORE_STAFF_LNAME));
        Log.d("DEBUG_APP", userFullName.toString());
        setTextUsername(userFullName.toString());
    }})
        .addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
            }
        });
}

If you if something is not clear, do not hesitate to ask. I will be happy to offer my help!

  • Thanks!! this really helped. Although, I am using the loadUserNameFromFirestore() method in the onBindViewHolder() of my RecyclerView adapter. So with your solution, the holder.setText() would end up being called first before the loadUserNameFromFirestore(). This would result in setTextUsername() not being called, thus would not work. The way I got around this was to pass through the ViewHolder as a parameter in loadUserNameFromFirestore(), and call holder.setText() within setTextUsername(). However, this will not allow me to retrieve the username in my OnClick(). Is there a way around this? – jdcles12 Oct 03 '18 at 12:04
  • Sorry, but I can't understand what do you mean.. This is a very bad technique to work with firebase. If I understand correctly, you call a READ operation for each item in you recycle view.. it mean that if you have 200 Appointments, you do 201 read call to firebase (1 for appointments list, 200 for each user).. this is very bad for performances and your money too xD If you only need the user's name, think to put it as a Appointments field.. so, retrieving appointment list, you have the user name too! – Devster - Angelo Cimino Oct 04 '18 at 11:26
  • For more understanding, in example you appointment structure in firebase will be: appointments { askdmadnbwKNSA { id: askdmadnbwKNSA , userId: LIKs854Aend , userName: Doc.Michael , date:1929281182912, etc etc...} – Devster - Angelo Cimino Oct 04 '18 at 11:28
  • Yes, I totally agree with you. However, I currently have a userId in my Appointment field. For data normalisation purposes, I thought that I would use that userId as a foreign key, and access my user details from my User table in Firestore. – jdcles12 Oct 05 '18 at 05:33
  • Yeah @jdcles12.. I know what you're saying.. but Firebase use denormalized structure.. it's very not recommended to use this type of data organization. If I can, I recommend you to see this link to understand more which are the various techniques you can use: https://angularfirebase.com/lessons/advanced-firestore-nosql-data-structure-examples/ – Devster - Angelo Cimino Oct 05 '18 at 11:01
  • Ahhh I see, thank you so much. I really appreciate your help!! @CyanDeveloper – jdcles12 Oct 05 '18 at 14:02
  • @jdcles12 I'm glad you enjoyed my solution. If you have no other doubts, please accept my answer as "correct" so next users can quickly find the solution. – Devster - Angelo Cimino Oct 05 '18 at 18:18