如何防止 TrayIcon 弹窗占用整个调度器线程 [英] How to prevent the TrayIcon popup to occupy the whole dispatcher thread

查看:33
本文介绍了如何防止 TrayIcon 弹窗占用整个调度器线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用 JFrameTrayIcon 的 Java 应用程序,我向 TrayIcon<添加了一个 PopupMenu/代码>.

I have a java application that uses a JFrame as well as a TrayIcon and I added a PopupMenu to the TrayIcon.

当我点击 TrayIcon 时,弹出菜单会出现,但只要 PopupMenu 可见,主框架就会冻结.

When I click on the TrayIcon the popup menu shows up, but the main frame freezes as long as the PopupMenu is visible.

我的第一个想法是事件调度线程被某人占用了.所以我编写了一个使用 Swing Worker 和进度条的小示例应用程序.

My first thought was that the event dispatch thread is occupied by someone. So I wrote a small example application that uses a swing worker and a progress bar.

public class TrayIconTest {

  public static void main(String[] args) throws AWTException {
    JFrame frame = new JFrame("TrayIconTest");
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

    Container contentPane = frame.getContentPane();
    JProgressBar jProgressBar = new JProgressBar();
    BoundedRangeModel boundedRangeModel = jProgressBar.getModel();
    contentPane.add(jProgressBar);

    boundedRangeModel.setMinimum(0);
    boundedRangeModel.setMaximum(100);

    PopupMenu popup = new PopupMenu();
    TrayIcon trayIcon =
            new TrayIcon(new BufferedImage(64, 64, BufferedImage.TYPE_INT_RGB), "TEST");


    // Create a popup menu components
    MenuItem aboutItem = new MenuItem("About");
    popup.add(aboutItem);
    trayIcon.setPopupMenu(popup);
    SystemTray tray = SystemTray.getSystemTray();
    tray.add(trayIcon);

    IndeterminateSwingWorker indeterminateSwingWorker = new IndeterminateSwingWorker(boundedRangeModel);
    indeterminateSwingWorker.execute();


    frame.setSize(640, 80);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);

  }
}

class IndeterminateSwingWorker extends SwingWorker<Void, Integer> {

  private BoundedRangeModel boundedRangeModel;

  public IndeterminateSwingWorker(BoundedRangeModel boundedRangeModel) {
    this.boundedRangeModel = boundedRangeModel;
  }

  @Override
  protected Void doInBackground() throws Exception {
    int i = 0;
    long start = System.currentTimeMillis();
    long runtime = TimeUnit.MILLISECONDS.convert(5, TimeUnit.MINUTES);
    while (true) {
      i++;
      i %= boundedRangeModel.getMaximum();

      publish(i);
      Thread.sleep(20);

      long now = System.currentTimeMillis();
      if ((now - start) > runtime) {
        break;
      }

      System.out.println("i = " + i);
    }
    return null;
  }

  @Override
  protected void process(List<Integer> chunks) {
    for (Integer chunk : chunks) {
      boundedRangeModel.setValue(chunk);
    }
  }
}

如何复制

当您启动示例应用程序时,您将看到一个带有进度条的框架.SwingWorker 模拟一个进度动作.

When you start the example application you will see a frame with a progress bar. A SwingWorker simulates a progress action.

SwingWorker 发布进度值并将其打印到 System.out.

The SwingWorker publishes the progress value and also prints it to System.out.

当我点击托盘图标(黑色的")时,进度条会冻结并显示弹出菜单.可以看到 SwingWorker 还在后台运行,因为它将进度值输出到命令行.

When I click on the tray icon ('the black one') the progress bar freezes and the popup menu shows up. I can see that the SwingWorker is still running in the background, because it outputs the progress value to the command line.

调查

所以我使用 jvisualvm 查看了该应用程序,它告诉我只要弹出窗口可见,事件调度程序线程就非常忙碌.

So I took a look at the application using jvisualvm and it shows me that the event dispatcher thread is very busy as long as the popup is visible.

我发现通过 sun.awt.windows.WTrayIconPeer.showPopupMenu(int, int) 方法可以查看弹出窗口.

I could figure out that look at the popup is made visible by the method sun.awt.windows.WTrayIconPeer.showPopupMenu(int, int).

此方法使用 EventQueue.invokeLater 将可运行文件提交给事件调度线程.在这个 runnable 中,方法 sun.awt.windows.WPopupMenuPeer._show 被调用.这个方法是native 并且它似乎实现了一个繁忙的等待循环,使得事件调度线程完全被这个方法占用.因此,只要弹出菜单可见,就不会执行其他 ui 相关任务.

This method submits a runnable using EventQueue.invokeLater to the event dispatch thread. In this runnable the method sun.awt.windows.WPopupMenuPeer._show is invoked. This method is native and it seems that it implements a busy wait loop so that the event dispatch thread is completely occupied by this method. Thus other ui related tasks are not executed as long as the popup menu is visible.

有没有人知道在显示 TrayIcon 弹出窗口时保持事件调度线程响应的解决方法?

Does anyone know a workaround that keeps the event dispatch thread responsive while a TrayIcon popup is shown?

推荐答案

我现在使用 JPopupMenu 作为解决方法,但您不能直接添加 JPopupMenuTrayIcon.

I'm now using a JPopupMenu as workaround, but you can't add the JPopupMenu directly to the TrayIcon.

诀窍是编写一个 MouseListener

public class JPopupMenuMouseAdapter extends MouseAdapter {

  private JPopupMenu popupMenu;

  public JPopupMenuMouseAdapter(JPopupMenu popupMenu) {
    this.popupMenu = popupMenu;
  }

  @Override
  public void mouseReleased(MouseEvent e) {
    maybeShowPopup(e);
  }

  @Override
  public void mousePressed(MouseEvent e) {
    maybeShowPopup(e);
  }

  private void maybeShowPopup(MouseEvent e) {
    if (e.isPopupTrigger()) {
      Dimension size = popupMenu.getPreferredSize();
      popupMenu.setLocation(e.getX() - size.width, e.getY() - size.height);
      popupMenu.setInvoker(popupMenu);
      popupMenu.setVisible(true);
    }
  }

并将其连接到 TrayIcon

TrayIcon trayIcon = ...;
JPopupMenu popupMenu = ...;

JPopupMenuMouseAdapter jPopupMenuMouseAdapter = new JPopupMenuMouseAdapter(popupMenu);
trayIcon.addMouseListener(jPopupMenuMouseAdapter);   

这篇关于如何防止 TrayIcon 弹窗占用整个调度器线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