I am using Java8. I have an Listener that calls onSuccess
when completed with a customToken
.
@Override
public String getCustomToken(Person person) {
FirebaseAuth.getInstance().createCustomToken(person.getUid()).addOnSuccessListener(new OnSuccessListener<String>() {
@Override
public void onSuccess(String customToken) {
// I would like to return the customToken
}
});
return null;
}
Question
How do I get this method to return the String customToken
?
Your question is intriguing, but the accepted answer unfortunately provides you with wrong means.
The problem with your question is that of API. You are trying to use callbacks in a way they are not designed to be used. A callback, by definition, is supposed to provide a means to do something asynchronously. It is more like a specification of what to do when something happens (in future). Making a synchronous method like getCustomToken()
return something that is a result of an inherently asynchronous operation like onSuccess()
implies a fundamental disconnect.
While dealing with callbacks, it is critical to understand the importance of continuations: taking actions when certain events of interest happen. Note that these events may not even happen. But you are specifying in the code the actions to take, if and when those events occur. Thus, continuation style is a shift from procedural style.
What adds to the data flow complexity is the syntax of the anonymous inner classes. You tend to think "oh, why can't I just return from here what onSuccess()
returns? After all, the code is right here." But imagine that Java had no inner classes (and as you may know, (anonymous) inner class can easily be replaced by a class that is not an inner class). You'd have needed to do something like:
OnSuccessListener listener = new SomeImplementation();
FirebaseAuth.getInstance().createCustomToken(listener);
Now, the code that returned data (String) is gone. You can even visually reason that in this case, there is no way for your method to return a string -- it is simply not there!
So, I encourage you to think of what should happen if and when (in future) onSuccess()
is called on the OnSuccessListener
instance that you pass in. In other words, think twice if you really want to provide in your API, the getCustomToken()
method (that returns a token string, given a Person
instance).
If you absolutely must provide such a method, you
Should document that the returned token may be null
(or something more meaningful like None
) and that your clients must try again if they want a valid value.
Should provide a listener that updates a thread-safe container of tokens that this method reads.
Googling around, I found the Firebase documentation. This also seems to suggest taking an action on success (in a continuation style):
FirebaseAuth.getInstance().createCustomToken(uid)
.addOnSuccessListener(new OnSuccessListener<String>() {
@Override
public void onSuccess(String customToken) {
// **Send token back to client**
}
});
The other problem with trying to provide such API is the apparent complexity of the code for something trivial. The data flow has become quite complex and difficult to understand.
If blocking is acceptable to you as a solution, then perhaps you can use the Callable-Future style where you pass a Callable
and then later do a get()
on the Future
that may block. But I am not sure if that is a good design choice here.
This would work syntactically:
final List<String> tokenContainer = new ArrayList<>();
FirebaseAuth.getInstance().createCustomToken(person.getUid()).addOnSuccessListener(new OnSuccessListener<String>() {
@Override
public void onSuccess(String customToken) {
tokenContainer.add(customToken);
}
});
return tokenContainer.get(0);
As said; this works syntactically. But if it really works would depend if the overall flow is happening in one thread; or multiple ones.
In other words: when the above code is executed in sequence, then that list should contain exactly one entry in the end. But if that callback happens on a different thread, then you would need a more complicated solution. A hackish way could be to prepend
return tokenContainer.get(0);
with
while (tokenContainer.isEmpty()) {
Thread.sleep(50);
}
return tokenContainer.get(0);
In other words: have the "outer thing" sit and wait for the callback to happen. But the more sane approach would be to instead use a field of the surrounding class.
Edit: if the above is regarded a hack or not; might depend on your context to a certain degree. The only thing that really troubles me with your code is the fact that you are creating a new listener; which gets added "somewhere" ... to stay there?! What I mean is: shouldn't there be code to unregister that listener somewhere?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With