创建新线程以在所述单独的线程

时间:2015-06-04 02:34:48

标签: java multithreading sockets user-interface updating

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

我尝试在论坛上查找,但有关图形更新的问题经常发生在我身上,而且我似乎从来没有把它弄好 - 在Java中至少有6种更新方法用于图形界面,每一个我试过赢得& #39;工作。

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

我认为答案在于创建一个单独的主题,以便从JLabel收到的不断变化的BufferedImage更新我的ObjectInputStream。有人介意给我这个吗?对我的代码有什么好处? SwingWorkerThreading(wtf是setDaemon(flag)?),RunnableTimerinvokeLater?试过这一切。显然不正确。

EDIT1: 试了你的回答immibis:

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()返回,这意味着输入未正确初始化或连接未同步。我希望它是第一个选择,因为我不知道如何处理最后一个选项。

EDIT2:

尝试了你的回答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。我在这里没有选择吗?

EDIT3: 试过乔丹的代码:

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

GUI类型崩溃了,并且正在绘制&#34;当我将鼠标光标悬停在它上面时的组件。当我启动代码时,它没有崩溃(+1)但它没有绘制任何东西,即使我试图将光标悬停在应该绘制BufferedImage的位置。也许我应该在revalidate()方法中调用覆盖repaint后添加paint(getGraphics())startRunning()

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

3 个答案:

答案 0 :(得分:1)

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

我们需要一些生产者与消费者沟通......

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

您可以在UI代码中创建此实现,然后设置icon

JLabel属性

现在,我们需要一些东西来制作图像...

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

答案 1 :(得分:0)

要更新标签,您需要确保使用EDT线程,因此请使用您收到BufferedImage的代码中的SwingUtilities.invokeLater(理想情况下,它位于单独的“工作”线程中:

SwingUtilities.invokeLater(new Runnable() {

        @Override
        public void run() {
            // Update your label here
        }
    });

答案 2 :(得分:0)

你想要完成的是什么。所有人可能会有更好的方式。 例如,将JLabel替换为JPanel,然后使用JPanel绘制方法

@Override
public void paint(Graphics g) {
    g.drawImage(Img, xcord, ycord, null);
}

然后让JPanel实现runnable并在该run方法中进行更新。

JPanel类

public class GraphicsPanel extends JPanel{

private BufferedImage img;

@Override
public void paint(Graphics g) {
    super.paint(g);

    Graphics2D G2D = (Graphics2D) g;

    G2D.drawImage(img, 0, 0, null);

}

public void setImg(BufferedImage img) {
    this.img = img;
}

}

然后确保从您希望调用其方法的位置可以看到此面板。 这看起来像这样

GraphicsPanel graphicsPanel = new GraphicsPanel();
    boolean running;
    BufferedImage srcImage;

    public void run(){  
        while(running){

            graphicsPanel.setImg(srcImage);
            graphicsPanel.repaint();

            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    }