The MediaTracker Class

The ImageObserver interface is useful for monitoring a single image, but it starts to fall apart when faced with multiple images. The java.awt.MediaTracker class can track the loading status of many different images and organize them into logical groups. In future versions of Java, it may even be able to monitor the loading of audio clips and other kinds of multimedia content. However, Sun’s been promising this since Java 1.0 and still hasn’t delivered, so I’m not holding my breath.

To use java.awt.MediaTracker, simply create an instance of MediaTracker and then use the MediaTracker’s addImage( ) method to place each Image you care about under the MediaTracker’s control. When you add an Image to a MediaTracker, you give it a numeric ID. This ID does not have to be unique; it is really a group ID that is used to organize different images into groups. Before using the image, you call a method such as checkID( ) to see whether the image is ready. Other methods let you force loading to start, discover which images in a group have failed to load successfully, wait for images to finish loading, and so on.

Note

One difference between a MediaTracker and an ImageObserver is that the MediaTracker is called before the Image is used, while an ImageObserver is called after the Image is used.

Example 9.7 is an applet that loads an image from the Net. As usual, the image is loaded from the document base, and the name of the image file is read from a <PARAM> tag; the name of the parameter is imagefile. The applet uses a MediaTracker to see whether the image has loaded. The applet displays the words “Loading Picture. Please hang on” until the image has finished loading.

Example 9-7. Load an Image

import java.awt.*;
import java.applet.*;

public class TrackedImage extends Applet implements Runnable {

  private Image picture;
  private MediaTracker tracker;

  public void init(  ) {

    this.picture = this.getImage(this.getCodeBase(  ), 
     this.getParameter("imagefile"));
    this.tracker = new MediaTracker(this);
    this.tracker.addImage(this.picture, 1);
       
    Thread play = new Thread(this);
    play.start(  );
  
  }
  
  public void run(  ) {
  
    try {
      this.tracker.waitForID(1);
      this.repaint(  );
    }
    catch (InterruptedException ie) {      
    }
  
  }
  
  public void paint(Graphics g) {
   
    if (this.tracker.checkID(1, true)) {
      g.drawImage(this.picture, 0, 0, this);
    }
    else {
      g.drawString("Loading Picture. Please hang on", 25, 50);
    }
    
  }
  
}

The init( ) method reads the name of the image to be loaded; prepares an Image object, picture, to hold it by calling getImage( ); constructs a new MediaTracker called tracker; and then adds picture to tracker with an ID of 1. Next it constructs a new Thread and starts it.

The Thread that’s spawned just makes sure that the applet is repainted as soon as the image has finished loading. It calls tracker.waitForID(1), which blocks until all media with ID number 1 have finished loading. When that’s true, the method returns and repaint( ) is called. I used a separate thread so the call to waitForID( ) won’t block the rest of the applet.

The paint( ) method calls tracker.checkID(1, true) to see whether the media with ID 1 is available. If this method returns true, the image is available, and the applet calls drawImage( ) to render the image on the screen. Otherwise, the picture is not available, so the applet displays the string “Loading Picture. Please hang on”. A more sophisticated applet could put up a progress bar that showed the percentage of the Image that had been loaded and the approximate time remaining.

The Constructor

The MediaTracker class has one constructor:

public MediaTracker(Component comp)

This constructor creates a MediaTracker object that tracks images for a specified Component. It’s usually invoked like this:

MediaTracker tracker = new MediaTracker(this);

The constructor’s argument is the component on which the images you want to track will be displayed, generally a Panel or an Applet or a Canvas. You often call the constructor within the subclass defining the component that will be rendering the images; therefore, the argument to MediaTracker( ) is often this.

Adding Images to MediaTrackers

The two overloaded addImage( ) methods each add one Image to the list of images being tracked by this MediaTracker object. When an image is added, it is assigned an ID number by which it can later be referred to. Images are loaded in order of their IDs; that is, image 1 is loaded before image 2, and so on. If multiple images have the same ID, there’s no guarantee which will be loaded first. Adding an Image to a MediaTracker does not start loading the image data. You have to call checkID(ID, true), checkAll(true), or one of the four wait methods first.

public void addImage(Image image, int id)

