我正在使用Java8。我有一个监听器,当用onSuccess
完成时调用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;
}
问题
如何让此方法返回字符串customToken
?
答案 0 :(得分:2)
你的问题很有趣,但不幸的是,接受的答案会为你提供错误的手段。
您的问题是API的问题。您试图以不适合使用的方式使用回调。根据定义,回调应该提供一种异步执行操作的方法。它更像是某事发生时(未来)应该做什么的规范。使像getCustomToken()
这样的同步方法返回一些由onSuccess()
这样的固有异步操作产生的结果意味着基本的断开。
在处理回调时,了解 continuation 的重要性至关重要:在发生某些感兴趣的事件时采取行动。请注意,这些事件甚至可能不会发生。但是,您要在代码中指定要执行的操作,以及这些事件发生的时间和时间。因此,延续风格是从程序风格的转变。
数据流复杂性增加的是匿名内部类的语法。你倾向于思考&#34;哦,为什么我不能从这里返回onSuccess()
返回的内容?毕竟,代码是正确的这里。&#34;但是想象一下Java没有内部类(并且你可能知道,(匿名)内部类很容易被一个不是内部类的类所取代)。您需要执行以下操作:
OnSuccessListener listener = new SomeImplementation();
FirebaseAuth.getInstance().createCustomToken(listener);
现在,返回数据(String)的代码已经消失。您甚至可以直观地推断出在这种情况下,您的方法无法返回字符串 - 它根本就不存在!
因此,我鼓励您考虑在您传入的onSuccess()
实例上调用OnSuccessListener
的时间和时间(将来)会发生什么。换句话说,如果您真的,请三思而后行想要在您的 API中提供getCustomToken()
方法(返回一个令牌字符串,给定Person
个实例)。
如果您绝对必须提供这样的方法,那么
应记录返回的令牌可能是null
(或更有意义的内容,如None
),并且如果客户需要有效值,则必须再次尝试。
应该提供一个侦听器来更新此方法读取的标记的线程安全容器。
在Google上搜索,我找到了Firebase documentation。这也似乎建议采取行动成功(以延续方式):
FirebaseAuth.getInstance().createCustomToken(uid)
.addOnSuccessListener(new OnSuccessListener<String>() {
@Override
public void onSuccess(String customToken) {
// **Send token back to client**
}
});
尝试提供此类API的另一个问题是代码的明显复杂性。数据流变得非常复杂且难以理解。
如果您认为阻止是一种解决方案,那么也许您可以使用Callable-Future样式传递Callable
,然后在get()
上Future
进行json_decode
可能阻止。但我不确定这是否是一个很好的设计选择。
答案 1 :(得分:1)
这将在语法上起作用:
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);
如上所述;这在语法上有效。但是,如果它确实有效将取决于整个流程是否在一个线程中发生;或多个。
换句话说:当上面的代码按顺序执行 时,该列表最后应该包含完全一个条目。但是如果回调发生在另一个线程上,那么你需要一个更复杂的解决方案。一种hackish方式可能是前置
return tokenContainer.get(0);
与
while (tokenContainer.isEmpty()) {
Thread.sleep(50);
}
return tokenContainer.get(0);
换句话说:让“外部事物”坐下来等待回调发生。但更合理的方法是使用周围类的字段。
编辑:如果以上被认为是黑客攻击;可能在某种程度上取决于你的背景。 真正使代码困扰我的唯一事情就是你正在创建一个 new 监听器;它被添加到“某处”......留在那里?!我的意思是:不应该有代码取消注册那个侦听器吗?
答案 2 :(得分:0)
将变量提取到合适的范围(类属性或方法变量)
private String customToken;
@Override
public String getCustomToken(Person person) {
FirebaseAuth.getInstance().createCustomToken(person.getUid()).addOnSuccessListener(new OnSuccessListener<String>() {
@Override
public void onSuccess(String customToken) {
this.customToken = customToken
}
});
return null;
}
答案 3 :(得分:0)
最初接受的答案建议睡眠线程,这是一个糟糕的解决方案,因为你无法知道线程需要多长时间才能睡眠。更好的解决方案是使用semaphore(或类似地,latch)。在侦听器获取值之后,它会释放一个信号量,允许线程返回值,如下所示。
private final AtomicReference<String> tokenReference = new AtomicReference();
private final Semaphore semaphore = new Semaphore(0);
public String getCustomToken(Person person) {
FirebaseAuth.getInstance().createCustomToken(person.getUid()).addOnSuccessListener(customToken -> {
this.tokenReference.set(customToken);
this.sempahore.release();
});
this.semaphore.acquire();
return this.tokenReference.get();
}
另请注意,我使用了AtomicReference
,因为为了满足您的要求,所有侦听器必须在与调用getCustomToken
的线程不同的线程上调用,并且我们希望值同步(我猜想Firebase正在创建一个线程,或者这个调用是通过网络发生的)。由于this.tokenReference
将被覆盖,因此在getCustomToken
被多次调用时可能会获得更新的值,根据您的使用情况,这可能是可接受的,也可能是不可接受的。