Android: wait for firebase valueEventListener
As previously mentioned, you can use Firebase Task API presented on Google I/O 2016.
Task<?>[] tasks = new Task[] {
saveUserName(username),
saveFriends(friends),
saveOtherStuff(stuff)
};
Tasks.whenAll(tasks)
.continueWithTask(new RollbackIfFailure())
.addOnCompleteListener(this)
.addOnFailureListener(this);
If each step that makes a modification can run in parallel, you can create a method that returns a Task
for each one of them as follows:
public Task<String> saveUserName(final String username) {
final TaskCompletionSource<String> tcs = new TaskCompletionSource<>();
mDatabase.child("users")
.child(username)
.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
String username = dataSnapshot.getValue(String.class);
if (null == username) {
tcs.setResult(null);
} else {
tcs.setException(new IllegalStateException(username));
}
}
@Override
public void onCancelled(FirebaseError firebaseError) {
tcs.setException(new IOException(TAG, firebaseError.toException()));
}
});
return tcs.getTask();
}
If any of them fail, you need to rollback the operations. Since this can be threated by a single task, you can create a Continuation
task:
class RollbackIfFailure implements Continuation<Void, Task<Void>> {
@Override
public Task<Void> then(@NonNull Task<Void> task) throws Exception {
final TaskCompletionSource<Void> tcs = new TaskCompletionSource<>();
if (task.isSuccessful()) {
tcs.setResult(null);
} else {
// Rollback everything
}
return tcs.getTask();
}
}
The listener callback methods run on the main thread. If you call alreadyInUse()
on the main thread, it will block the thread at semaphore.acquire()
, preventing the callbacks from running and releasing the semaphore.
You might find this answer to a related question helpful.