This addImage( ) method adds image to the list of images being tracked by this MediaTracker object and assigns it the ID number id. The image will eventually be displayed at its normal (unscaled) size. For example, the following code fragment from an applet sets up a tracker for an image called logo.gif that’s in the same directory as the web page—it’s given the ID number 1:

Image picture = this.getImage(this.getDocumentBase(  ), "logo.gif");
MediaTracker tracker = new MediaTracker(this);
tracker.addImage(picture, 1);

If you are going to scale the image, you must use the next version of addImage( ).

public void addImage(Image image, int id, int width, int height)

This version of addImage( ) adds image to the list of Image objects being tracked by this MediaTracker with the ID number id. The image will eventually be displayed scaled to the width width and the height height. The following code fragment sets up a tracker for an image called logo.gif that’s in the same directory as the web page. This image will be scaled into a 30-pixel × 30-pixel square when it’s displayed:

Image picture = this.getImage(this.getDocumentBase(  ), "logo.gif");
MediaTracker tracker = new MediaTracker(this);
tracker.addImage(picture, 1, 30, 30);

Checking Whether Media Has Loaded

There are four check methods that tell you whether particular images tracked by a MediaTracker have loaded. These are:

public boolean checkID(int id)
public boolean checkID(int id, boolean load)
public boolean checkAll(  )
public boolean checkAll(boolean load)

public boolean checkID(int id)

This method checks whether all the images with the indicated ID that are tracked by this MediaTracker have finished loading. If they have, it returns true; otherwise, it returns false. Multiple Image objects may have the same ID. Merely checking the status of an image with this method will not make it start loading if it is not already loading. For example, the following paint( ) method draws thePicture only if all the Image objects with ID 1 have finished loading:

public void paint(Graphics g) {
   
    if (theTracker.checkID(1)) {
      g.drawImage(thePicture, 0, 0, this);
    }
    else {
      g.drawString("Loading Picture. Please hang on", 25, 50);
    }
    
  }

Warning

There’s something a little funny about how this operates. The Image with ID 1 is not necessarily the same Image as thePicture. There may be zero or more Image objects with ID 1, and none of them are necessarily thePicture. It’s up to the programmer to make sure the ID you check is the ID of the Image you want to load. Although there are occasions when you want to check one picture and display another, this is rare. There probably should be a MediaTracker method with the signature check(Image img), but there isn’t.

public boolean checkID(int id, boolean load)

This method checks whether all the Image objects with the indicated ID that are tracked by this MediaTracker have finished loading. If they have, it returns true; otherwise, it returns false. Multiple Image objects may have the same ID. If the boolean argument load is true, all images with that ID that are not yet being loaded will begin loading.

The following paint( ) method checks whether the Image objects with ID 1 have finished loading. If they have, then thePicture is drawn. Otherwise, the Image objects with ID 1 start loading (if they aren’t already), and the message “Loading Picture. Please hang on” is displayed:

public void paint(Graphics g) {
   
    if (theTracker.checkID(1, true)) {
      g.drawImage(thePicture, 0, 0, this);
    }
    else {
      g.drawString("Loading Picture. Please hang on", 25, 50);
    }
    
  }

public boolean checkAll( )

This method checks whether all the Image objects that are tracked by this object have finished loading. ID numbers are not considered. If all images are loaded, it returns true; otherwise, it returns false. It does not start loading images that are not already loading.

public boolean checkAll(boolean load)

This version of checkAll( ) checks whether all the Image objects that are tracked by this MediaTracker have finished loading. If they have, it returns true; otherwise, it returns false. If the boolean argument load is true, then it also starts loading any Image objects that are not already being loaded.

Waiting for Media to Load

MediaTracker is often used in conjunction with the getImage( ) methods of Applet or java.awt.Toolkit. The getImage( ) method is called as many times as necessary to load images. Then the MediaTracker makes sure the images have finished loading before the program continues. You could do this yourself by loading images in separate threads, then waiting on the threads loading the images. However, the MediaTracker class shields you from the details and is substantially easier to use correctly.

The next four methods start loading images tracked by the MediaTracker and then block while waiting for the images to load. For performance reasons, each method loads four images at a time in four separate threads. If more than four images need to be loaded, then some of them will have to wait for others to finish first. After invoking one of these methods, the calling thread will not continue until all the requested images have finished loading. If you don’t want your program to block while you wait for images to download, you can call these methods inside a separate thread.

public void waitForID(int id) throws InterruptedException

