Issue
I am using the Real Time Database from Firebase on java for Android and I am trying to write a set of helper function to facilitate read and write functions from/to the RTDB.
My function looks like the following and is supposed to return a HashMap of my Parking objects on the database; I get a reference to my database and add an onSuccessListener
where I iterate through the snapshot and add each Parking
object to my HashMap and return the HashMap parkings
.
The problem is the function returns parkings
with no values in it before the onSuccessListener runs.
public static ArrayList<Parking> getParkingLots() {
DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference();
Task<DataSnapshot> dataSnapshotTask = mDatabase.get();
ArrayList<Parking> parkings = new ArrayList<Parking>();
dataSnapshotTask.addOnSuccessListener(new OnSuccessListener<DataSnapshot>() {
@Override
public void onSuccess(DataSnapshot dataSnapshot) {
Iterable<DataSnapshot> parkingsData = dataSnapshot.getChildren();
for (DataSnapshot parking :
parkingsData) {
parkings.add(parking.getValue(Parking.class));
}
}
});
return parkings;
}
I tried this implementation as well where I directly try and get the results from the Task datSnapshotTask
but I get an exeption thrown
java.lang.IllegalStateException: Task is not yet complete
.
public static HashMap<String, Parking> getParkingLots() {
DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference();
Task<DataSnapshot> dataSnapshotTask = mDatabase.get();
Iterable<DataSnapshot> parkingsData = dataSnapshotTask.getResult().getChildren();
HashMap<String, Parking> parkings = new HashMap<String, Parking>();
for (DataSnapshot parking :
parkingsData) {
parkings.put(parking.getKey(), parking.getValue(Parking.class));
}
return parkings;
}
Is there a way to get the results from the Task in an await fashion ?
Solution
Data is loaded from Firebase asynchronously, since it may have to come from the server. While the data is being loaded, your main code continues to run. Then when the data is available, the task completes and your onSuccess
gets called.
It's easiest to see what this means in practice by running in a debugger, or by adding some logging:
DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference();
Log.i("Firebase", "1. Starting to load data");
Task<DataSnapshot> dataSnapshotTask = mDatabase.get();
for (DataSnapshot parking: parkingsData) {
Log.i("Firebase", "2. Got data");
}
Log.i("Firebase", "3. After starting to load data");
When you run this code, it prints:
Starting to load data
After starting to load data
Got data
This is probably not the order that you expected, but it actually working as intended. It also explains why you're not getting a result from your getParkingLots
function: by the time your return parkings
runs, the parkings.add(parking.getValue(Parking.class))
hasn't been called yet!
The solution for this is always the same: any code that needs the data from the database, needs to be inside your onSuccess
method or be called from there. This means you can't return the parking lots from the function, but you can pass in a callback that takes them as a parameter. Or you could return a Task<HashMap<String, Parking>>
pretty similar to what Firebase does got get()
.
For an example of the callback, see my longer explanation on: getContactsFromFirebase() method return an empty list
Answered By - Frank van Puffelen
Answer Checked By - Gilberto Lyons (JavaFixing Admin)