简体   繁体   English

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

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

I'm student and I'm working on project with few of my friends. 我是学生,并且正在与几个朋友一起进行项目。 My task is to make something like class library. 我的任务是制作类似类库的东西。 Classes in this library should provide API for my friend who must make GUI part of application. 该库中的类应为我的朋友提供API,我的朋友必须将GUI作为应用程序的一部分。 GUI could be made by any toolkit (Swing, JavaFX, SWT, AWT, all should work, in fact, it should work even if there is no GUI). GUI可以由任何工具包(Swing,JavaFX,SWT,AWT都可以使用,实际上,即使没有GUI也可以使用)。 I need to make class that waits for data to arrive from network. 我需要创建等待数据从网络到达的类。 I don't know when data will arrive, and UI must be responsive during waiting, so I put that in different thread. 我不知道数据何时到达,UI必须在等待期间响应,因此我将其放在其他线程中。 Now problem is how to make GUI respond when data arrive. 现在的问题是如何使GUI在数据到达时做出响应。 Well, I tought that this is asynchronous event and GUI should register event handlers, and I should call that methods when event happens. 好吧,我坚信这是异步事件,GUI应该注册事件处理程序,并且当事件发生时我应该调用该方法。 I proposed this solution: 我提出了这个解决方案:

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

    }


}

My question is: Should I replace "important" line with something like this: 我的问题是:我应该用以下内容替换“重要”行:

java.awt.EventQueue.invokeLater(new Runnable(){
    @Override
    public void run(){
        dal.dataArrived(data);
    }
});

Or something like: 或类似的东西:

javafx.Platform.runLater(new Runnable(){
    @Override
    public void run(){
        dal.dataArrived(data);
    }
});

Or maybe I should do something completely different? 还是我应该做一些完全不同的事情?

Problem is that I'm not sure which of this will work for any type of UI. 问题是我不确定哪种方法适用于任何类型的UI。 If it's GUI, dataArrived() could potentialy make changes to GUI and no matter what type of GUI it is, this changes should be drawn on screen properly. 如果是GUI,则dataArrived()可能会对GUI进行更改,无论GUI是哪种类型,都应在屏幕上正确绘制此更改。 I also think that it is better if I do "invoke this code later" so that my someMethodThatWaitsForData() method could trigger event and continue on with it's on work. 我还认为,最好是“稍后再调用此代码”,以便我的someMethodThatWaitsForData()方法可以触发事件并继续进行。

I appreciate your help. 我感谢您的帮助。

Here's an Event Listener article I wrote a while back. 这是我前一段时间写的事件监听器文章。 The article explains how you write your own event listeners. 本文介绍了如何编写自己的事件侦听器。

You're correct in that you want to write your own event listeners if you want your library to work with any GUI. 您的要求是正确的,如果您希望库可以与任何GUI一起使用,您都希望编写自己的事件侦听器。

I'm most familiar with Swing, so yes, you'll have GUI code that looks like this: 我最熟悉Swing,因此,您将拥有如下所示的GUI代码:

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

If you want it to be completely agnostic to what GUI is being used the only real solution is to let the receiver handle it in dataArrived . 如果您希望它与正在使用的GUI完全无关,那么唯一的解决方案就是让接收方在dataArrived对其进行dataArrived Since every toolkit has its own implementation all you can really do to make it work with any toolkit is to disregard it. 由于每个工具箱都有其自己的实现,因此您可以真正使它与任何工具箱一起使用的方法就是忽略它。 Otherwise what you will actually end up with is a list of "supported toolkits" and a case for each one. 否则,您最终会得到的是“受支持的工具箱”列表以及每个工具箱的情况。

If you just want dataArrived to be executed away from someMethodThatWaitsForData then you could make your own dispatch thread or make a new thread each time. 如果只希望从someMethodThatWaitsForData执行dataArrived ,则可以每次创建自己的调度线程或创建一个新线程。

If you want to be truly independent of any front-end system, I would recommend creating two threads. 如果您想真正独立于任何前端系统,建议您创建两个线程。 The first is your Waiter , which will just listen for events and put them into a Queue of some sort (see the "All Known Implementing Classes" section). 第一个是您的Waiter ,它将仅侦听事件并将它们放入某种队列中(请参阅“所有已知的实现类”部分)。 The second will invoke the data listener or listeners whenever the queue is not empty. 每当队列不为空时,第二个将调用一个或多个数据侦听器。

