【问题标题】:What's proper way to make my own events in java在java中制作我自己的事件的正确方法是什么
【发布时间】:2013-12-21 02:04:28
【问题描述】:

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

感谢您的帮助。

【问题讨论】:

    标签: java multithreading events user-interface


    【解决方案1】:

    这是我不久前写的一篇Event Listener 文章。本文介绍了如何编写自己的事件侦听器。

    如果您希望您的库与任何 GUI 一起工作,您想编写自己的事件侦听器是正确的。

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

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

    【讨论】:

    • 谢谢。所以最重要的两件事是: 1. 我应该继承 EventObject 并将所有参数放在该类中的事件侦听器中。 2. 我应该用 new Thread(runnable).start() 触发事件。
    • @Marko: 1) 你会写很多事件对象类。每种不同类型的事件都需要自己的事件对象。 2) 我在一个单独的线程中触发了事件,因为我不想阻止 GUI。您可能需要也可能不需要在单独的线程中触发事件。如果您在单独的线程中触发事件,您可能必须提供方法,例如 Swing 稍后的调用,以使 GUI 事件在 Event Dispatch 线程上触发。
    • 但是如果有人编写 GUI 在他的 handleEvent 方法中写了这样的东西:“JOptionPane.showMessageDialog(this,"Hi!");”。我的意思是,据我所知,这应该在 EDT 中。对此有什么想法吗?
    • @Marko:我们可以推测整个星期。构建你的库,看看你的朋友是否可以使用你的 API 来构建他的 GUI。
    • 好的,我的问题有点晚了。所以我最后一个问题的答案是否定的。如果我用“new Thread(runnable).start()”触发事件并且有人写“JOptionPane.showMessageDialog(this,"Hi!");”在他的 handleEvent 方法中,它不起作用。比我必须决定我应该使用 java.awt.EventQueue.invokeLater 触发事件(这可能仅限于 Swing 和 AWT - 或者是吗?)或者让我的 API 更难使用的用户。对此有什么想法吗?
    【解决方案2】:

    如果您希望它完全不知道所使用的 GUI,唯一真正的解决方案是让接收器在 dataArrived 中处理它。由于每个工具包都有自己的实现,因此要使其与任何工具包一起使用,您真正要做的就是忽略它。否则,您实际上会得到一份“支持的工具包”列表和每个工具包的案例。

    如果您只想在 someMethodThatWaitsForData 之外执行 dataArrived,那么您可以创建自己的调度线程或每次创建一个新线程。

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        自并发包的发明以来,在后台调用 Runnable 的概念已被弃用。早期这样做的主要原因是 GUI 代码需要在不同的线程中执行,以保证它保持响应,即使主线程忙于做一些计算,但实际的多线程仍然是在早期。生成的 invokeLater 概念有效,但会带来很大的创建开销。如果您经常需要做一些小事,但每次都需要创建一个全新的 Runnable,只是为了让该事件进入 Swing 线程,这尤其令人讨厌。

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

        例子:

        您初始化一个新按钮,一旦按下它就会执行一些繁重的计算。

        在 GUI 线程中,单击按钮后会调用以下方法:

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

        executor 是 ExecutorService,onClickAction 是 Runnable。由于 onClickAction 是一个 Runnable,在 Button 创建期间提交了一次,因此这里不会访问新的内存。让我们看看这个 Runnable 究竟做了什么:

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

        dispatcher 在内部使用上面提到的 LinkedBlockingQueue(顺便说一句,Executor 在内部也使用一个),其中 myListeners 是要调度的对象的固定(并发)侦听器和数据列表。在 LinkedBlockingQueue 上,有几个线程正在使用 take() 方法等待。现在一个人在新事件时被唤醒并执行以下操作:

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

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

        【讨论】:

        • 有用的建议,但不能真正回答我的问题。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-01-26
        • 2019-08-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-09
        • 1970-01-01
        相关资源
        最近更新 更多