【问题标题】:Unable to update GUI while sending file发送文件时无法更新 GUI
【发布时间】:2014-01-11 17:42:22
【问题描述】:

我正在使用套接字通过网络发送文件。文件被正确接收,没有任何问题。但现在我使用JProgressBar 来显示已发送文件的百分比。我的问题是,即使我在单独的线程中更新 GUI,进度条也只有在文件完全发送时才会更新。我也尝试将主线程的优先级调整为Thread.MIN_PRIORITY,但问题仍然存在。

完整的代码很长,所以我不发布它(如果有人问我会发布)。发送文件和更新进度条的短代码是

final double temp=(done/fileSize)*100; // percentage of file sent
SwingUtilities.invokeLater(new Runnable()
{
    public void run()
    {
        try
        {
            jpb.setString("Completed : "+temp+" %");
            jpb.setValue((int)temp); // this is executed only when 100%
        }      
        catch(Exception e) 
        {
            e.printStackTrace();
        }
    }
});
System.out.println(temp); // only this and
bos.write(b,0,read);      // this statement is executed

【问题讨论】:

  • 不要阻塞 EDT(事件调度线程)——当这种情况发生时,GUI 将“冻结”。而不是调用Thread.sleep(n) 实现一个Swing Timer 用于重复任务或一个SwingWorker 用于长时间运行的任务。有关详细信息,请参阅Concurrency in Swing
  • 你读过Oracle官方教程-如何使用ProgressBar
  • ProgressBar 如果您的意思是JProgressBar,请输入。如果您不这样做,请链接到它。
  • 在你的代码中,在你浏览完链接的文章之后。
  • SwingWorker 旨在在后台线程中执行耗时的任务(数据库调用、下载/上传文件、大量处理内容)并在Event Dispatch Thread 中更新 Swing 组件(例如 JProgressBar)。所以在这种情况下是要走的路。查看示例 here

标签: java multithreading swing event-dispatch-thread jprogressbar


【解决方案1】:

问题出在下面一行:

final double temp=(done/fileSize)*100; // percentage of file sent

如果 donefileSize 都不是 double 那么 done/fileSize 的结果是 0.0。

使它们加倍(至少其中一个)以保留除法的小数部分。

【讨论】:

  • 两者都是双倍的,我正在 CLI 中打印 temp 的值以进行调试。它显示正确的值
  • 会是饥饿问题吗?由于另一个 GUI 操作使 swing 事件调度程序线程保持忙碌。
【解决方案2】:

这是我谈到的一个实现。

这不是最好的设计,因为我做得又快又脏,但这样文件传输代码不依赖于 UI。

public class FileTransfer implements Runnable
{
    double transferPercent = 0.0;

    double getTransferPercent(){ return transferPercent; }

    @Override
    public void run()
    {
        while(transferingFile)
        {
            // Write data
            // Update transferPercent
        }
    }
}

public class UIClass extends TimerTask
{
    private FileTransfer fileTransfer;

    public void createUI()
    {
        TimerTask myClass = this;
        JButton b = new JButton("Transfer");
        b.addMouseListener(new MouseAdapter()
        {
            @Override
            public void mouseClicked(MouseEvent e)
            {
                fileTransfer.start();
                Timer t = new Timer();
                t.scheduleAtFixedRate(myClass, 0.0, 20);
            }
        });
    }

    // Update the UI here!
    @Override
    public void run()
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                jpb.setValue(fileTransfer.getTransferPercent());
            }
        });
    }
}

【讨论】:

    【解决方案3】:

    我可能会以不同的方式设计它。我会让网络代码独立于 UI 并让它更新一个发送百分比的变量。

    然后,用户界面将使用计时器轮询此数字以更新进度条。

    但是您的代码应该可以工作。尝试在你的运行函数上添加一个@Override。也许 SwingUltities.invokeLater 正在调用 Runnable 的 run 函数而不是你自己的。

    【讨论】:

    • 不是这个问题被称为事件调度线程,Swing 中的并发,结果是 SwingWorker 或 Runnable#Thread,输出到 Swing GUI 包装到 invokeLater
    • 详细说明...你会如何设计这个?
    • 我的设计大概是这样的。
    • “也许 SwingUltities.invokeLater 正在调用 Runnable 的 run 函数而不是你自己的。” Java 没有函数。类有方法。 Runnable 是一个接口,因此它没有实现 run 方法。
    • 函数和方法是一样的。方法是成员函数。
    猜你喜欢
    • 2018-08-07
    • 1970-01-01
    • 1970-01-01
    • 2016-07-01
    • 1970-01-01
    • 2010-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多