我有一個Java應用程序,它使用了JFrame
以及TrayIcon
,我在TrayIcon
上添加了PopupMenu
。如何防止TrayIcon彈出窗口占用整個調度程序線程
當我點擊TrayIcon
時彈出菜單出現,但只要PopupMenu
可見,主框架就會凍結。
我的第一個想法是事件調度線程被某人佔用。所以 我寫了一個小的示例應用程序,它使用了一個swing工作器和一個進度條。
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);
}
}
}
如何重現
當您啓動示例應用程序,你會看到一個進度條的框架。 A SwingWorker
模擬進度操作。
SwingWorker
發佈進度值並將其打印到System.out
。
當我點擊托盤圖標('黑色')時,進度條凍結,彈出菜單出現。我可以看到SwingWorker仍在後臺運行,因爲它將進度值輸出到命令行。
調查
所以我看了一下使用jvisualvm
的應用程序,它讓我發現,在事件調度線程是非常繁忙的,只要彈出窗口可見。
我能弄清楚,看看彈出是通過該方法sun.awt.windows.WTrayIconPeer.showPopupMenu(int, int)
可見。
此方法使用EventQueue.invokeLater
向事件分派線程提交可運行子集。在這個可運行中,方法sun.awt.windows.WPopupMenuPeer._show
被調用。這種方法是native
和看來它實現了一個繁忙的等待循環,所以事件調度線程被這個方法完全佔用了。 因此,只要彈出式菜單可見,其他與UI相關的任務就不會執行。
有沒有人知道一個解決方案,保持事件調度線程響應,而顯示TrayIcon
彈出窗口?