如何防止 TrayIcon 弹窗占用整个调度器线程 [英] How to prevent the TrayIcon popup to occupy the whole dispatcher thread
问题描述
我有一个使用 JFrame
和 TrayIcon
的 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
作为解决方法,但您不能直接添加 JPopupMenu
到 TrayIcon
.
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屋!