繁体   English   中英

用Java编写自己的事件的正确方法是什么

[英]What's proper way to make my own events in java

我是学生,并且正在与几个朋友一起进行项目。 我的任务是制作类似类库的东西。 该库中的类应为我的朋友提供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()方法可以触发事件并继续进行。

我感谢您的帮助。

这是我前一段时间写的事件监听器文章。 本文介绍了如何编写自己的事件侦听器。

您的要求是正确的,如果您希望库可以与任何GUI一起使用,您都希望编写自己的事件侦听器。

我最熟悉Swing,因此,您将拥有如下所示的GUI代码:

button.addActionListener(new ActionListener(){
    @Override
    public void actionPerformed(ActionEvent event){
        dal.buttonPressed(data);
    }
});

如果您希望它与正在使用的GUI完全无关,那么唯一的解决方案就是让接收方在dataArrived对其进行dataArrived 由于每个工具箱都有其自己的实现,因此您可以真正使它与任何工具箱一起使用的方法就是忽略它。 否则,您最终会得到的是“受支持的工具箱”列表以及每个工具箱的情况。

如果只希望从someMethodThatWaitsForData执行dataArrived ,则可以每次创建自己的调度线程或创建一个新线程。

如果您想真正独立于任何前端系统,建议您创建两个线程。 第一个是您的Waiter ,它将仅侦听事件并将它们放入某种队列中(请参阅“所有已知的实现类”部分)。 每当队列不为空时,第二个将调用一个或多个数据侦听器。

自从并发包的发明以来,在后台调用Runnable的概念已被弃用。 这样做是在较早的日子完成的主要原因是,GUI代码需要在不同的线程中执行,以确保它保持响应,即使主线程忙于执行一些计算,但实际的多线程仍然在其早期。 产生的invokeLater概念可以使用,但是创建开销很大。 如果您经常需要做些次要的事情,那么这尤其烦人,但是每次您需要创建一个全新的Runnable时,只是为了将该事件放入Swing线程中。

更现代的方法应该使用线程安全列表,例如LinkedBlockingQueue。 在这种情况下, 任何线程都可以将事件扔到队列中,而其他侦听器/ GUI事件处理程序可以异步将其取出,而不需要同步或后台Runnable。

例:

您初始化一个新的Button,一旦按下它,它将进行大量的计算。

在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(执行程序在内部也使用btw),其中myListeners是固定的(并发)侦听器列表,并分配要分发的Object数据。 在LinkedBlockingQueue上,几个线程正在使用take()方法。 现在,从新事件中唤醒一个,并执行以下操作:

while (true) {
  nextEvent = eventQueue.take();
  for (EventTarget target : nextEvent.listeners) {
    target.update(nextEvent.data);
  }
}

所有这些背后的一般想法是,一旦您将所有内核用于代码,并且您将生成的对象数量保持在尽可能低的水平(可能进行更多优化,这只是演示代码)。 特别是,您不需要为频繁发生的事件而从头实例化新的Runnable,这会带来一定的开销。 缺点是使用这种GUI模型的代码需要处理一直在发生的多线程这一事实。 使用Java提供给您的工具并不难,但这首先是设计代码的完全不同的方式。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM