java.awt.MediaTracker is a utility class that
simplifies life if we have to wait for one or more images to be loaded
before they're displayed. A MediaTracker
monitors the preparation of an image or a group of images and lets us
check on them periodically, or wait until they are
completed. MediaTracker uses the
ImageObserver interface internally to receive image
updates.
The following applet, LoadMe, uses a
MediaTracker to wait while an image is prepared. It
shows a "Loading..." message while it's waiting. (If you
are retrieving the image from a local disk or very fast network, this
message might go by quickly, so pay attention.)
import java.awt.*;
public class TrackImage extends java.applet.Applet implements Runnable {
Image img;
final int MAIN_IMAGE = 0;
MediaTracker tracker;
boolean show = false;
Thread runme;
String message = "Loading...";
public void init() {
img = getImage( getClass().getResource(getParameter("img")) );
tracker = new MediaTracker(this);
tracker.addImage( img, MAIN_IMAGE );
}
public void start() {
if ( !tracker.checkID( MAIN_IMAGE ) ) {
runme = new Thread( this );
runme.start();
}
}
public void stop() {
runme.stop();
runme = null;
}
public void run() {
repaint();
try {
tracker.waitForID( MAIN_IMAGE );
} catch( InterruptedException e) { }
if ( tracker.isErrorID( MAIN_IMAGE ) )
message= "Error";
else
show = true;
repaint();
}
public void paint( Graphics g ) {
if ( show )
g.drawImage( img, 0, 0, this );
else {
g.drawRect( 0, 0, getSize().width-1, getSize().height-1);
g.drawString( message, 20, 20 );
}
}
}
From its init() method, LoadMe
requests its image and creates a MediaTracker to
manage it. Later, after the applet is started,
LoadMe fires up a thread to wait while the image is
loaded. Note that we don't do this in init()
because it would be rude to do anything time-consuming there; it would
take up time in an AWT thread that we don't
own. In this case, waiting in init() would be
especially bad because paint() would never get
called and our "loading" message wouldn't be
displayed; the applet would just hang until the image loaded. It's
often better to create a new thread for initialization and display a
startup message in the interim.
When we construct a MediaTracker, we give it
a reference to our component (this). After creating
a MediaTracker, we assign it images to manage.
Each image is associated with an integer identifier we'll
use later for checking on its status. Multiple images can be
associated with the same identifier, letting us manage them as a
group. The value of the identifier is also used to prioritize loading
when waiting on multiple sets of images; lower IDs have higher
priority. In this case, we want to manage only a single image, so we
created one identifier called MAIN_IMAGE and passed
it as the ID for our image in the call to
addImage().
In our applet's start() method, we
call the MediaTracker's
checkID() routine with the ID of the image to see
if it's already been loaded. If it hasn't, the applet fires
up a new thread to fetch it. The thread executes the
run() method, which simply calls the
MediaTracker waitforID() routine
and blocks on the image, waiting for it to finish loading. The
show flag tells paint() whether
to display our status message or the actual image. We do a
repaint() immediately upon entering
run() to display the "Loading..."
status, and again upon exiting to change the display. We test for
errors during image preparation with isErrorID()
and change the status message if we find one.
This may seem like a lot of work to go through, just to put up
a status message while loading a single
image. MediaTracker is more valuable when we are
working with many images that have to be available before we can begin
parts of our application. It saves us from implementing a custom
ImageObserver for every application. In the
future, MediaTracker should also be able to track
the status of audio clips and other kinds of media (as its name
suggests).