java - 创建新线程以更新所述单独线程中的 JLabel 和 setIcon()

标签 java multithreading sockets user-interface updating

尝试从另一个通过套接字发送字节数组的应用程序获取图像,并将其转换为 BufferedImage并设置 JLabel GUI 中每 3 秒更新一次。

我尝试在论坛上查找它,但是关于图形更新的问题对我来说经常出现,而且我似乎从来没有弄对过——java中至少有6种用于图形界面的更新方法,而我尝试过的每一种都不起作用。

我知道问题不在于与客户端的连接,因为我可以使用 ImageIO.write() 轻松保存收到的图像。它每 3 秒更新一次我期望收到的图像。我就是无法让 Java 更新 JLabel正确地进行,而不必去论坛并询问人们。我想这是一项复杂的任务。这是代码:http://pastebin.com/95nMGLvZ 。我正在 Netbeans 中做这个项目,所以里面有很多东西没有必要阅读,因为它与问题没有直接关系。

我认为答案在于创建一个单独的线程来更新我的 JLabel来自千变万化BufferedImage这是从 ObjectInputStream 收到的。有人介意帮我做这件事吗?什么对我的代码更好? SwingWorker , Threading (wtf 是 setDaemon(flag) 吗?),Runnable , Timer , invokeLater ?尝试了这一切。显然不正确。

编辑1: 尝试了你的答案:

public void startRunning() { try { server = new ServerSocket(666, 10); connection = server.accept(); networkStatus("Connected to " + connection.getInetAddress().getHostName()); Thread thr = new Thread(new Runnable() { @Override public void run() { try { input = new ObjectInputStream(connection.getInputStream()); } catch (IOException ex) { JOptionPane.showMessageDialog(null, ex.toString()); } } }); thr.start(); System.out.println(!connection.isInputShutdown()); while (connection.isConnected()) { try { byte[] byteImage = (byte[]) input.readObject(); InputStream in = new ByteArrayInputStream(byteImage); final BufferedImage bi = ImageIO.read(in); jLabel_screen.setIcon(new ImageIcon(bi)); ImageIO.write(bi, "jpg", new File("C:\\Users\\User\\Desktop\\test.jpg")); System.out.println("i'm working"); } catch (IOException ex) { JOptionPane.showMessageDialog(null, ex.toString()); } catch (ClassNotFoundException ex) { Logger.getLogger(SpyxServer.class.getName()).log(Level.SEVERE, null, ex); } } } catch (IOException ex) { JOptionPane.showMessageDialog(null, ex.toString()); } }

它不起作用。它说byte[] byteImage = (byte[]) input.readObject();线路有 NullPointerException 。唯一可以为 null 的值是 readObject() 的返回值,这意味着输入未正确初始化或连接未同步。我希望这是第一个选择,因为我不知道如何处理最后一个。

编辑2:

尝试了你的答案 blazetopher:

public void startRunning() throws IOException { server = new ServerSocket(666, 10); try { connection = server.accept(); networkStatus("Connected to " + connection.getInetAddress().getHostName()); input = new ObjectInputStream(connection.getInputStream()); while (true) { try { byte[] byteImage = (byte[]) input.readObject(); InputStream in = new ByteArrayInputStream(byteImage); final BufferedImage bi = ImageIO.read(in); SwingUtilities.invokeLater(new Runnable() {//<----------- @Override public void run() { jLabel_screen.setIcon(new ImageIcon(bi)); } }); ImageIO.write(bi, "jpg", new File("C:\\Users\\User\\Desktop\\test.jpg")); System.out.println("i'm working"); } catch (IOException ex) { JOptionPane.showMessageDialog(null, ex.toString()); } catch (ClassNotFoundException ex) { Logger.getLogger(SpyxServer.class.getName()).log(Level.SEVERE, null, ex); } } } catch (EOFException eofException) { networkStatus("Connection Closed. :("); } finally { input.close(); connection.close(); } }

使用SwingUtilities.invokeLater也没用。至少程序可以运行,甚至可以保存图像,但仍然无法更新 JLabel。我已经没有选择了吗?

编辑3: 尝试了乔丹的代码:

@Override public void paint(Graphics g) { g.drawImage(biGlobal, 0, 0, null); }

当我将鼠标光标悬停在 GUI 上时,GUI 崩溃了并且正在“绘制”组件。当我启动代码时,它没有崩溃(+1),但它没有绘制任何内容,即使我尝试将光标悬停在应该绘制 BufferedImage 的位置也是如此。也许我应该添加 revalidate()repaint调用覆盖后 paint(getGraphics())里面startRunning()方法?

编辑4:while(true)代码实际上可能是问题所在,但是当我使用 SwingTimer 时,它与客户端不同步,并在第一个周期后崩溃。有什么替代方案吗?

最佳答案

一般来说,您有生产者/消费者模式。有些东西正在生成图像,有些东西想要消耗图像。通常,消费者会等待生产者告诉它已经生产了一些东西,但在这种情况下,我们可以使用观察者模式,让生产者通知消费者一些东西已经生产了(而不是等待它)

我们需要某种方式让生产者与消费者进行通信......

public interface PictureConsumer {
    public void newPicture(BufferedImage img);
}

您将在 UI 代码中创建此实现,然后设置 JLabelicon 属性

现在,我们需要一些东西来生成图像......

public class PictureProducer extends SwingWorker<Object, BufferedImage> {

    private PictureConsumer consumer;

    public PictureProducer(PictureConsumer consumer) {
        this.consumer = consumer;
    }

    @Override
    protected void process(List<BufferedImage> chunks) {
        // Really only interested in the last image
        BufferedImage img = chunks.get(chunks.size() - 1);
        consumer.newPicture(img);
    }

    @Override
    protected Object doInBackground() throws Exception {
        /*
         This whole setup worries me. Why is this program acting as the 
         server?  Why aren't we pooling the image producer?         
         */
        try (ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(666, 10)) {
            try (Socket socket = server.accept()) {
                try (ObjectInputStream ois = new ObjectInputStream(socket.getInputStream())) {
                    // Using `while (true)` is a bad idea, relying on the fact
                    // that an exception would be thrown when the connection is closed is
                    // a bad idea.
                    while (!socket.isClosed()) {
                        // Generally, I'd discourage using an ObjectInputStream, this 
                        // is just me, but you could just as easily read directly from
                        // the ByteArrayInputStream...assuming the sender was sending
                        // the data as a byte stream ;)
                        byte[] bytes = (byte[]) ois.readObject();
                        try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes)) {
                            BufferedImage img = ImageIO.read(bis);
                            publish(img);
                        }
                    }
                }
            }
        }
        return null;
    }

    @Override
    protected void done() {
        try {
            get();
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, "Image Producer has failed: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
        }
    }

}

参见Worker Threads and SwingWorker了解更多详情

您可以举一个相反的例子(其中一些服务器正在生成图像,而客户端正在使用它们)here

关于java - 创建新线程以更新所述单独线程中的 JLabel 和 setIcon(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30634011/

相关文章:

java - 为什么Socket Server关闭后,TCP接收方还能接收到数据?

python - 如何不断尝试连接套接字直到它出现?

android - 无法连接到UNIX套接字,Android

奇怪的Java应用程序绘图

java - 获取同一元素的运行时属性

java - 线程卡住了,因为在运行中有一个永无止境的功能

java - 在 Java 中释放信号量对象的正确方法是什么?

java - Amazon SQS 旧配置文件格式警告

java - Android 从 AsyncTask 中调用不同类的方法

java - 这两个示例是否等效( volatile +同步增量与同步获取+增量)?