Thread-safe Splash Screen

christ1an

Grünschnabel
Hallo,

nach ausgiebigem Studium der Einträge in diesem Board und den vielen Lösungen die erarbeitet wurden, traue ich mich nun auch mal eine Frage zu stellen.

Es geht um Folgendes:

Ich möchte meiner (Java3D-)Applikation einen Splashscreen vorschalten, dessen Thread vor dem Laden der Hauptapplikation erzeugt wird und ein Bild anzeigt, während im Hintergrund die Oberfläche der Applikation (SWING-basiert) initialisiert wird.

Die beiden Threads werden auch wie gewünscht nacheinander gestartet, der für den Splash-Screen benötigte Frame wird auch in der richtigen Größe (entsprechend den Abmessungen des Bildes) dargestellt. Leider bleibt der Frame jedoch blank, das Bild an sich wird nicht angezeigt.

Das ändert sich, sobald die Hauptapplikation fertig geladen ist - plötzlich ist auch im Splash-Screen das Bild zu sehen.

Zuerst vermutete ich das Problem an der Implementierung des Splash-Screens und versuchte einige andere Möglichkeiten, wie beispielsweise aus dem Forum (http://www.tutorials.de/forum/java/146899-splash-screen.html) oder extra als "threadsafe"-tituliert: http://www.javaworld.com/javaworld/javatips/jw-javatip104.html :

Code:
class SplashWindow3 extends JWindow
{
    public SplashWindow3(String filename, Frame f, int waitTime)
    {
        super(f);
        JLabel l = new JLabel(new ImageIcon(filename));
        getContentPane().add(l, BorderLayout.CENTER);
        pack();
        Dimension screenSize =
          Toolkit.getDefaultToolkit().getScreenSize();
        Dimension labelSize = l.getPreferredSize();
        setLocation(screenSize.width/2 - (labelSize.width/2),
                    screenSize.height/2 - (labelSize.height/2));
        addMouseListener(new MouseAdapter()
            {
                public void mousePressed(MouseEvent e)
                {
                    setVisible(false);
                    dispose();
                }
            });
        final int pause = waitTime;
        final Runnable closerRunner = new Runnable()
            {
                public void run()
                {
                    setVisible(false);
                    dispose();
                }
            };
        Runnable waitRunner = new Runnable()
            {
                public void run()
                {
                    try
                        {
                            Thread.sleep(pause);
                            SwingUtilities.invokeAndWait(closerRunner);
                        }
                    catch(Exception e)
                        {
                            e.printStackTrace();
                            // can catch InvocationTargetException
                            // can catch InterruptedException
                        }
                }
            };
        setVisible(true);
        Thread splashThread = new Thread(waitRunner, "SplashThread");
        splashThread.start();
    }
}

Und zu guter letzt noch eine Methode, die gänzlich auf Swing verzichtet und nur AWT benutzt - jedoch ebenfalls ohne Erfolg. Jeweils wird das bild erst angezeigt, wenn die Hauptapplikation fertig geladen ist - und der SplashScreen im Grunde ja seiner Natur nach bereits verschwinden sollte.

Es scheint also, als würde das Laden der Hauptapplikation das Anzeigen des Bildes innerhalb des SplashScreen-Threads verhindern, ist hier vielleicht eine höhere Prozesspriorität erforderlich? Hat es mit Java3D zu tun?

Testweise habe ich den Screen auch mal innerhalb der Applikation ausgeführt - mit dem gleichen Ergebnis:

Wenn die Applikation grade sehr ausgelastet ist (z.B. beim Laden und verarbeiten einer großen Datei) und der Splashscreen gestartet wird, zeigt sich der leere Frame, sobald der Ladevorgang abgeschlossen ist, wird das Bild angezeigt. Während des Ladeprozesses kann das Fenster des (kurzfristig testweise nicht auf undecorated gesetzt) aber bewegt werden - für mich ein Hinweis darauf, dass das Threading im Grunde funktioniert. Ein Schlafenlegen des Hauptthreads brachte leider auch nicht den erhofften Erfolg.

Ich wäre sehr glücklich, wenn es Hinweise oder Lösungen zu diesem Problem gibt, komme leider selber nicht weiter voran.

Danke schonmal im Voraus!
 
Hallo!

Schau mal hier:
Java:
/**
 * 
 */
package de.tutorials;

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JWindow;

/**
 * @author Thomas.Darimont
 * 
 */
public class SplashScreenExample {

    /**
     * @param args
     */
    public static void main(String[] args) {

        final SplashScreen splashScreen =
                new SplashScreen(new File("c:/splash.jpg"), new Dimension(320, 240));

        someLongRunningIntializationOperation();
        splashScreen
                .close();

        EventQueue
                .invokeLater(new Runnable() {
                    public void run() {
                        new JFrame("Simple Smart Client") {
                            {
                                setDefaultCloseOperation(EXIT_ON_CLOSE);
                                setSize(640, 480);
                                setLocationRelativeTo(null);
                                setVisible(true);
                            }
                        };

                    }
                });

    }

    static void someLongRunningIntializationOperation() {
        try {
            TimeUnit.SECONDS
                    .sleep(10);
        } catch (InterruptedException e) {
            e
                    .printStackTrace();
        }
    }

    static class SplashScreen extends JWindow {
        Image splashImage;

        public SplashScreen(File splashImageFile, Dimension dimension) {
            this
                    .setSize(dimension);
            try {
                this.splashImage = ImageIO
                        .read(splashImageFile);
            } catch (IOException e) {
                e
                        .printStackTrace();
            }
            setLocationRelativeTo(null);
            setVisible(true);
        }

        public void paint(Graphics g) {
            super
                    .paint(g);
            g
                    .drawImage(splashImage, 0, 0, null);
        }

        public void close() {
            setVisible(false);
            dispose();
        }
    }

}
Wenn du noch eine Abstraktion für die Darstellung von Fortschritt (beispielsweise über eine JProgressbar) brauchst
kannst du mal hier schauen:
http://www.tutorials.de/forum/java/...n-fuer-die-textkonsole.html?highlight=JCurses

Gruß Tom

Gruß Tom
 
Hi Tom,

vielen Dank für deine Ausführungen. Nachdem ich mich noch ein wenig mit dem Programm neschäftigt habe, ist mir aufgefallen wo mein Problem lag: Das Laden der Daten wurde innerhalb des AWT Threads ausgeführt - klar das da die Zeichenfunktionen erstmal brach liegen.

nun habe ich das Ganze entsprechend geändert und es klappt mit allen getesteten SplashScreens ausgezeichnet :)

Nochmals ein Dankeschön für die Hilfe - heute werd ich mal versuchen, den Ladestatus per Progressbar zu visualisieren, gute idee!

christian
 
Zuletzt bearbeitet:
Zurück