This method forces the images with the ID number id that are tracked by this MediaTracker to start loading and then waits until each one has either finished loading, aborted, or received an error. An InterruptedException is thrown if another thread interrupts this thread. For example, to begin loading Image objects with ID 1 and then wait for them to finish loading, you would write:

try {
  tracker.waitForID(1);
}
catch (InterruptedException e) {
}

Let’s look at a longer example. Netscape may be infamous for foisting the <BLINK> tag on the world, but it made some solid contributions to HTML as well. Two of the best are the HEIGHT and WIDTH attributes of the <IMG> tag. To the user, it seems much faster to load a page that includes HEIGHT and WIDTH attributes for all its images than one that doesn’t. Actually, the time it takes to load the page is the same either way; but browsers can start displaying text and partial images as soon as they know how much space to reserve for each image. Consequently, if the width and height of each image is specified in the HTML, the browser can lay out the page and display the text immediately, without forcing the user to wait for everything to load. However, it takes a lot of time to measure every image on a page manually and rewrite the HTML, especially for pages that include many different size images.

Example 9.8 is a program that gets a URL from the user, reads the requested page using an HTMLEditorKit.Parser, and outputs the HTML with HEIGHT and WIDTH attributes added to all the <IMG> tags that didn’t have them. It uses the default Toolkit object’s getImage( ) method to retrieve the Image objects, the Image’s getWidth( ) and getHeight( ) methods to measure them, and a MediaTracker to make sure that the height and width are available. Before the height and the width of a particular image are read, the MediaTracker’s waitForID( ) method is invoked to let the image finish loading first.

Example 9-8. A Program That Adds Height and Width Tags to the IMGs on a Web Page

import javax.swing.text.*;
import javax.swing.text.html.*;
import javax.swing.text.html.parser.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.awt.*;
import java.awt.image.*;


public class ImageSizer extends HTMLEditorKit.ParserCallback {

  private Writer out;
  private URL base;
  
  public ImageSizer(Writer out, URL base) {
    this.out = out;
    this.base = base;
  }

  public void handleStartTag(HTML.Tag tag, 
   MutableAttributeSet attributes, int position) {
    try {  
      out.write("<" + tag);
      this.writeAttributes(tag, attributes);
      out.write(">");
      out.flush(  );
    }
    catch (IOException e) {
      System.err.println(e);
      e.printStackTrace(  );
    }
    
  }
  
