Java事件侦听器返回值

时间:2017-03-03 12:14:38

标签: java

我正在使用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

4 个答案:

答案 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),并且如果客户需要有效值,则必须再次尝试。

    < / LI>
  • 应该提供一个侦听器来更新此方法读取的标记的线程安全容器。

在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被多次调用时可能会获得更新的值,根据您的使用情况,这可能是可接受的,也可能是不可接受的。

相关问题