0

I'm retrieving Hospital Data from firestore. I want to keep retrieved data to be global for the entire fragment, but the Map will return null after coming out from addOnSuccessListner().

I've declared the Map inside onCreateView()

static  Map<String, Map<String, String>> hospitals ;
public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.fragment_home, container, false);
        hospitals = new HashMap<>();
        .......
        .....

        return root;
}

Below is the code where I'm retrieving HospitalData.

private void readHospitalData() {
    CollectionReference collectionReference = fStore.collection("hospital Details");
    collectionReference.get()
            .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
                @Override
                public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
                    for(QueryDocumentSnapshot queryDocumentSnapshot: queryDocumentSnapshots){
                        Map<String, String> map = new HashMap<>();
                        map.put("h_name", queryDocumentSnapshot.getString("h_name"));
                        map.put("h_email", queryDocumentSnapshot.getString("h_email"));
                        map.put("h_phone", queryDocumentSnapshot.getString("h_phone"));
                        map.put("h_road", queryDocumentSnapshot.getString("h_road"));
                        map.put("h_area", queryDocumentSnapshot.getString("h_area"));
                        map.put("h_city", queryDocumentSnapshot.getString("h_city"));
                        map.put("h_state", queryDocumentSnapshot.getString("h_state"));
                        map.put("h_pincode", queryDocumentSnapshot.getString("h_pincode"));

                        hospitals.put(queryDocumentSnapshot.getId(), map);
                    }
                    Log.d("MAP-->" , hospitals.toString());  // display the Map
                }
            });
    Log.d("MAP-->" , hospitals.toString());  // but here it will display null

}
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Kushal P
  • 7
  • 2
  • 1
    It should not be null unless you are calling `readHospitalData` before `onCreateView` . It will be empty `collectionReference.get()` is an asynchronous call so you need to put Logs inside `onSuccess` only . – ADM May 06 '21 at 05:07
  • You might also check **[this](https://stackoverflow.com/questions/48499310/how-to-return-a-documentsnapshot-as-a-result-of-a-method/48500679#48500679)** out. – Alex Mamo May 06 '21 at 08:09

2 Answers2

1

Firebase API calls that deal with reading and writing data are fully asynchronous. This means that the call always returns immediately, without blocking the code to wait for a result. The results come sometime later, whenever they’re ready.

That's why you are getting null here

CollectionReference collectionReference = fStore.collection("hospital Details");
    collectionReference.get()
            .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
    public void onSuccess(DocumentSnapshot snapshot) {
        // handle the document snapshot here
    }
});
task.addOnFailureListener(new OnFailureListener() {
    public void onFailure(Exception e) {
        // handle any errors here
    }
});
    Log.d("MAP-->" , hospitals.toString());

REASON FOR THIS TYPE OF BEHAVIOUR OF FIREBASE CONSUMER API: ->why make the consumer of an API go through the extra trouble of using an asynchronous API? Can’t we just have simpler synchronous APIs instead? Well, Firebase could provide synchronous APIs for mobile apps, but then we’d inherit one of two problems that are both even worse than writing this extra code.

1.Calling a synchronous function on your app’s main thread could freeze the app indefinitely, which is a terrible user experience. On Android, it could also soft-crash with an Application Not Responding (ANR) dialog.

  1. To avoid blocking the main thread with the asynchronous method, we’d have to manage our own threads to call these APIs properly. This is even more code, and can be difficult to get right. Even expert engineers have challenges with correct threading behaviour.

Think async!

Firebase APIs are sensitive to the performance of your app’s main thread. This means that any Firebase API that needs to deal with data on disk or network is implemented in an asynchronous style. The functions will return immediately, so you can call them on the main thread without worrying about performance. All you have to do is implement callbacks using the patterns established in the documentation and samples.

There is one solution for this problem that you have to use Kotlin-Flow for the synchronous call, go through this link: https://developer.android.com/kotlin/flow#callback

Nitin Prakash
  • 927
  • 9
  • 16
0

I solved this issue by using Callback

I created HospitalData.class and retrieved the data from firebase there. And by using the Callback interface I read the data in the Fragment

HospitalData.class

    public class HospitalData {
    
        FirebaseFirestore fStore = FirebaseFirestore.getInstance();
        Map<String, Map<String, String>> hospitals = new HashMap<>();
        HospitalCallback hospitalCallback;
    
        HospitalData(){
            // empty constructor
        }
    
        public void setHospitalCallback(HospitalCallback hospitalCallback) {
            this.hospitalCallback = hospitalCallback;
        }
    
        void getHospitalData(){
            CollectionReference collectionReference = fStore.collection("hospital Details");
            collectionReference.get()
                    .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
                        @Override
                        public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
                            for(QueryDocumentSnapshot queryDocumentSnapshot: queryDocumentSnapshots){ 
                                Map<String, String> map = new HashMap<>();
                                map.put("h_name", queryDocumentSnapshot.getString("h_name"));
                                map.put("h_email", queryDocumentSnapshot.getString("h_email"));
                                map.put("h_phone", queryDocumentSnapshot.getString("h_phone"));
                                map.put("h_road", queryDocumentSnapshot.getString("h_road"));
                                map.put("h_area", queryDocumentSnapshot.getString("h_area"));
                                map.put("h_city", queryDocumentSnapshot.getString("h_city"));
                                map.put("h_state", queryDocumentSnapshot.getString("h_state"));
                                map.put("h_pincode", queryDocumentSnapshot.getString("h_pincode"));
    
                                hospitals.put(queryDocumentSnapshot.getId(), map);
    
                            }
                            hospitalCallback.DisplayHospitalData(hospitals);
                        }
                    });
        }
    
        interface HospitalCallback{
            public void DisplayHospitalData(Map<String, Map<String, String>> map);
        }
}

In my HomeFragment.class

public class HomeFragment extends Fragment implements HospitalCallback{

    static  Map<String, Map<String, String>> hospitals ;
    HospitalData hospitalData;

    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.fragment_home, container, false);
        hospitals = new HashMap<>();
        hospitalData = new HospitalData();
        hospitalData.getHospitalData();
        hospitalData.setHospitalCallback(this);

        return root;
    
    }

    @Override
    public void DisplayHospitalData(Map<String, Map<String, String>> map) {
         Log.d("HOSPITAL DATA-->", map.toString());
    }

}
Kushal P
  • 7
  • 2