Нужно ли запускать роботы в очереди событий?
Робот является частью библиотеки AWT, но, похоже, он отличается от большинства остальных библиотек. Я создаю Swing GUI, который смешивает Swing с Java Native Access (JNA) и Robot, чтобы позволить Java управлять некоторыми рабочими программами MS Windows/Citrix. Я чувствую, что, поскольку робот будет размещать в очереди события в "начальной входной очереди платформы", последнее, что я хочу сделать, это запустить его на EDT, но, с другой стороны, большинство классов в AWT и Swing-библиотеках должен выполняться в потоке событий Swing. Поэтому, чтобы попытаться прояснить это в моем сознании, позвольте мне задать как можно более конкретный вопрос:
Должны ли методы робота (в частности, нажатия и отпускания клавиш, перемещения мыши, нажатия и отпускания мыши) запускаться или выходить из потока отправки событий Swing (EDT)?
Ответы
Ответ 1
Методы Robot
, о которых вы упомянули, не должны запускаться в EDT. Взглянув на исходный код, выяснилось, что каждый из этих "событийных" методов имеет одну общую черту (вызов afterEvent
):
public synchronized void keyPress(int keycode) {
checkKeycodeArgument(keycode);
peer.keyPress(keycode);
afterEvent();
}
public synchronized void mousePress(int buttons) {
checkButtonsArgument(buttons);
peer.mousePress(buttons);
afterEvent();
}
// etc
private void afterEvent() {
autoWaitForIdle();
autoDelay();
}
private void autoWaitForIdle() {
if (isAutoWaitForIdle) {
waitForIdle();
}
}
public synchronized void waitForIdle() {
checkNotDispatchThread();
/* snip */
}
private void checkNotDispatchThread() {
if (EventQueue.isDispatchThread()) {
throw new IllegalThreadStateException("Cannot call method from the event dispatcher thread");
}
}
Если вы вызовете любой из этих методов на EDT, а Robot.isAutoWaitForIdle
true
, будет выбрано исключение. Это означает, что даже если isAutoWaitForIdle
false
, эти методы не следует вызывать из EDT.
Ответ 2
- API совершенно точно говорит, тогда я понимаю, что роботу следует игнорировать, если он вызван с EDT или нет.
Использование класса для генерации входных событий отличается от отправки событий в очередь событий AWT или AWT-компонентов тем, что события генерируются в собственной входной очереди платформы.
- Я отношусь к новым в Java, первым касался Java1.6.009, тогда я не могу сравнивать изменения для AWT и (когда родился) Swing в Java1.3 и отдыхать в Java1.4.
мой пример
import javax.imageio.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
public class CaptureScreen implements ActionListener {
private JFrame f = new JFrame("Screen Capture");
private JPanel pane = new JPanel();
private JButton capture = new JButton("Capture");
private JDialog d = new JDialog();
private JScrollPane scrollPane = new JScrollPane();
private JLabel l = new JLabel();
private Point location;
public CaptureScreen() {
capture.setActionCommand("CaptureScreen");
capture.setFocusPainted(false);
capture.addActionListener(this);
capture.setPreferredSize(new Dimension(300, 50));
pane.add(capture);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(pane);
f.setLocation(100, 100);
f.pack();
f.setVisible(true);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createPicContainer();
}
});
}
private void createPicContainer() {
l.setPreferredSize(new Dimension(700, 500));
scrollPane = new JScrollPane(l,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.setBackground(Color.white);
scrollPane.getViewport().setBackground(Color.white);
d.setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
d.add(scrollPane);
d.pack();
d.setVisible(false);
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("CaptureScreen")) {
Dimension d1 = Toolkit.getDefaultToolkit().getScreenSize(); // gets the screen size
Robot r;
BufferedImage bI;
try {
r = new Robot(); // creates robot not sure exactly how it works
Thread.sleep(1000); // waits 1 second before capture
bI = r.createScreenCapture(new Rectangle(d1)); // tells robot to capture the screen
showPic(bI);
saveImage(bI);
} catch (AWTException e1) {
e1.printStackTrace();
} catch (InterruptedException e2) {
e2.printStackTrace();
}
}
}
private void saveImage(BufferedImage bI) {
try {
ImageIO.write(bI, "JPG", new File("screenShot.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
}
private void showPic(BufferedImage bI) {
ImageIcon pic = new ImageIcon(bI);
l.setIcon(pic);
l.revalidate();
l.repaint();
d.setVisible(false);
location = f.getLocationOnScreen();
int x = location.x;
int y = location.y;
d.setLocation(x, y + f.getHeight());
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
d.setVisible(true);
}
});
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
CaptureScreen cs = new CaptureScreen();
}
});
}
}
Ответ 3
Усиляясь на продуманном ответе @mKorbel и подтверждая его эмпирический результат, обратите внимание, как различные методы Robot
делегируют внутренний экземпляр RobotPeer
, встроенная реализация которого зависит от платформы. Кроме того, методы синхронизированы. Синтетические события все поступают на EventQueue
, независимо от источника.