Java 8u161/8u162 позволяет использовать приложение Swing для использования процессора
При запуске приложения Swing на 8u161 или 8u162 и фокусе находится в JTextField, и вы переключаетесь на другое приложение (например, Chrome) и обратно в приложение. Использование процессора увеличивается до 15% на моем 8-ядерном ПК с Windows 10 (как если бы весь ядро занятые обработки событий).
Просто запустите приложение и переключите его пару раз. Если я нажму на вкладку на панели с вкладками, загрузка процессора упадет до 0, как ожидалось.
public class Test {
public static void main(String... args) {
SwingUtilities.invokeLater(() -> {
JFrame f = new JFrame("Test");
JTabbedPane tp = new JTabbedPane();
tp.addTab("tab 1", new JTextField(20));
f.add(tp);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}
Я попытался посмотреть очередь событий, чтобы увидеть, что происходит, и это очень похоже на то, что последнее событие обрабатывается снова и снова
Если я добавлю это в вышеуказанную программу, я получаю много java.awt.event.InvocationEvent [INVOCATION_DEFAULT, runnable = sun.awt.windows.WInputMethod...
Toolkit.getDefaultToolkit().getSystemEventQueue().push(new EventQueue(){
@Override protected void dispatchEvent(AWTEvent event) {
System.out.println(event);
super.dispatchEvent(event);
}
});
Работает нормально на 8u151, 8u152 и 9.0.4
У меня много клиентов, которые обновляют до 161 и получают эту проблему, поэтому любые предложения об обходном пути очень ценятся. Я подал ошибку с Oracle
JProfiler показывает это:
Кажется, работает нормально на 8u172 b02
Согласно openjdk это было введено 8184016 и зафиксировано 8183504
Ответы
Ответ 1
Используют ли ваши клиенты методы ввода? Если вам не нужны методы ввода, я предлагаю вам отключить его.
public class Test {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame f = new JFrame("Test");
JTabbedPane tp = new JTabbedPane();
JTextField tf = new JTextField();
tf.enableInputMethods(false); // disable IM
tp.addTab("tab 1", tf);
f.add(tp);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}
}
Ответ 2
Это, вероятно, довольно уродливо, но это обходной путь, который я создал на основе моих наблюдений, когда возникает ошибка. Я уверен, что можно создать лучшее обходное решение, чтобы лучше понять, почему именно ошибка возникает в первую очередь.
Это существенно предотвращает JTextComponent
как первое, что после фокусировки окна, перенос фокуса на другой компонент (upFocusCycle(component)
для меня помещается в рамку/диалог), а затем передает его обратно в JTextComponent
. Я не знаю, будет ли это работать в каждом случае или даже что-то сломать, но, похоже, это работает для меня. Разумеется, используйте на свой страх и риск.
public class JTextFieldTest {
public static final void main(String[] args) {
SwingUtilities.invokeLater(() -> {
// Install workaround, without this the bug occurs
installTextComponentFocusWorkaround();
JFrame window = new JFrame("Test");
window.setLocationByPlatform(true);
window.add(new JButton("Button"), BorderLayout.CENTER);
window.add(new JTextField(), BorderLayout.SOUTH);
window.pack();
window.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
window.setVisible(true);
});
}
public static void installTextComponentFocusWorkaround() {
KeyboardFocusManager.getCurrentKeyboardFocusManager().addVetoableChangeListener(new VetoableChangeListener() {
private boolean rejectNext = false;
private JComponent target;
@Override
public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
if (evt.getNewValue() != null) {
if (evt.getPropertyName().equals("focusOwner")) {
if (evt.getNewValue() instanceof JTextComponent) {
if (rejectNext) {
JComponent component = (JComponent) evt.getNewValue();
KeyboardFocusManager.getCurrentKeyboardFocusManager().upFocusCycle(component);
target = component;
System.out.println("Rejected JTextComponent focus");
throw new PropertyVetoException("Rejected JTextComponent focus", evt);
}
} else {
rejectNext = false;
if (target != null) {
System.out.println("Temp focus: " + evt.getNewValue());
target.requestFocus();
target = null;
}
}
} else if (evt.getPropertyName().equals("focusedWindow")) {
System.out.println("Window focused");
rejectNext = true;
}
}
}
});
}
}
Я также пробовал такие вещи, как очистка фокуса вообще или только бросание PropertyVetoException
, но только на самом деле фокусировался на других, прежде чем JTextComponent
похоже, работал.
Я ищу JTextComponent
потому что ошибка произошла для меня как с JTextField
и с JTextArea
, хотя я не уверен, что затронуты и другие классы.