I have 2 callback functions in my app, one of which requires the other one's result, so I want them to execute asynchronously. The scenario is this:
I obtain the device's token with FirebaseInstanceId.getInstance().getInstanceId()
. That's the 1st callback.
When I have the token, I use it in accessing the Firebase Realtime DB to get the user's data. That's the 2nd callback.
In my current solution, I'm using AsyncTask
with a Semaphore
in the doInBackground
function, like so:
private class AsyncOp extends AsyncTask<Void, Void, String> {
@Override
protected String doInBackground(Void... voids) {
final Semaphore semaphore = new Semaphore(0);
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(new OnSuccessListener<InstanceIdResult>() {
@Override
public void onSuccess(InstanceIdResult instanceIdResult) {
token = instanceIdResult.getToken();
JSONObject requestObject = new JSONObject();
try {
requestObject.put("token", token);
semaphore.release();
} catch (JSONException e) {
Log.e(TAG_MAINACTIVITY, token);
semaphore.release();
}
JsonObjectRequest req = new JsonObjectRequest(Request.Method.POST, REQUEST_URL + "token",
requestObject, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
Log.i(TAG_MAINACTIVITY, "Token saved successfully");
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG_MAINACTIVITY, "Failed to save token - " + error);
}
});
_queue.add(req);
}
});
try {
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
return token;
}
@Override
protected void onPostExecute(String token) {
getUser(token);
}
}
public void getUser(String token) {
db.child("users").child(token).addListenerForSingleValueEvent(new ValueEventListener() {
@SuppressLint("SetTextI18n")
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
if (!dataSnapshot.exists()) {
// Navigate to fragment where new users can sign up:
goToSignup();
} else {
currentUser = dataSnapshot.getValue(User.class);
assert currentUser != null;
updateHeadline(currentUser.getName()); // Update UI to display the user's name
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
Log.d(TAG_MAINACTIVITY, databaseError.getMessage());
}
});
}
and in my OnCreate
I execute as follows:
_queue = Volley.newRequestQueue(this);
AsyncOp getToken = new AsyncOp();
getToken.execute();
It works just fine, but I can't help but feel like I'm missing the whole point here and that there's a better solution that I can use. I've scoured other StackOverflow questions dealing with similar issues and found some suggestions, but I got tangled up trying to implement them, being pretty new to this whole ordeal (that's actually my first "real" project). I tried looking for something that would work like a Promise
and found Future
but I probably didn't do it properly since it didn't work. That's the only solution that worked for me, other than simply nesting getUser(token)
inside the first callback. So if you have any ideas that would work for me, I'd be glad to hear it.
Firebase performs all network and disk I/O off the main thread. That means there is seldom a reason to run Firebase interactions in a background task.
Aside from that, the flow is significantly more complicated when you use a semaphore. I'd consider just putting the call to get a user into the completion handler of the previous call.
Combined that becomes:
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(new OnSuccessListener<InstanceIdResult>() {
@Override
public void onSuccess(InstanceIdResult instanceIdResult) {
token = instanceIdResult.getToken();
JSONObject requestObject = new JSONObject();
requestObject.put("token", token);
JsonObjectRequest req = new JsonObjectRequest(Request.Method.POST, REQUEST_URL + "token",
requestObject, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
Log.i(TAG_MAINACTIVITY, "Token saved successfully");
getUser(token);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG_MAINACTIVITY, "Failed to save token - " + error);
}
});
_queue.add(req);
}
});