  public void handleEndTag(HTML.Tag tag, int position) {
    try {    
      out.write("</" + tag + ">");
      if (tag.breaksFlow(  )) out.write("
");
      out.flush(  );
    }
    catch (IOException e) {
      System.err.println(e);
    }
  }
  
  private void writeAttributes(HTML.Tag tag, AttributeSet attributes) 
   throws IOException {
    
    Enumeration e = attributes.getAttributeNames(  );
    while (e.hasMoreElements(  )) {
      Object name = e.nextElement(  );
      String value = (String) attributes.getAttribute(name);
      out.write(" " + name + "="" + value + """);
    }
    // for the IMG tag we may have to add HEIGHT and WIDTH attributes
    if (tag == HTML.Tag.IMG) { 
      try {
        if (attributes.getAttribute(HTML.Attribute.HEIGHT) == null
         || attributes.getAttribute(HTML.Attribute.WIDTH) == null) {
           URL u = new URL(base, 
            (String) attributes.getAttribute(HTML.Attribute.SRC));
           Image img = Toolkit.getDefaultToolkit(  ).getImage(u);
           Component temp = new Label(  );
           MediaTracker tracker = new MediaTracker(temp);
           tracker.addImage(img, 1);
           try {
             tracker.waitForID(1);
             if (attributes.getAttribute(HTML.Attribute.WIDTH) == null) {
               out.write(" WIDTH="" + img.getWidth(temp) + """);
             }
             if (attributes.getAttribute(HTML.Attribute.HEIGHT) == null) {
               out.write(" HEIGHT="" + img.getHeight(temp) + """);
             } 
           }
           catch (InterruptedException ex) {
           }
        }
      }
      catch (MalformedURLException ex) {
        // SRC attribute is malformed
      }
    }
    
  }
  
  public void handleComment(char[] text, int position) { 
    
    try {
      out.write("<!-- ");
      out.write(text);
      out.write(" -->");
      out.flush(  );
    }
    catch (IOException e) {
      System.err.println(e);
    }
    
  }
  
  public void handleText(char[] text, int position) { 
    
    try { 
      out.write(text);
      out.flush(  );
    }
    catch (IOException e) {
      System.err.println(e);
      e.printStackTrace(  );
    }
    
  }
  
  public void handleSimpleTag(HTML.Tag tag, 
   MutableAttributeSet attributes, int position) {
    try {
      out.write("<" + tag);
      this.writeAttributes(tag, attributes);
      out.write(">");
    }
    catch (IOException e) {
      System.err.println(e);
      e.printStackTrace(  );
    }
  }

  public static void main(String[] args) { 
    
    for (int i = 0; i < args.length; i++) { 
      
      // The ParserGetter class is from Chapter 8
      ParserGetter kit = new ParserGetter(  );
      HTMLEditorKit.Parser parser = kit.getParser(  );
    
      try {
        URL u = new URL(args[0]);
        InputStream in = u.openStream(  );
        InputStreamReader r = new InputStreamReader(in);
        HTMLEditorKit.ParserCallback callback 
         = new ImageSizer(new OutputStreamWriter(System.out), u);
        parser.parse(r, callback, false);
      }
      catch (IOException e) {
        System.err.println(e); 
        e.printStackTrace(  );
      }
      
    } 
    
  }
  
}
                     

This is a standalone application that extends HTMLEditorKit.ParserCallback. It’s very similar to Example 8.10, PageSaver, in the last chapter. The only real difference is that it adds HEIGHT and WIDTH attributes to the IMG tags rather than rewriting attribute values. Consequently, the only significantly different part of this example is the writeAttributes( ) method. If this method sees that it’s dealing with an IMG tag, it checks to see whether that tag has both HEIGHT and WIDTH attributes. If it does, then the tag is simply copied from the input onto the output. However, if either is missing, the method then constructs the full URL for the image and retrieves it with Toolkit.getDefaultToolkit().getImage( ). Next, a MediaTracker is created. The MediaTracker( ) constructor requires an ImageObserver as an argument, so a blank java.awt.Label is constructed to fill that role. This will also be used later as the ImageObserver for the getWidth( ) and getHeight( ) methods. The image is added to the tracker, and the tracker’s waitForID( ) method is used to pause execution until the image has actually finished loading. At this point, the image’s getWidth( ) and getHeight( ) methods are invoked, and their results are written in the output as the value of the HEIGHT and WIDTH attributes.

public boolean waitForID(int id, long milliseconds) throws InterruptedException

This version of waitForID( ) begins loading all the images that are tracked by this MediaTracker with the ID number id and then waits until each one has either finished loading, aborted, or received an error or until the specified number of milliseconds has passed. An InterruptedException is thrown if another thread interrupts this thread. For example, to begin loading all Image objects with ID 1 and wait no longer than 2 minutes (120,000 milliseconds) for them to finish loading, you would write:

try {
  tracker.waitForID(1, 120000);
}
catch (InterruptedException e) {
}

public boolean waitForAll( ) throws InterruptedException

The waitForAll( ) method starts loading all the images that are tracked by this MediaTracker in up to four separate threads while pausing the thread that invoked it until each tracked image has either finished loading, aborted, or received an error. An InterruptedException is thrown if another thread interrupts the waiting thread.

This method might be used by an animation applet that wants to load all frames before it begins playing. The applet would add each frame to the MediaTracker and then call waitForAll( ) before starting the animation. Example 9.9 is a simple applet that does exactly this. To play an animation, an applet must download a series of images, each of which will be one cell of the animation. Downloading the images is easy using the URL class: just create URL objects that point to each image, and call getImage( ) with each URL. Use a MediaTracker to force the images to load; then use waitForAll( ) to make sure all frames have loaded. Finally, call drawImage( ) to display the images in sequence.

Example 9.9 reads a list of filenames from <PARAM> tags in the applet; the parameter names are Cell1, Cell2, Cell3, and so on. The value of the parameter Cell n should be the name of the n th file in the animation sequence. This makes it easy to retrieve an indefinite number of images; the init( ) method continues reading parameters until it finds a parameter name that doesn’t exist. The applet builds URLs from the filenames and the document base and uses getImage( ) to retrieve each image. The Image objects are stored in a Vector since it’s not known in advance how many there will be. The applet assumes that the files reside in the same directory as the HTML page; therefore, one applet on a site can play many different animations just by changing the <PARAM> tags and the image files. When an image is retrieved, it’s added to the MediaTracker tracker, and tracker’s checkID( ) method starts loading the image. Then the start( ) method spawns a new thread to display the images in sequence. tracker’s waitForAll( ) method pauses the animation thread until the images have finished loading.

Example 9-9. A Very Simple Animator Applet

import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.applet.*;
import java.util.*;

public class Animator extends Applet 
 implements Runnable, MouseListener {

  private boolean running = false;
  private int     currentCell = 0;
  private Vector  cells = new Vector(  );
  private MediaTracker tracker;

  public void init(  ) {

    this.addMouseListener(this);
    
    String nextCell;
    this.tracker = new MediaTracker(this);
    for (int i = 0; (nextCell = this.getParameter("Cell" + i)) != null;  i++) {
      Image img = this.getImage(this.getDocumentBase(  ), nextCell);
      cells.addElement(img);
      tracker.addImage(img, i);
      // start loading the image in a separate thread
      tracker.checkID(i, true);
    }
    
  }
  
  public void run(  ) {
  
    // wait for all images to finish loading
    try {
      this.tracker.waitForAll(  );
    }
    catch (InterruptedException e) {      
    }
    
    for (currentCell=0; currentCell < cells.size(  ); currentCell++) { 
      if (!running) return;
        // paint the cell
        this.repaint(  );
        // sleep for a tenth of a second
        // i.e. play ten frames a second
      try {
        Thread.sleep(100);
      }
      catch (InterruptedException ie) {      
      }
    }
  
  }

  public void stop(  ) {
    this.running = false;
  }
  
  public void start(  ) {
    this.running = true;
    Thread play = new Thread(this);
    play.start(  );
  }

  public void paint(Graphics g) {
    g.drawImage((Image) cells.elementAt(currentCell), 0, 0, this);    
  }
  
  // The convention is that a mouseClick starts
  // a stopped applet and stops a running applet.
  public void mouseClicked(MouseEvent e) {
  
    if (running) {
      this.stop(  );
    }
    else {
      this.start(  );
    }
          
  }
  
// do-nothing methods required to implement the MouseListener interface
  public void mousePressed(MouseEvent e) {}
  public void mouseReleased(MouseEvent e) {}
  public void mouseEntered(MouseEvent e) {}
  public void mouseExited(MouseEvent e) {}

}

It’s easy to imagine ways to enhance this applet. One possibility would be sprites that follow a path across a constant background. But whatever extensions you add, they won’t require any changes to the applet’s networking code.

public boolean waitForAll(long milliseconds) throws InterruptedException

This method is similar to the previous waitForAll( ) method. It too starts loading all the images that are tracked by this MediaTracker and waits until each one has either finished loading, aborted, or received an error. However, this method times out if the specified number of milliseconds has passed before all images are complete. It throws an InterruptedException if another thread interrupts this thread. The following code fragment begins loading all Image objects tracked by the MediaTracker tracker and waits not more than 2 minutes (120,000 milliseconds) for them to finish loading:

try {
  tracker.waitForAll(120000);
}
catch (InterruptedException e) {
}

As with any time-consuming operation, you should display some sort of progress bar so that the user has an idea how long the operation is likely to take and give the user an option to cancel the download.

Error Checking

If there is an error as an Image is loaded or scaled, then that Image is considered “complete”; no further loading of that image’s data takes place. The following methods let you check whether an error has occurred and find out which image is at fault. However, there are no methods that tell you what sort of error has occurred.

public boolean isErrorAny( )

This method checks whether an error occurred while loading any image tracked by this object:

if (tracker.isErrorAny(  )) {
  System.err.println("There was an error while loading media");
}

This method does not tell you which image failed or why. If there was an error, you can use getErrorsAny( ) to find out which objects returned errors. Do not assume that a single Image caused the error; it is common for an entire group of images to fail to load as a result of a single error, such as a broken network connection. Consequently, if one image failed to load, chances are others did too.

public Object[ ] getErrorsAny( )

This method returns an array containing all the objects tracked by this MediaTracker that encountered an error while loading. If there were no errors, it returns null:

if (tracker.isErrorAny(  )) {
  System.err.println("The following media failed to load:");
  Object[] failedMedia = tracker.getErrorsAny(  );
  for (int i = 0; i < failedMedia.length; i++) {
    System.err.println(failedMedia[i]);
  }
}

public boolean isErrorID(int id)

This method returns true if any of the media with the specified ID encountered an error while loading; otherwise, it returns false. If there was an error, use getErrorsID( ) to find out which objects returned errors. Remember, a single ID may refer to several Image objects, any of which may have encountered errors:

if (tracker.isErrorID(2)) {
  System.err.println("An error occurred loading media with ID 2");
}

public Object[ ] getErrorsID(int id)

This method returns an array containing all the objects with this ID that encountered an error while loading. If there were no errors, it returns null:

if (tracker.isErrorID(2)) {
  System.err.println("The following media failed to load:");
  Object[] failedMedia = tracker.getErrorsID(2);
  for (int i = 0; i < failedMedia.length; i++) {
    System.err.println(failedMedia[i]);
  }
}

Checking the Status of Media

MediaTracker has a number of methods that report the status of image groups. Unfortunately, MediaTracker only lets you check the status of image groups, not individual images—which is a good argument for keeping image groups small. To report the status, the MediaTracker class defines four flag constants that are combined to tell you whether the images in question are loading, aborted, errored out, or completed. These constants are shown in Table 9.2.

Table 9-2. Media Status Constants

Constant

Value

Meaning

MediaTracker.LOADING

1

At least one image in the group is still being downloaded.

MediaTracker.ABORTED

2

The loading process aborted for at least one image in the group.

MediaTracker.ERRORED

4

An error occurred during loading for at least one image in the group.

MediaTracker.COMPLETE

8

At least one image in the group was downloaded successfully.

These constants are compared to the values returned by the status methods to determine whether particular conditions or combinations of conditions are true. Since each constant is an integral power of two, each has exactly one set bit, and thus the different constants can be easily combined with the bitwise operators to test combinations of conditions.

public int statusAll(boolean load)

This method returns the status of all the media tracked by this MediaTracker object. Thus to test whether any of the media tracked by MediaTracker m have finished loading, you would write:

if ((m.statusAll(false) & MediaTracker.COMPLETED) 
 		== MediaTracker.COMPLETED) {

If the argument load is true, any media that have not yet begun loading will start loading. If it is false, they will not.

public int statusID(int id, boolean load)

This method returns the status of the media sharing the ID id that are tracked by this MediaTracker. If the load argument is true, the media with the given id will begin loading if they haven’t already started. If load is false, they won’t. Thus, to test whether any of the media tracked by MediaTracker m with ID 2 have finished loading, you would write:

if ((m.statusAll(2, false) & MediaTracker.COMPLETED)
            == MediaTracker.COMPLETED) {...}

Because of the nature of the flags, if any of the images with ID 2 have finished loading, this will be true. Similarly, if any of the images with ID 2 have errored out, then the following expression will also be true:

(m.statusAll(2, false) & MediaTracker.ABORTED)
            == MediaTracker.ABORTED)

The same is true for the MediaTracker.LOADING and MediaTracker.ERRORED flags. Because there isn’t any way to check a single image (other than putting the image into a group by itself), statusID( ) and statusAll( ) can return apparently contradictory results. For example, if there are four images in the group, it’s entirely possible that statusID( ) will return the value:

MediaTracker.LOADING | MediaTracker.ABORTED | MediaTracker.ERRORED 
| MediaTracker.COMPLETE

This means that, of the four images, one is still loading, one aborted, an error occurred in another, and one loaded successfully. A single group of images can appear to simultaneously have completed loading, still be loading, have aborted, and have encountered an error. There is no way to test the status of a single Image if it shares an ID with another Image. For this reason, it’s probably not a good idea to let too many images share the same ID.

Removing Images from MediaTrackers

Starting in Java 1.1, it is possible to remove an image from a MediaTracker using one of the three overloaded removeImage( ) methods:

public void removeImage(Image image)
public void removeImage(Image image, int id)
public void removeImage(Image image, int id, int width, int height)

The first method removes all instances of the Image argument from the MediaTracker. Most of the time, there’ll be only one of these. The second variant removes the specified image if it has the given ID. The third removes the specified image if it has the given ID and width and height.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.98.108