Laden eines Bildes nur sehr langsam
*Markus
- java
0 Slyh0 *Markus
0 H-P Ortner
Hallo,
da meine Java-Kenntnisse noch relativ bescheiden sind, habe ich mich zu den Grundlagen mal ein bisschen mit dem Laden von Grafiken in ein Fenster beschäftigt.
Dabei verwendete ich Code aus verschiedenen Java-Beispielen. Jetzt ist es so, dass diese kleine Fenster, das hier erzeugt wird, immens lange zum Laden braucht. Das Fenster öffnet sich erst eine Sekunde nach Programmstart. Die Grafik darin erscheint erst zwei Sekunden nach Programmstart. Offensichtlich ist eine Menge Overhead vorhanden. Ich kann mir auch nicht vorstellen, dass ich für das Laden von Bildern Methoden überschreiben muss, wie ich es aber bisher nur sah. Wie macht man es richtig? Hier das Beispielprogramm:
import javax.swing.*;
import java.awt.Toolkit;
import java.awt.Graphics;
import java.awt.Image;
class Bild extends JComponent {
private Image image = Toolkit.getDefaultToolkit().getImage("/home/markus/Bilder/ws.jpg");
@Override
public void paint(Graphics g) {
g.drawImage(image, 0, 0, this);
}
}
public class GrafikVersuch1 {
public static void main(String[] args) {
JFrame f = new JFrame("GrafikVersuch1");
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
f.setLocation(0, 0);
f.setSize(400, 200);
f.add( new Bild() );
f.setVisible(true);
}
}
Markus.
Hallo,
Dabei verwendete ich Code aus verschiedenen Java-Beispielen. Jetzt ist es so, dass diese kleine Fenster, das hier erzeugt wird, immens lange zum Laden braucht. Das Fenster öffnet sich erst eine Sekunde nach Programmstart. Die Grafik darin erscheint erst zwei Sekunden nach Programmstart. Offensichtlich ist eine Menge Overhead vorhanden.
Wie sieht die Prozessorbelastung während dieser 3 Sekunden aus?
Die 1. Sekunden könnte der normale Overhead sein, der beim Laden der
VM und inbesondere der viiieeelen Swing-Klassen benötigt wird. (Swing
ist recht umfangreich. Auch wenn du nur wenig machst, wird da im
Hintergrund recht viel geladen und initialisiert...)
Daß dein Bild erst nach 2 Sekunden erscheint, mag damit zusammen-
hängen, daß es asynchron geladen wird. Durch den Aufruf von
Toolkit.getImage() wird das Laden des Bildes nur "angestoßen". Es
wird erst bei der ersten Anforderung im Hintergrund geladen. Je nach
Implementierung kann dies die genannten 2 Sekunden dauern.
Im Zweifelsfall solltest du das Bild mal über die ImageIO-Klasse
laden, so wie es im Java-Almanac beschrieben ist. Dabei werden die Daten
sofort aus der Datei geladen und stehen somit auch sofort zur Verfügung.
Ich kann mir auch nicht vorstellen, dass ich für das Laden von Bildern Methoden überschreiben muss, wie ich es aber bisher nur sah.
Üblicherweise wird das in Java aber so gemacht. Es gibt zwar im Prinzip
auch andere Möglichkeit. Die bequemste ist aber, eine Komponente
von Component (oder einer anderen passenden Klasse) abzuleiten und
die paint-Methode zu überladen um dann über das Graphics-Objekt das
Bild zu zeichnen.
Dein Code sieht im Prinzip gut aus.
Gruß
Slyh
Hallo,
Wie sieht die Prozessorbelastung während dieser 3 Sekunden aus?
Sie steigt kurz an, was aber für das Starten von Programmen typisch ist.
Die 1. Sekunden könnte der normale Overhead sein, der beim Laden der
VM und inbesondere der viiieeelen Swing-Klassen benötigt wird. (Swing
ist recht umfangreich. Auch wenn du nur wenig machst, wird da im
Hintergrund recht viel geladen und initialisiert...)
So etwas in die Richtung dachte ich mir schon.
Daß dein Bild erst nach 2 Sekunden erscheint, mag damit zusammen-
hängen, daß es asynchron geladen wird. Durch den Aufruf von
Toolkit.getImage() wird das Laden des Bildes nur "angestoßen". Es
wird erst bei der ersten Anforderung im Hintergrund geladen. Je nach
Implementierung kann dies die genannten 2 Sekunden dauern.
Ehrlich gesagt finde ich das schon etwas viel.
Ich kann mir auch nicht vorstellen, dass ich für das Laden von Bildern Methoden überschreiben muss, wie ich es aber bisher nur sah.
Üblicherweise wird das in Java aber so gemacht. [...]
Hmmm, mit dieser Art zu programmieren muss ich mich wahrscheinlich noch anfreunden. Desweiteren frage ich mich, wie das vorher gehandhabt wurde, da das Überschreiben von Methoden doch erst seit Java 5 funktioniert.
Markus.
Hallo,
Ich kann mir auch nicht vorstellen, dass ich für das Laden von Bildern Methoden überschreiben muss, wie ich es aber bisher nur sah.
Üblicherweise wird das in Java aber so gemacht. [...]
Hmmm, mit dieser Art zu programmieren muss ich mich wahrscheinlich noch anfreunden. Desweiteren frage ich mich, wie das vorher gehandhabt wurde, da das Überschreiben von Methoden doch erst seit Java 5 funktioniert.
Nein, das ging schon immer. Das ist ja eine grundlegende Methodik der OOP und Java war schon immer _nur_ objektorientiert. Überschrieben wird/wurde eine Methode der Superklassen dann, wenn die Methode der abgeleiteten Klasse genau so heißt, die selben Argumente hat und den selben Objekttyp oder Datentyp zurück gibt, wie die Methode der Superklasse. Dass dies schon immer ging, beweist die Methode public String toString(). Diese ist in allen Objekten vorhanden, wird nur jeweils Objektspezifisch überschrieben.
Die @Override-Notation ab 1.5 zeigt nur dem Compiler, dass hier eine Methode der Superklasse überschrieben wird. Er reagiert dann mit einem Compiler-Fehler, wenn das schief läuft. Wenn man es also macht, wie immer, kann man @Override weglassen. Und diese Notation erlaubt, dass der Rückgabewert nicht absolut identisch sein muss. Er kann nun auch ein vom Rückgabenwert der zu überschreibenden Methode abgeleiteter Objekttyp sein.
viele Grüße
Axel
Hallo Axel,
Und diese Notation erlaubt, dass der Rückgabewert nicht absolut identisch sein muss. Er kann nun auch ein vom Rückgabenwert der zu überschreibenden Methode abgeleiteter Objekttyp sein.
Das geht unabhängig davon, ob man diese Annotation angibt oder nicht.
Grüße
Daniel
Hallo,
Und diese Notation erlaubt, dass der Rückgabewert nicht absolut identisch sein muss. Er kann nun auch ein vom Rückgabenwert der zu überschreibenden Methode abgeleiteter Objekttyp sein.
Das geht unabhängig davon, ob man diese Annotation angibt oder nicht.
Stimmt. Die Annotation @Override schützt also nur vor versehentlichem Nichtüberschreiben ;-)
Beispiel: Main.java:
class MyString1 {
String myStr = "Mein String";
MyString1(String s) {
myStr = s;
}
public String getMyString() {
return myStr;
}
}
class MyString2 extends MyString1 {
MyString2(String s) {
super(s + " erweitert");
}
}
class TestOverride1 {
TestOverride1() {
return;
}
public MyString1 test() {
return new MyString1("Hallo Welt1");
}
}
class TestOverride2 extends TestOverride1 {
TestOverride2() {
super();
}
//ueberschreibt test() in TestOverride1 mit JDK1.5;
//JDK1.4 wuerde einen Compiler-Fehler bringen,
//weil die Typen nicht uebereinstimmen.
public MyString2 test() {
return new MyString2("Hallo Welt2");
}
}
class TestOverride3 extends TestOverride2 {
TestOverride3() {
super();
}
//ueberschreibt test() in TestOverride2 _nicht_,
//weil die Argumente nicht uebereinstimmen
//@Override würde einen Compiler-Fehler bringen
//@Override
public MyString1 test(String s) {
return new MyString1("Hallo Welt3 " + s);
}
}
class Main {
public static void main(String[] args) {
TestOverride1 to1 = new TestOverride1();
TestOverride2 to2 = new TestOverride2();
TestOverride3 to3 = new TestOverride3();
System.out.println(to1.test());
System.out.println(to1.test().getMyString());
System.out.println(to2.test());
System.out.println(to2.test().getMyString());
//Das Folgende ruft die Methode .test() von TestOverride2 auf,
//weil kein Ueberschreiben stattgefunden hat.
System.out.println(to3.test());
System.out.println(to3.test().getMyString());
}
}
viele Grüße
Axel
Hallo Markus!
...
... Das Fenster öffnet sich erst eine Sekunde nach Programmstart.
So lange kann es dauern - wenn Rechner schwach oder sonstwie beschäftigt ist. ;o)
Die Grafik darin erscheint erst zwei Sekunden nach Programmstart. Offensichtlich ist eine Menge Overhead vorhanden.
Das ist doch überraschend, wie groß ist das Bild?
Ich kann mir auch nicht vorstellen, dass ich für das Laden von Bildern Methoden überschreiben muss, wie ich es aber bisher nur sah.
Doch das ist die übliche Methode
Wie macht man es richtig? Hier das Beispielprogramm:
Richtig ist relativ - ich würd es so machen:
Eine Klasse für das Bild:
import java.awt.Graphics;
import java.awt.Image;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
public class Bild extends JComponent {
private Image image = null;
public Bild(String fileName){
File file= new File(fileName);
try {
image= ImageIO.read(file);
} catch (Exception e){
System.err.println(e);
System.exit(1);
}
}
@Override
public void paint(Graphics g) {
g.drawImage(image, 0, 0, this);
}
}
Eine Klasse zum Starten:
import javax.swing.JFrame;
public class Viewer1 {
public static void main(String[] args) {
JFrame f = new JFrame("GrafikVersuch1");
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
f.setLocation(0, 0);
f.setSize(400, 200);
f.add(new Bild("ws.jpg") );
f.setVisible(true);
}
}
Und dann würde ich der Breite und Höhe des Bildes und ...
Viel Spaß beim Tüfteln, du ist am richtigen Weg.
Viele Grüße
H-P Ortner