The concept of invoking a Runnable in the background is kind of deprecated since the invention of the concurrent package. 自从并发包的发明以来,在后台调用Runnable的概念已被弃用。 The main reason that this was done in earlier days, is that the GUI code needs to be executed in a different thread, to guarantee that it stays responsive, even if the main thread is busy doing some calculations, but actual multi-threading was still in its very early days. 这样做是在较早的日子完成的主要原因是,GUI代码需要在不同的线程中执行,以确保它保持响应,即使主线程忙于执行一些计算,但实际的多线程仍然在其早期。 The resulting invokeLater concept works, but comes with a strong creation overhead. 产生的invokeLater概念可以使用,但是创建开销很大。 This is especially annoying if you frequently have to do minor things, but each time you need to create an entire new Runnable, just to get that event into the Swing thread. 如果您经常需要做些次要的事情,那么这尤其烦人,但是每次您需要创建一个全新的Runnable时,只是为了将该事件放入Swing线程中。

A more modern approach should use a thread-safe list, like a LinkedBlockingQueue. 更现代的方法应该使用线程安全列表,例如LinkedBlockingQueue。 In this case any thread can just throw the event into the queue, and other listener/GUI-Event-handlers can take them out asynchronously, without the need of synchronization or background Runnables. 在这种情况下, 任何线程都可以将事件扔到队列中,而其他侦听器/ GUI事件处理程序可以异步将其取出,而不需要同步或后台Runnable。

Example: 例:

You initialize a new Button that does some heavy calculation once it is pressed. 您初始化一个新的Button,一旦按下它,它将进行大量的计算。

In the GUI thread the following method is called once the button is clicked: 在GUI线程中,单击按钮后将调用以下方法:

void onClick() {
  executor.submit(this.onClickAction);
}

Where executor is an ExecutorService and the onClickAction a Runnable. executor是ExecutorService, onClickAction是Runnable。 As the onClickAction is a Runnable that was submitted once during Button creation, no new memory is accessed here. 由于onClickAction是在Button创建过程中提交一次的Runnable,因此此处没有新的内存可访问。 Let's see what this Runnable actually does: 让我们看看这个Runnable的实际作用:

void run() {
  final MyData data = doSomeHeavyCalculation();
  dispatcher.dispatch(myListeners, data);
}

The dispatcher is internally using the LinkedBlockingQueue as mentioned above (the Executor uses one internally as well btw), where myListeners is a fixed (concurrent) List of listeners and data the Object to dispatch. dispatcher在内部使用如上所述的LinkedBlockingQueue(执行程序在内部也使用btw),其中myListeners是固定的(并发)侦听器列表,并分配要分发的Object数据。 On the LinkedBlockingQueue several threads are waiting using the take() method. 在LinkedBlockingQueue上,几个线程正在使用take()方法。 Now one is woken up as of the new event and does the following: 现在,从新事件中唤醒一个,并执行以下操作:

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

The general idea behind all this, is that for once you utilize all cores for your code, and in addition you keep the amount of objects generated as low as possible (some more optimizations are possible, this is just demo code). 所有这些背后的一般想法是,一旦您将所有内核用于代码,并且您将生成的对象数量保持在尽可能低的水平(可能进行更多优化,这只是演示代码)。 Especially you do not need to instantiate new Runnables from scratch for frequent events, which comes with a certain overhead. 特别是,您不需要为频繁发生的事件而从头实例化新的Runnable,这会带来一定的开销。 The drawback is that the code using this kind of GUI model needs to deal with the fact that multi-threading is happening all the time. 缺点是使用这种GUI模型的代码需要处理一直在发生的多线程这一事实。 This is not difficult using the tools Java gives to you, but it is an entire different way of designing your code in the first place. 使用Java提供给您的工具并不难,但这首先是设计代码的完全不同的方式。

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

相关问题 用Java制作自己的收藏集的正确方法 - Right way to make my own collection in java 什么是使清单在整个Android Java项目中被读取和修改的正确方法? - What is the proper way to make my List be read and modified throughout my entire Android Java project? 在 Gnome 顶部栏中设置我的 Java Swing 应用程序标题的正确方法是什么? - What is the proper way to set my Java Swing application's title in the Gnome top bar? 在Java的UI中显示资金的正确方法是什么? - What's the proper way to show money in an UI in Java? 在 Java 中存储应用程序的 conf 数据的正确方法是什么? - What is the proper way to store app's conf data in Java? 在Java中声明项目常量的正确方法是什么? - What's the proper way of declaring project constants in Java? Java Swing-做基于阶段的GUI的正确方法是什么? - Java Swing - What's the proper way of doing a stage based GUI? 使我自己成为Java的例外 - Make my own exceptions Java 在 Java 语言中以一种或另一种方式初始化 static 变量的正确方法或潜在区别是什么? - What's the proper way or underlying difference of initializing a static variable in one way or another in Java language? 用MapDB创建数据库的正确方法是什么? - What's the proper way to create a DB with MapDB?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM