سؤال

The java docs says:

In an applet, the GUI-creation task must be launched from the init() method using invokeAndWait(); otherwise, init() may return before the GUI is created, which may cause problems for a web browser launching an applet.

What is wrong if init() returns before GUI is created? What kind of problems could be caused for the browser?

The docs further says that:

In any other kind of program, scheduling the GUI-creation task is usually the last thing the initial thread does, so it doesn't matter whether it uses invokeLater() or invokeAndWait().

How does GUI-creation task being the last thing done changes anything? Also, it says about the usual practice, if GUI-creation is not the last task being done by the initial thread, would still it not matter if invokeAndWait() is used or invokeLater()?

I think i understand what they are trying to say but I still want to be sure, hence, me posting the question. Thanx in advance!!

هل كانت مفيدة؟

المحلول

What is wrong if init() returns before GUI is created? What kind of problems could be caused for the browser?

My primary concern would be that the start() method is called prior to the component creation being concluded. If components are referenced in the start method, this would lead to a NullPointerException.

..couldn't the same thing happen with stand-alone programs?

Desktop applications don't have a start() method. The method is part of the applet life-cycle, but not that of applications.

نصائح أخرى

I'll tell you my experience with an example - Suppose we have an applet (extending JApplet) with a JMenuBar, with 2 constructors (one calls super() and the other calls this() and sets up a Frame from its argument). We also have a main method (not required for the applet) in the class which

  1. Intantiates a JFrame
  2. Constructs the applet with this Frame as an argument (overloaded constructor that calls super() and sets a class variable) Calls init() method of the applet
  3. Makes the Frame centered on screen and visible (to work as an independent application)

In the init() method, we create the menubar in addition to the other GUI components and put in a JMenuItem as "Exit" if the class variable for the Frame is set in the constructor (it calls System.exit() when selected).

Now, the invokeAndWait() mechanism (as in the tutorials) works fine as an applet, because the applet UI is controlled by a separate thread. Run as an application, when we call that System.exit() method, we will run into Exceptions, because the main thread will try to hang on to the variables while the UI thread wants to exit. It is annoying to read those JVM errors if we start the application from a cmd/shell. This may be 1 reason why it is not recommended for applications.

Take a look at the following code - it works both as an applet and an application. The methods init1() and init2() create the same UI. Both works. init2() creates a separate event dispatch thread, without racing with any other thread in a browser, and is recommended for applets. However, this causes occasional Exceptions when run as stand-alone application when the "Exit" button is clicked. This is why init1() is recommended for an application. This is my understanding, anyway, and I could be wrong. But does the explanation/rationale make sense now?

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);
}}

@trashgod So I have put in a non-empty start() method, and added a JPanel which makes a rotating square (animated with a Thread started from start()). I still find that init1() and init2() behaves exactly the same. Care to explain to the OP (and myself) why you'd prefer invokeAndWait() for this case.

@Andrew I understand I am not right with the answer above. Care to elaborate why I am not right, and what the right answer is. I tried to follow one of your answers, got to a broken link.

Here is the compilable application (yes, normally I'd put it into 3 separate classes, but here we are trying to answer a specific issue).

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);
}}}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top