1

I want to parcel an array with a fragment. The parcelable array depends on firestore data retrieving. I mean the elements of array are got from Firestore. But retrieving data from Firestore is geting very late and and the next lines of code are being executed as well as a null array is being parceled. What to do to make the next lines wait until the data is retrieved from the Firestore??

public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {



private static final String MAPVIEW_BUNDLE_KEY = "MapViewBundleKey";
private static final int PERMISSIONS_REQUEST_ENABLE_GPS = 9001;
private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 9002;
private static final String TAG = "MainActivity";
private static final int ERROR_DIALOG_REQUEST = 9003;
private boolean mLocationPermissionGranted = false;

private List<User>mUserList=new ArrayList<>();
private ArrayList<UserLocation>mUserLocations=new ArrayList<>();

private FusedLocationProviderClient mFusedLocationProviderClient;
FirebaseFirestore mDb;

private GoogleMap mGoogleMap;
private UserLocation mUserPosition=null;
private LatLngBounds latLngBoundary;

private ClusterManager mClusterManager;
private MyClusterManagerRenderer mClusterManagerRenderer;
private ArrayList<ClusterMarker> mClusterMarkers=new ArrayList<>();


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mDb = FirebaseFirestore.getInstance();

    initUser();//in this method the data retrieving is implemented

    if (findViewById(R.id.fragment_container) != null) {
        if (savedInstanceState != null) {
            return;
        }            
        MapFragment mapFragment = new MapFragment();
        Bundle bundle=new Bundle();
        bundle.putParcelableArrayList(getString(R.string.userlocations_array),  mUserLocations);
        mapFragment.setArguments(bundle);


        getSupportFragmentManager().beginTransaction()
                .add(R.id.fragment_container, mapFragment).commit();
    }

}

private void initUser() {
    User user=new User();
    user.setEmail("nobeld@gmail.com");
    user.setResponse("ok");
    user.setUser("student");
    user.setUserId("5");
    user.setUserName("nobel");
    ((UserClient)(getApplicationContext())).setUser(user);
    mUserList.add(user);
    User user1=new User();
    user1.setEmail("rahuld@gmail.com");
    user1.setResponse("ok");
    user1.setUser("student");
    user1.setUserId("6");
    user1.setUserName("rahul");
    User user2=new User();
    user2.setEmail("milond@gmail.com");
    user2.setResponse("ok");
    user2.setUser("student");
    user2.setUserId("7");
    user2.setUserName("milon");
    mUserList.add(user1);
    mUserList.add(user2);
    for(User u: mUserList){
        getUserLocation(u);
        //firestore is implemented inside this method
        Log.d(TAG, "initUser: in user array");
    }



}

private void setCameraView(){
    if(mUserPosition!= null){
        Log.d(TAG, "setCameraView: user position got");
        double bottomboundary=mUserPosition.getGeo_point().getLatitude()-.05;
        double leftboundary = mUserPosition.getGeo_point().getLongitude()-.05;
        double upboundary = mUserPosition.getGeo_point().getLatitude()+.05;
        double rightboundary = mUserPosition.getGeo_point().getLongitude()+.05;
        latLngBoundary=new LatLngBounds(new LatLng(bottomboundary,leftboundary),
                new LatLng(upboundary,rightboundary));
        mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngBounds(latLngBoundary,0));

    }else {
        Log.d(TAG, "setCameraView: user position is null");
    }
}
private void getUserLocation(User user){
    Log.d(TAG, "getUserLocation: ");
    DocumentReference locationRef=mDb.collection(getString(R.string.collection_user_location_student))
            .document(user.getUserId());
    locationRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
        @Override
        public void onComplete(@NonNull Task<DocumentSnapshot> task) {
            if(task.isSuccessful()){
                if(task.getResult().toObject(UserLocation.class)!= null){
                    Log.d(TAG, "Location onComplete: ");
                    UserLocation u=task.getResult().toObject(UserLocation.class);
                    mUserLocations.add(u);
                    //here adding the elements to array.

                }else {
                    Log.d(TAG, "onComplete: result is empty");
                }
            }
        }
    });

}

}

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
Nobel Dhar
  • 33
  • 1
  • 4
  • 3
    Firebase APIs are asynchronous by design. It would be harmful for your app if they blocked your main thread code until completion, which could take any amount of time. Read more about that here: https://medium.com/p/e037a6654a93/info?source=email-6a53613f4e6d-1539641699893-activity.response_created – Doug Stevenson Jan 03 '19 at 16:49

2 Answers2

2

Because the data is available only inside the onComplete() method due it's asynchronous behavior, by the time you are trying to add the mUserLocations list to the Bundle object, the data hasn't finished loading yet from the database and that's why is not accessible (the list is empty). A quick solve for this problem would be to move the following lines of code:

Bundle bundle=new Bundle();
bundle.putParcelableArrayList(getString(R.string.userlocations_array),  mUserLocations);
mapFragment.setArguments(bundle);

Inside the inside the onComplete() method right after the following line of code:

//here adding the elements to array.

What to do to make the next lines wait until the data is retrieved from the Firestore??

If you want to use the mUserLocations outside that method, I recommend you see the last part of my anwser from this post in which I have explained how it can be done using a custom callback. You can also take a look at this video for a better understanding.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
1

These functions do not complete immediately; instead, they return results via the OnCompleteListener callback interface that you've provided. This is because it takes some time for your device to talk to the server, for the server to authenticate and process your request, and return the data you requested.

If you need to collect all the data before continuing, I suggest either re-writing your query to query for all of the required data at once (i.e. not individual requests at a time) then perform your subsequent steps after the callback has been executed (i.e. invoke a method at the end of the callback), or alternatively, by performing your request off the UI Thread and use synchronization to block execution of your task until it is completed.

Either way, the requests you are making are asynchronous and your code is trying to do something useful in the time that it will be waiting around for a response. This is why it continues to step through.

Mapsy
  • 4,192
  • 1
  • 37
  • 43