我是学生,我和很少的朋友一起做项目。我的任务是制作类库。这个库中的类应该为我的朋友提供API,他必须使GUI成为应用程序的一部分。 GUI可以由任何工具包(Swing,JavaFX,SWT,AWT,所有应该工作,实际上,即使没有GUI也应该工作)。我需要制作等待数据从网络到达的类。我不知道数据何时到达,并且UI必须在等待期间响应,所以我把它放在不同的线程中。现在的问题是如何在数据到达时进行GUI响应。好吧,我认为这是异步事件,GUI应该注册事件处理程序,我应该在事件发生时调用这些方法。我提出了这个解决方案:
interface DataArrivedListener{
void dataArrived(String data);
}
class Waiter{
private DataArrivedListener dal;
public void setDataArrivedListener(DataArrivedListener dal){
this.dal = dal;
}
void someMethodThatWaitsForData(){
// some code goes here
data = bufRdr.readLine();
//now goes important line:
dal.dataArrived(data);
// other code goes here
}
}
我的问题是: 我应该用这样的东西替换“重要”的一行:
java.awt.EventQueue.invokeLater(new Runnable(){
@Override
public void run(){
dal.dataArrived(data);
}
});
或类似的东西:
javafx.Platform.runLater(new Runnable(){
@Override
public void run(){
dal.dataArrived(data);
}
});
或许我应该做一些完全不同的事情?
问题是我不确定哪种方法适用于任何类型的UI。如果是GUI,dataArrived()可能会对GUI进行更改,无论它是什么类型的GUI,都应该在屏幕上正确地绘制这些更改。我也认为如果我“稍后调用此代码”会更好,这样我的someMethodThatWaitsForData()方法可以触发事件并继续工作。
感谢您的帮助。
答案 0 :(得分:2)
这是我之前写过的一篇Event Listener文章。本文解释了如何编写自己的事件监听器。
如果您希望您的库可以使用任何GUI,那么您想要编写自己的事件侦听器是正确的。
我对Swing最熟悉,所以是的,您将拥有如下所示的GUI代码:
button.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent event){
dal.buttonPressed(data);
}
});
答案 1 :(得分:1)
如果您希望它与使用的GUI完全无关,唯一真正的解决方案是让接收方在dataArrived
处理它。由于每个工具包都有自己的实现,所以你真正能够使它与任何工具包一起使用就是忽略它。否则你最终会得到的是“支持的工具包”列表和每个工具包的案例。
如果你只想让dataArrived
从someMethodThatWaitsForData
开始执行,那么你可以自己创建自己的调度线程或每次创建一个新线程。
答案 2 :(得分:1)
如果你想真正独立于任何前端系统,我建议你创建两个线程。第一个是你的Waiter
,它只会监听事件并将它们放入某种Queue中(参见“所有已知的实现类”部分)。第二个将在队列不为空时调用数据侦听器。
答案 3 :(得分:1)
自发明并发包以来,在后台调用Runnable的概念已被弃用。这在早些时候完成的主要原因是,GUI代码需要在不同的线程中执行,以保证它保持响应,即使主线程忙于进行一些计算,但实际的多线程仍然是在很早的时候。由此产生的invokeLater概念可行,但具有强大的创建开销。如果你经常需要做一些小事情,这尤其令人讨厌,但每次你需要创建一个全新的Runnable时,只需将该事件放入Swing线程。
更现代的方法应该使用线程安全列表,例如LinkedBlockingQueue。在这种情况下,任何线程都可以将事件抛入队列,而其他侦听器/ GUI事件处理程序可以异步地将它们取出,而无需同步或后台Runnables。
示例:
初始化一个新按钮,按下它后会进行一些繁重的计算。
在GUI线程中,单击按钮后会调用以下方法:
void onClick() {
executor.submit(this.onClickAction);
}
其中executor
是ExecutorService,onClickAction
是Runnable。由于onClickAction是在Button创建期间提交过一次的Runnable,因此此处不会访问新内存。让我们看看这个Runnable实际上做了什么:
void run() {
final MyData data = doSomeHeavyCalculation();
dispatcher.dispatch(myListeners, data);
}
dispatcher
在内部使用如上所述的LinkedBlockingQueue(Executor在内部使用一个以及btw),其中myListeners是一个固定(并发)的侦听器列表和要分派的Object的数据。在LinkedBlockingQueue上,有几个线程正在使用take()
方法等待。现在,一个人在新事件中被唤醒并执行以下操作:
while (true) {
nextEvent = eventQueue.take();
for (EventTarget target : nextEvent.listeners) {
target.update(nextEvent.data);
}
}
所有这一切背后的一般思想是,一旦你为你的代码利用所有核心,另外你保持尽可能低的生成对象的数量(可能有一些更优化,这只是演示代码)。特别是您不需要从头开始为频繁事件实例化新的Runnables,这会带来一定的开销。缺点是使用这种GUI模型的代码需要处理多线程一直在发生的事实。使用Java提供给您的工具并不困难,但这是一种完全不同的设计代码的方式。