Почему invokeAndWait() предпочтительнее для апплетов, а не для автономных приложений?
Документация Java говорит:
В апплете задача создания GUI должна быть запущена из метода init() с помощью invokeAndWait(); в противном случае init() может вернуться до создания графического интерфейса, что может вызвать проблемы при запуске апплета в браузере.
Что не так если init()
возвращает до создания GUI? Какие проблемы могут быть вызваны для браузера?
Документы далее говорят, что:
В любой другой программе планирование задачи создания GUI обычно является последним, что делает исходный поток, поэтому не имеет значения, использует ли он invokeLater() или invokeAndWait().
Каким образом задача создания GUI, будучи последней вещью, что-либо меняет? Кроме того, в нем говорится об обычной практике: если создание GUI не является последней задачей, выполняемой исходным потоком, все равно не имеет значения, invokeAndWait()
используется или invokeLater()
?
Я думаю, что понимаю, что они пытаются сказать, но я все еще хочу быть уверенным, поэтому я отправляю вопрос. Спасибо заранее!!
4 ответа
Что не так если
init()
возвращает до создания GUI? Какие проблемы могут быть вызваны для браузера?
Моя главная задача состоит в том, чтобы start()
Метод вызывается до завершения создания компонента. Если компоненты указаны в методе start, это приведет к NullPointerException
,
.. не может ли то же самое случиться с автономными программами?
Настольные приложения не имеют start()
метод. Этот метод является частью жизненного циклаапплета, а не приложений.
@trashgod Итак, я вставил непустой метод start () и добавил JPanel, который создает вращающийся квадрат (анимированный с помощью Thread, запущенного из start ()). Я все еще нахожу, что init1 () и init2 () ведут себя точно так же. Не забудьте объяснить ОП (и мне), почему вы бы предпочли invokeAndWait () для этого случая.
@ Андрей, я понимаю, что я не прав с ответом выше. Позаботьтесь о том, чтобы объяснить, почему я не прав, и каков правильный ответ. Я пытался следовать одному из ваших ответов, попал по неработающей ссылке.
Вот скомпилируемое приложение (да, обычно я бы поместил его в 3 отдельных класса, но здесь мы пытаемся ответить на конкретный вопрос).
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class P4 extends JApplet implements ActionListener {
private Frame frame; // null/applet, non-null/application
private Painter painter = new Painter();
public P4 () { super(); }
public P4 (Frame f) { this(); frame = f; }
public void init () {
// init1(); // better for application
init2(); // better for applets
}
public void init1 () {
createGUI();
}
public void init2 () {
try {
javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
createGUI();
}
});
} catch (Exception ex) {
ex.printStackTrace();
}}
private void createGUI () {
JPanel p = new JPanel(new BorderLayout());
JButton b = new JButton ("Click to say Hello to the world");
b.addActionListener (this);
p.add (b, "South");
p.add (painter, "Center");
if (frame != null) { // application
b = new JButton ("Exit");
b.addActionListener (this);
p.add (b, "North");
}
getContentPane().add(p);
}
public void actionPerformed (ActionEvent e) {
if ("Exit".equals (e.getActionCommand()))
System.exit (0);
JOptionPane.showMessageDialog (frame, "Hello, world!");
}
public void start () { new Thread (painter).start(); }
public void stop () { }
public void paint (Graphics g) {
super.paint (g);
}
public static void main (String[] args) {
JFrame fr = new JFrame ("Appletication");
P4 p4 = new P4 (fr);
p4.init(); p4.start(); // initialize GUI
fr.add (p4); fr.pack(); fr.setVisible (true);
}
public class Painter extends JPanel implements Runnable {
private int state, px[] = new int[4], py[] = new int[4];
public Painter () {
super();
setPreferredSize (new Dimension (300, 200));
}
public void run () {
for ( ; ; ) {
if (++state == 45)
state = 0;
repaint();
try {
Thread.sleep (25);
} catch (InterruptedException ex) {
}}}
public void paint (Graphics g) {
int w = getWidth(), h = getHeight(),
cx = w/2, cy = h/2, halfD = (cx < cy) ? cx : cy;
halfD -= 10;
Graphics2D g2 = (Graphics2D) g;
g2.setPaint (Color.white); g2.fillRect (0,0,w,h);
for (int i = 0 ; i < 4 ; i++) {
double theta = (i*90 + 2*state) * Math.PI / 180;
px[i] = (int) Math.round (cx + halfD * Math.cos (theta));
py[i] = (int) Math.round (cy - halfD * Math.sin (theta));
}
g2.setPaint (Color.red);
g2.fillPolygon (px, py, 4);
}}}
Я расскажу вам о своем опыте с примером - предположим, у нас есть апплет (расширяющий JApplet) с JMenuBar, с двумя конструкторами (один вызывает super (), а другой вызывает this () и устанавливает Frame из своего аргумента). У нас также есть основной метод (не требуется для апплета) в классе, который
- Intantiates JFrame
- Создает апплет с этим Frame в качестве аргумента (перегруженный конструктор, который вызывает super () и устанавливает переменную класса). Вызывает метод init () апплета.
- Делает Рамку центрированной на экране и видимой (для работы в качестве независимого приложения)
В методе init () мы создаем строку меню в дополнение к другим компонентам графического интерфейса и помещаем JMenuItem как "Выход", если переменная класса для Frame установлена в конструкторе (при выборе она вызывает System.exit()),
Теперь механизм invokeAndWait() (как в учебниках) отлично работает как апплет, потому что пользовательский интерфейс апплета управляется отдельным потоком. Запустите как приложение, когда мы вызовем этот метод System.exit(), мы столкнемся с исключениями, потому что основной поток попытается удержать переменные, пока поток пользовательского интерфейса захочет выйти. Досадно читать эти ошибки JVM, если мы запускаем приложение из cmd/shell. Это может быть одной из причин, почему это не рекомендуется для приложений.
Посмотрите на следующий код - он работает и как апплет, и как приложение. Методы init1() и init2() создают один и тот же пользовательский интерфейс. Обе работы. init2() создает отдельный поток отправки событий, не участвуя ни в каком другом потоке в браузере, и рекомендуется для апплетов. Однако это вызывает случайные исключения при запуске в качестве автономного приложения при нажатии кнопки "Выход". Вот почему init1() рекомендуется для приложения. Во всяком случае, это мое понимание, и я могу ошибаться. Но имеет ли смысл объяснение / обоснование сейчас?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class P4 extends JApplet implements ActionListener {
private Frame frame; // null/applet, non-null/application
public P4 () { super(); }
public P4 (Frame f) { this(); frame = f; }
public void init () {
// init1(); // better for application
init2(); // better for applets
}
public void init1 () {
createGUI();
}
public void init2 () {
try {
javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
createGUI();
}
});
} catch (Exception ex) {
ex.printStackTrace();
}}
private void createGUI () {
JPanel p = new JPanel();
JButton b = new JButton ("Click to say Hello to the world");
b.addActionListener (this);
p.add (b);
if (frame != null) { // application
b = new JButton ("Exit");
b.addActionListener (this);
p.add (b);
p.setPreferredSize (new Dimension (400, 50));
}
getContentPane().add(p);
}
public void actionPerformed (ActionEvent e) {
if ("Exit".equals (e.getActionCommand()))
System.exit (0);
JOptionPane.showMessageDialog (frame, "Hello, world!");
}
public void start () { }
public void stop () { }
public void paint (Graphics g) {
super.paint (g);
}
public static void main (String[] args) {
JFrame fr = new JFrame ("Appletication");
P4 p4 = new P4 (fr);
p4.init(); // initialize GUI
fr.add (p4); fr.pack(); fr.setVisible (true);
}}