5. How Applets Work
By now you should have a good understanding of the differences between top-down programming and object-oriented programming, along with a healthy appreciation for the syntax and semantics of the Java language. You've got a great idea for a killer applet, and you're ready to start coding. Where do you go from here?
In this chapter, we'll explain the basics of writing applets. We'll start by explaining how to extend the Applet class and describing the important methods to override to get the behavior you want from your applet. We'll show you how to use the methods of the Applet class to get pictures and sound clips from the network. You'll learn how to get parameters from HTML code so your applets can exhibit different behaviors without being recompiled. We will explain how to make your applet respond to mouse actions and keyboard input. Finally, we'll show you how to make your applets come to life by using threads and teach you how to rid your applets of that annoying flicker.
What Is an Applet
An applet is a nifty class combining elements of a sophisticated graphics window with easy-to-use networking capabilities. It is, in essence, a miniature graphical user interface, like Microsoft Windows or X11, that is guaranteed to have basically the same functionality regardless of the type of computer running it.
Applets are very useful for writing applications on the Internet because they can be embedded in HTML documents and run using Java-enabled Web browsers like Netscape Navigator 2.0. To create your own applets, you extend the Applet class and reference the new class in a Web page. Let's take another look at the Hello World Applet from Chapter 2, "Getting Started With Java."
Example 5-1a: The Hello World Applet.
import java.applet.Applet; import java.awt.Graphics; public class HelloWorldApplet extends Applet { public void init() { resize(250,250); } public void paint(Graphics g) { g.drawString("Hello world!",25,25); } }
The Hello World Applet extends the Applet class, meaning that all of the methods and variables available to the Applet class are available to our extension of it. Two of these methods are init and paint. We can override these methods, changing their default behavior so they will do what we want. Here's the HTML code for a Web page that embeds the Hello World Applet:
Example 5-1b: The Hello World Web page.
Figure 5-1: Push here to see the Hello World Applet<HTML> <HEAD> <TITLE>Hello World Applet</TITLE> </HEAD> <BODY> <APPLET CODE="HelloWorldApplet.class" WIDTH=250 HEIGHT=250> </APPLET> </BODY> </HTML>
The APPLET tag has not been incorporated into any existing HTML standards by the World Wide Web Consortium (W3C), the authoritative standards group for the WWW. This syntax is used by Netscape Navigator 2.0 and by Sun's appletviewer, the only Java-capable Web browsers currently in existence, Therefore, it will probably enjoy a pseudostandard existence like that of the other non-HTML compliant markup tags used by Netscape, such as <CENTER> and <BLINK>. The W3C is currently proposing an <INSERT> tag for embedding applications in Web pages; for the latest news on this topic, check out http://www.w3.org/pub/WWW/TR/WD-insert.html.
The CODE parameter inside the APPLET tag specifies the full URL to the applet's compiled class-here we are assuming that this HTML page is in the same directory as the Hello World class. Notice that we have to tell the Web browser, by using WIDTH and HEIGHT tags, how large the applet will be so that it can lay out the page properly. Under Netscape Navigator 2.0, you can specify 100% for the values of these tags-this will cause the browser to give the applet as much space as it needs initially
Programming applets in Java is stylistically different than programming applications in other languages. Java code is written largely in an event-driven fashion, similar to hypertext a la the Web instead of the usual linear flow as in traditional text. The runtime environment, your Web browser, acts as an interface between the code and the computer on which the browser is running. Figure 5-2 graphically represents this relationship.
Figure 5-2: Applets and the runtime environment.
The runtime environment understands certain applet methods, like paint and mouseMove, and it will call these when the screen needs to be painted or when the mouse has moved. By default, an applet does nothing when these methods are called-it is up to you, the programmer to override these methods if you want your applet to respond to the corresponding events, such as mouse motion or a request to refresh the graphics display.
The Stages of an Applet
When a Java-compliant Web browser loads an Applet class, it first allocates memory for the applet and its global variables. Then it runs the applet's init method. (Generally programmers use the init method to initialize global variables, get resources from the network, and set up the user interface.) Next the browser calls the applet's start method. If the portion of the browser containing the applet is visible (which is generally the case when an applet is just being started!), the applet's paint method is called. If the user leaves the page containing the applet, the browser calls the stop method. When the user returns, the start method is called again, as well as the paint method. The following code illustrates what happens when the user leaves the page and then returns:
Example 5-2: The Counting Applet.
import java.applet.*; import java.awt.*; public class Count extends Applet { int InitCount=0; int StartCount=0; int StopCount=0; int PaintCount=0; public void init() { resize(250,75); InitCount = InitCount + 1; } public void start() { StartCount = StartCount + 1; } public void stop() { StopCount = StopCount + 1; } public void paint(Graphics g) { PaintCount++; String Output = new String( "Inits: "+InitCount+ " Starts: "+StartCount+ " Stops: "+StopCount+ " Paints: "+paintCount); g.drawString(Output,25,25); } }
The applet's output after being loaded is shown in Figure 5-3. The applet has been initialized once, started once, stopped never, and painted at least once.
Figure 5-3: The Counting Applet.
If you go to another Web page and then come back to this one (without shutting down your Web browser), you see that the applet still has been initialized only once, but has been started twice, stopped once, and painted at least twice. You may try this for display in Figure 5-4.
Figure 5-4: The Counting Applet after a reload.
Clicking the Reload button in Netscape Navigator +2.0 causes the applets on the current page to be stopped and started again.
If you obscure the applet by moving another window on top of it, and then bring it back to the foreground by moving the window away again, you'll find that the applet has not been started or stopped again, but that it has been painted again. Table 5-1 lists some important Applet methods.
Method | Description |
---|---|
init() | Called once only, when the applet code is first loaded. |
start() | Called whenever a Web page containing the applet becomes active in the Web browser |
stop() | Called whenever a Web page containing the applet is no longer active in the Web browser |
destroy() | Called when the applet is explicitly killed |
paint(Graphics g) | Called when applet needs to redraw its graphics window |
Security concerns constrain applet design in some respects. Most notably applets are unable to access a user's local hard drive in any fashion. Thus, any large chunks of data needed by your applets have to be retrieved from a file-serving computer via a network, as we explain in the next section.
Getting Resources
One of the things that has made the World Wide Web so successful is the ease with which authors can add pictures and sound to their Web pages simply by including, in the page's HTML code, the location of the image or sound clip they want to use. It is just as easy and much more powerful to do this using Java. HTML is a document description language; Java is a bona fide programming language. Your Java applets could use images as graphical icons or sprites in an arcade-style game. The following Java applet grabs a picture and a sound clip from the network and displays them:
Example 5-3: Web-capable applet.
import java.applet.*; import java.awt.*; import java.net.*; public class WebApplet extends Applet { private Image myImage; private AudioClip mySound; private URL ImageURL; private URL SoundURL; public void init() { resize(250,250); try { //Bind URLs to the resources ImageURL = new URL(getDocumentBase(),"../images/redcup0trans.gif"); SoundURL = new URL(getDocumentBase(),"../sounds/sample.au"); } //Watch out for bad URLs catch (MalformedURLException e) {} //Download the picture myImage = getImage(ImageURL); //Download the audio clip mySound = getAudioClip(SoundURL); } public void start() { //Start sound playing in a loop mySound.loop(); } public void stop() { //Stop playing sound mySound.stop(); } public void paint(Graphics g) { //Paint the image g.drawImage(myImage,0,0,this); } }
As you read through this code, you'll see references to three classes that may be unfamiliar to you: java.awt.Image, java.applet.AudioClip, and java.net.URL. These classes, like most classes defined in the Java API, do more or less what their names imply.
Image
The Image class defines a simple, generic, two-dimensional graphics image. The Graphics class (used by the paint method) can draw Images with the drawImage method, as shown in the following example:
Image myImage; myImage = createImage(50,50); g.drawImage(myImage,0,0,this);
The createImage method is defined for the java.awt.Component class, a parent of the Applet class (direct parent of the Applet class is the Panel class). The createImage method can take two integers as arguments and create an empty new instance of the Image class with the specified size.
The drawImage method takes four parameters: the Image itself, the X and Y coordinates of the Image's new location in the Graphics window and an ImageObserver. We'll describe the ImageObserver class in detail later in Chapter 9, "Graphics & Images," but for now be sure to always use your applet itself as the ImageObserver when using drawImage.
When you need to explicitly pass your applet as a parameter, you can use the this keyword, discussed in Chapter 4, "Syntax & Semantics."
The Applet class uses the getImage(URL) method to grab images from the network. This method is really implemented by the AppletContext (see the sidebar "The AppletContext Interface" earlier in this chapter). Consequently, Java applets can import images of any graphics format that the context supplied by the Web browser can understand. The most common formats are the Graphics Interchange Format (GIF) and Joint Photographic Experts Group (JPEG) format. Images may take a while to download, but we can go ahead and draw the Image in our applet; it will just take a while for the picture to actually appear. If you need finer control over the Images, you can use the Mediatracker class, which is discussed in Chapter 9.
AudioClip
The java.applet.AudioClip class is a high-level representation of a clip of audio data. Three methods are defined in the class: play, loop, and stop. The applet class defines a getAudioClip method that, when given the URL of a sound clip, returns an AudioClip containing that sound. Like the getImage method, the getAudioClip method is actually implemented by the applet's context; so again, the applet is able to use any sound format that the Web browser directly supports. Here's a short example:
AudioClip mySound; mySound.play();
This code plays the sound represented by the given AudioClip. The Applet class defines a method, play(URL), which, when passed a URL that points to an audio clip, plays it. If the audio clip is missing, or in an unsupported format, nothing happens. AudioClips, like Images, can be used as soon as they are instantiated, but it may take some time to actually download and play the sound. While you can use the Mediatracker class to download pictures ahead of time for you, the Mediatracker does not yet support AudioClips.
URL
A URL, or Uniform Resource Locator is the complete address of an object on the World Wide Web (for example, http://www.vmedia.com/index.html is the address for the home page of the Ventana Online site). The Java language provides a separate class to handle URLs. An instance of the class represents an object on the Web. The URL class will be described completely in Chapter 14, "Networking With URLs," but you can go ahead and start using it now. The easiest way to create a URL object is to use the URL (String) constructor:
URL myObject; myObject = new URL("http://www.vmedia.com/index.html");
Unfortunately, this Java code is incomplete. If we tried to compile this code, the Java compiler would complain that we have failed to handle a MalformedURLException. URLs can be very complex, and it's easy to try to create a URL object with a string that looks like, but is not, a URL. If this happens, the URL constructor will fail and notify the applet that the string it tried to parse as a URL is a MalformedURL. We have to be prepared for this contingency and catch the error. The following code accomplishes this:
URL myObject; try { myObject = new URL("http://www.vmedia.com/index.html"); } catch (MalformedURLException e) { //Code here is run if URL is malformed }
We'll be covering exceptions in detail in Chapter 10, "Advanced Program Design," so don't worry if this is unclear. Just remember that when you create a new URL, you have to try to catch the exception. If you're absolutely sure your URL has the proper syntax, you needn't put any code between the second set of braces.
There is another important URL constructor that takes a URL and a String as parameters. The URL indicates a base absolute URL, and the String contains the path to the object relative to the base. For instance, if you supplied http://www.vmedia.com/ourbook/ as the URL and "images/picture.gif" as the String, the new URL would point to http://www.vmedia.com/ourbook/images/picture.gif. If you supplied "/otherbook/index.html" as the String, the new URL would point to http://www.vmedia.com/otherbook/index.html.
This constructor is useful in conjunction with the Applet class's getCodeBase method, which returns the URL of the Applet's class file. You can use getCodeBase and the relative URL constructor to create URLs to objects without specifying a host name. This is especially useful because untrusted applets are not allowed to open network connections to remote hosts, except to the Web server from which the class file was loaded (we'll discuss these restrictions more thoroughly in Chapter 13, "Networking With Sockets & Streams"). So, you need to install the resources you want your applet to use on the same Web server as the applet. When constructing URLs to these resources in your applet, it is easiest and safest to use the getCodeBase method with the relative URL constructor instead of using absolute URLs. Doing so also makes your applet easier to install on a new Web server.
You may have noticed that we have coded some data into our example applet-namely, the URLs of the image and the sound clip. If you are a professional programmer or a computer science professor, you probably noticed this and cringed-by most traditional programming standards, this is a bad thing to do. Whenever you want to change the image or sound clip used by this applet, you'll have to change the code and recompile the class. Fortunately Java gives you a way to fix this by allowing you to give your applets parameters at runtime that you can use to specify a different image or sound clip.
Getting Parameters
A feature of most good high-level languages is the ability to take arguments from the command line. Programmers use this capability to make it possible for programs to change their behavior based on user input, eliminating the need for a complicated user interface. But Java applets are not run from the command line; they are embedded inside HTML code, and so are their "command line" parameters. Consider the following Web page:
Example 5-4a: A Web page with parameters.
<HTML> <HEAD> <TITLE>Good Web Applet</TITLE> </HEAD> <BODY> <APPLET CODE="GoodWebApplet.class" WIDTH=250 HEIGHT=250> <PARAM NAME="IMAGE" VALUE="../images/sample.gif"> <PARAM NAME="SOUND" VALUE="../sounds/sample.au"> </APPLET>
We have embedded a variable named IMAGE with the value "../images/sample.gif" in the HTML code shown in Example 5-4a. The applet can access the IMAGE variable using the getParameter method, which takes the name of a parameter variable as its input and returns a String containing the variable's value. The process is the same for the SOUND parameter. All parameters are represented as Strings.
Chapter 6, "Discovering the Application Programming Interface," will teach you how to convert variables of type String to other types-a useful technique, if you want to get a number as a parameter.
Using this new functionality, we can rewrite the init method of our Web-capable applet to make it dataless:
Example 5-4b: A good Web-capable applet.
import java.applet.*; import java.awt.*; import java.net.*; public class GoodWebApplet extends Applet { private Image myImage; private AudioClip mySound; private URL ImageURL; private URL SoundURL; public void init() { String ImageParam; String SoundParam; resize(250,250); //Fill in from HTML code ImageParam = getParameter("IMAGE"); SoundParam = getParameter("SOUND"); try { //Use parameters to grab URLs ImageURL = new URL(ImageParam); SoundURL = new URL(SoundParam); } catch (MalformedURLException e) {} myImage = getImage(ImageURL); mySound = getAudioClip(SoundURL); } public void start() { //Start sound playing in a loop mySound.loop(); } public void stop() { //Stop playing sound mySound.stop(); } public void paint(Graphics g) { //Paint the image g.drawImage(myImage,0,0,this); } }
By allowing the author of the Web page, rather than the programmer to determine the locations of the picture and sound clip, we have made this applet much more useful. All the author has to do is change the VALUE tag of either parameter to change the image or sound clip used by the applet. When you have an applet feature you want to change frequently, or have some default applet behavior you'd like Web page authors to be able to modify easily you should use parameters.
As useful as they are, parameters allow applets to interact with users in a very narrow fashion only-the input is via a series of strings written down before the user starts the program. Suppose we want to change the applet's behavior while the applet is running-when the user moves the mouse, or presses a certain key? This capability is essential for writing complex Internet applications, and Java's event handling paradigm makes it easy to accomplish.
Handling User Events
Java applets employ the concept of events to handle real-time user interaction and changes in the runtime environment. Events are packets of information generated in response to certain user actions, such as moving the mouse or pressing a key on the keyboard. They also can be generated in response to modifications of the environment-for example, when an applet's window is obscured by another window. The runtime environment watches for events to occur and passes the event information on to another method called an event handler. An event handler is a method that is called when a particular event occurs. Many commonly used event handlers are predefined for the Applet class. By default, these handlers do nothing-to use them, just override the appropriate method with your own code. For instance:
public boolean mouseMove(Event evt, int x, int y) { //Code here is run when the mouse is moved return true; //The event has been handled }
Whenever the mouse is moved, the mouseMove method is called. It returns true to indicate that it has handled the event, and that no other object needs to worry about it.
Mouse Events
Three parameters are passed to the mouseMove event handler: the event itself, which is a class containing all of the information needed to uniquely identify an event, and the X and Y coordinates of the event-in this case, the new location of the mouse within the applet. Table 5-2 lists predefined event handlers.
Event Handler for Java 1.0 | Event Handler for Java 1.1+ | Description |
---|---|---|
mouseDown(Event,int,int) | processMouseEven(MouseEvent) | The mouse button is pushed. The integer arguments indicate the location of the mouse. |
mouseUp(Event,int,int) | processMouseEvent(MouseEvent) | The mouse button is released. |
mouseMove(Event,int,int) | processMouseMotionEvent(MouseEvent) | The mouse is moved |
mouseDrag(Event,int,int) | processMouseMotionEvent(MouseEvent) | The mouse is moved while the button is being held down. |
mouseEnter(Event,int,int) | processMouseEvent(MouseEvent) | The mouse enters the applet. |
mouseExit(Event,int,int) | processMouseEvent(MouseEvent) | The mouse leaves the applet. |
keyDown(Event,int) | processKeyEvent(KeyEvent) | A cursor or function key is pressed. The integer argument indicates the particular key (see Table 5-3). |
keyUp(Event,intt) | processKeyEvent(KeyEvent) | A cursor or function key is released. |
By overriding some of these predefined event handlers, we can write the Cursor Applet, listed below. The Cursor Applet paints an image that follows the mouse around the applet's window. The chasing behavior can be turned off and on by clicking the mouse button:
Example 5-5: Curser Applet.
import java.applet.*; import java.awt.*; import java.net.*; public class CursorApplet extends Applet { //The position of the mouse private int mouse_x, mouse_y; //Do we want to follow the mouse? private boolean Follow = true; private Image CursorImage; public void init() { mouse_x = 125; mouse_y = 125; resize(250,250); String CursorFile = getParameter("CURSORFILE"); try { URL CursorURL = new URL(CursorFile); CursorImage = getImage(CursorURL); } catch (MalformedURLException e) { CursorImage = createImage(0,0); } } public void paint(Graphics g) { //A simple border g.drawRect(0,0,249,249); //Draw cursor at the mouse's location g.drawImage(CursorImage,mouse_x,mouse_y,this); } public boolean mouseMove(Event evt, int x, int y) { if (Follow) { //Update our local mouse information mouse_x = x; mouse_y = y; //Redraw the graphics window repaint(); } return true; } public boolean mouseDown(Event evt, int x, int y) { //If it's not one thing... if (Follow) {Follow = false;} //It's another else {Follow = true;} return true; } }
When you check for mouse-down events, you can specify different handling depending on whether the user single-clicks or double-clicks. The Event class defines a variable, clickCount, which is set to 1 for single-clicks and 2 for double-clicks. It will, in fact, be set to the number of clicks the user manages to make before the Event is generated. In practice, only single- and doubleclicks are useful. The following code fragment illustrates the use of this variable:
public boolean mouseDown(Event evt, int x, int y) ( if (evt.clickCount==1) { //Single-click case } elseif (evt.clickCount==2) { //Double-click case } else { //Super-nimble-finger case } return true; }
This method is called when the mouse button is pressed. If the button is pressed once, the applet enters the single-click case; if the button is pressed twice, the applet enters the double-click case.
Keyboard Events
The keyUp and keyDown methods work in the same way the mouse event methods work, except that they are passed the key's identifier rather than the event's coordinates. A key's identifier is an integer that corresponds to that key. Normal typewriter keys have their ASCII values. Java supports many special keys as well, and these are shown in Table 5-3. These special keys are actually defined as static integer variables (constants) for the Event class. Chapter 6, "Discovering the Application Programming Interface," will show you how to convert integers to Strings if you want to display the keyboard input.
The following code fragment checks for arrow key presses:
public boolean keyDown(Event evt, int key) { switch(key) { case Event.UP: case Event.DOWN: case Event.LEFT: case Event.RIGHT: default: } return true; }
The keyDown method is called when a key is pressed. The switch statement differentiates between the four arrows and all other keys, and it calls the appropriate section of code for the key in question.
UP |
DOWN |
LEFT |
RIGHT |
PGUP |
PGDOWN |
HOME |
END |
F1-F12 |
Event Handling: What's Really Going On
Here's what is really going on when an event occurs. The AppletContext notices that an event has occurred inside the applet (see the sidebar "The AppletContext Interface" earlier in this chapter). The AppletContext creates a new instance of the Event class, fills in the appropriate parameters so the applet will know what kind of event has happened, and passes the new Event to the applet's handleEvent method. The Event class contains all of the information needed to uniquely identify an event; these event variables are listed in Table 5-4.
Event Variable | Description |
---|---|
public Object target | The component in which the event occurred - for applet writing, this generally the applet itself. |
public long when | The time at which the event happened. The timestamp is a long integer containing the number of milliseconds since 00:00:00, January 1, 1970, GMT. Java provides a method, java.lang.System.currentTimeMillis, that return this value. |
public int id | Type of event (see Table 5-6). |
public int x | The X-coordinate of the event. |
public int y | The Y-coordinate of the event. |
public int key | The key identifier (for special keys, see Table 5-3). |
public int modifiers | The state of masking keys. |
public Object arg | An optional argument. This is not used for mouse our keyboard events, but will be of great use when using the graphics widgets provided by Java API. These are described in Chapter 9, "Graphics & Images." |
The handleEvent method checks the type of event and calls the appropriate predefined event handler, passing it the relevant parameters. (A diagram of this process is shown in Figure 5-5.) handle mouseMove
Figure 5-5: The handleEvent method.
For example, when the mouse is moved, an Event of type MOUSE_MOVE is constructed and passed to the handleEvent method. This method notes that the Event is a MOUSE_MOVE and calls the mouseMove method, passing to it the Event and its location. It is in fact unnecessary to pass the Event's location as separate parameters because this information is encoded in the Event itself. This is done for the programmer's convenience.
You can override the handleEvent method if you want to be able to handle a large number of different events without overriding each individual method. This may also be useful if you want to handle sequences of events differently. If you do this, remember that the predefined event handlers will not be called unless you call them explicitly in your new handleEvent method. The following example calls a generic mouse event handler for mouse events and passes all other events to the runtime environment:
public boolean handleEvent(Event evt) switch (evt.id) { case Event.MOUSE MOUSE_DOWN: case Event.MOUSE MOUSE_UP: case Event.MOUSE MOUSE_MOVE: case Event.MOUSE MOUSE_DRAG: case Event.MOUSE MOUSE_ENTER: case Event.MOUSE MOUSE_EXIT: } }
You can generate events in your own code. To do so, simply construct a new instance of the Event class and post it to your applet (event constructors are listed in Table 5-5). Generating events might be useful for writing a macro system that incorporates mouse motion, for example. You could implement a drawing package that, after recording the user drawing a figure, could replicate the figure in other locations. The following code will make your applet think the mouse has moved to a new location:
Event FakeEvt; long time = System.currentTimeMillis(); FakeEvt = new Event(this,time,MOUSE_MOVE,new_x,new_y,0,0,null); postEvent(FakeEvt);
Event Constructor | Description |
---|---|
Event(Object,long,int,int,int,int,int,Object) | The target component, timestamp, event type, X-coordinate, Y-coordinate, key identifier, modifiers, and argument. |
Event(Object,long,int,int,int,int,int) | The target component, timestamp, event type, X-coordinate, Y-coordinate, key identifier, and modifiers. |
Event(Object,int,Object) | The target component, event type, X-coordinate, and argument. |
A complete list of the event types appears in Table 5-6. The types marked with an asterisk are irrelevant for applets. The SCROLL and LIST_ types are used in conjunction with user input components, which will be covered in Chapter 7, "Basic User Interface."
Event types |
---|
WINDOW_DESTROY |
WINDOW_EXPOSE |
WINDOW_ICONIFY |
WINDOW_DEICONIFY |
WINDOW_MOVED |
KEY_PRESS |
KEY_RELEASE |
KEY_ACTION |
KEY_ACTION_RELEASE |
MOUSE_DOWN |
MOUSE_UP |
MOUSE_MOVE |
MOUSE_DRAG |
MOUSE_ENTER |
MOUSE_EXIT |
SCROLL_LINE_UP |
SCROLL_LINE_DOWN |
SCROLL_PAGE_UP |
SCROLL_PAGE_DOWN |
SCROLL_PAGE_ABSOLUTE |
LIST_SELECT |
LIST_DESELECT |
ACTION_EVENT |
LOAD_FILE* |
SAVE_FILE* |
GOT_FOCUS |
LOST_FOCUS |
As you can probably guess, much of the code in a good Java applet will be written for methods that run in response to events. But suppose we want some sequence of events to run independently of other events? Specifically, suppose we want our applet to display an animated figure and respond to events at the same time? If we start a loop to display the animation in any of our methods, the method will never complete and the applet will be stuck. Fortunately, Java provides an elegant way out of this trap: Threads.
Animation With Threads
Imagine your runtime environment as an office, with a single worker scurrying around inside. This worker is in charge of running the applet's methods in response to various events. Our worker is very methodical; it must wait until each task is complete before going on to the next one. Suppose we want to give the worker a method to run that is so complex or repetitive that it would preclude the worker from doing anything else? The solution in this case is simple-hire a new worker to do the timeconsuming job. The new worker is a new Thread.
Threads are different from other classes in that once they are instantiated and begin running, they run independently in the runtime environment of the method that started them. In this respect, they resemble processes in multi-processing operating systems like UNIX. When most methods are called, the program waits until the method has been completely executed before continuing. But when a thread's run method is called (usually when the thread is started), the originating method keeps executing while the thread's run method is running. Figure 5-6 contains flowcharts for a single Thread and for two Threads.
Figure 5-6: Thread flowchart.
You can use threads in your Java programs by defining extensions of the Thread class. We'll describe this process in detail in Chapter 11, "Advanced Threading." But for now we'll explain how to allow your applet to use threads. This technique will be useful if, for instance, your applet displays an animated image, but you want the apptet to do other things while the animation is playing.
The Runnable Interface
Since the Java language does not allow for multiple inheritance, as discussed in Chapter 3, "Object Orientation in Java," your applet cannot directly extend both the Applet and the Thread classes. You can allow your applet to have a method that runs inside of a thread by using the Runnable fnterface. This interface, like the Thread class, is part of the java.lang package. You can use it this way:
class ThreadedApplet extends Applet implements Runnable { }
The Runnable interface tells the compiler that this class will define a run method, and that this method should be executed inside of a thread. The run method is controlled by an instance of the Thread class constricted on the applet itself. Here is how you would use this in practice:
class ThreadedApplet extends Applet implements Runnable { private Thread engine = null; //This will be our thread public void init() { engine = new Thread(this); //This thread now controls our run method } public void start() { engine.start(); //This starts the run method } public void stop() { if (engine!=null && engine.isAlive()) //If we need to, engine.stop(); //Stop the run method } } public void run() { while (engine.isAlive()) { //code here runs until applet stops } } }
When the applet is initialized, it creates a new thread bound to the applet. When the applet starts, it starts the thread running. The thread executes the code inside the loop in the run method until the applet is stopped.
SimpleThread Methods
The Thread class defines many methods to help you control your threads. We'll cover these in detail in Chapter 11, "Advanced Threading," but Table 5-7 briefly describes the most important methods to remember.
Thread Method | Description |
---|---|
isAlive() | Returns a boolean variable indicating whether a thread is alive or not. |
sleep(long) | Asks the thread to sleep for the specified number of milliseconds. This method will throw an InterruptedException if it receives an interrupt signal from another thread. Interrupts are discussed in Chapter 11, "Multi-Threaded Applets." |
start() | Starts the thread running. |
stop() | Stops the thread running. |
suspend() | Temporally pauses the thread. |
resume() | Resumes running after suspension. |
Example 5-6a: Animated Cursor Applet.
import java.applet.*; import java.awt.*; import java.net.*; //This class has a thread run method //and will control it through a Thread //instance constructed on this applet public class AnimatedCursorApplet extends Applet implements Runnable { private int mouse_x, mouse_y; //Array of animation images private Image CursorImages[]; //Index of the current image private int CursorIndex = 0; //This Thread controls the run method private Thread anim = null; //Is the animation paused? private boolean paused = false; public void init() { resize(250,250); //Bind the thread instance to the applet anim = new Thread(this); mouse_x = 125; mouse_y = 125; //We assume 5 images--for now CursorImages = new Image[5]; int i; String CursorParam; URL CursorURL; //Fill the image array for (i=0; i<5; i++) { CursorParam = getParameter("CURSORFILE"+i); try { CursorURL = new URL(CursorParam); CursorImages[i] = getImage(CursorURL); } catch (MalformedURLException e) { //Create blank if URL is bad CursorImages[i] = createImage(0,0); } } } public void start() { //Start the run method anim.start(); } public void stop() { if (anim!=null && anim.isAlive()) { //Stop the run method if necessary anim.stop(); } } public void paint(Graphics g) { int px, py; //Set Cursor to the current image Image Cursor = CursorImages[CursorIndex]; g.drawRect(0,0,249,249); //Center the image px = mouse_x - Cursor.getWidth(this)/2; py = mouse_y - Cursor.getHeight(this)/2; g.drawImage(Cursor,px,py,this); } public boolean mouseMove(Event evt, int x, int y) { mouse_x = x; mouse_y = y; return true; } public boolean mouseDown(Event evt, int x, int y) { //If paused, restart the run method if (paused) { anim.resume(); paused = false; } //Otherwise, pause the run method else { anim.suspend(); paused = true; } return true; } public void run() { while (anim!=null) { try { //Suspend for 50 milliseconds anim.sleep(50); } //In case something wakes us up catch (InterruptedException e) {} //Move along to the next image CursorIndex = CursorIndex + 1; if (CursorIndex==5) { //Start again at the beginning CursorIndex = 0; } repaint(); } } }
Example 5-6b: Animated Cursor Applet Web page.
Figure 5-7: The animated cursor<HTML><HEAD> <TITLE>Animated Cursor Applet</TITLE> </HEAD> <BODY> <APPLET CODE="AnimatedCursorApplet.class" HEIGHT=250 WIDTH=250> <PARAM NAME="CURSORFILEO" VALUE="../images/anim0.gif"> <PARAM NAME="CURSORFILE1" VALUE="../images/animl.gif"> <PARAM NAME="CURSORFILE2" VALUE="../images/anim2.gif"> <PARAM NAME="CURSORFILE3" VALUE="../images/anim3.gif"> <PARAM NAME="CURSORFILE4" VALUE="../images/anim4.gif"> </APPLET></BODY></HTML>
Removing Flicker
Unless you are working on an amazingly superfast graphics workstation, when you view this applet you will notice a flickering that is somewhat annoying. This effect is a result of the applet's efforts to paint the screen faster than the screen can update itself. The classic solution to this problem is a method called double buffering. Figure 5-8 contains a diagram of this procedure.
Figure 5-8: Double buffering.
When the runtime environment needs to repaint the graphics window-for example, when a window has been obscured, or when the applet has requested that the window be repainted-the applet's context calls the applet's update method and passes it the graphics window's Graphics object. An Applet can request a repaint of its graphics output with the repaint method. The repaint method calls the update method as soon as possible. If the applet calls for another repaint before the update method has been called from the first repaint, the applet is still only updated once. The update method, as defined on the default Applet, simply passes on its Graphics object to the paint method.
Applets can define an offscreen canvas to paint to instead of painting directly to the runtime environment's graphics window. The canvas is an Image; the applet can paint onto the Image by using the Image's getGraphics method. This method returns a Graphics object bound to the Image. The applet can paint to the Image as fast as it cares to. The applet's update method draws the Image onto the applet context's Graphics object instead of allowing the applet to draw onto it. The net effect is to remove flickering caused by overly fast graphics output from an applet.
There is one significant difference between painting to the runtime environment's Graphics object and painting to an Image's Graphics object. The runtime environment's graphics window is cleared and must be repainted in its entirety every time it is updated. An Image will retain things painted onto it unless they are explicitly painted over. If you want to use an Image to paint an animated figure, you must repaint the background around the figure each time you update it, otherwise your figure will leave trails as it moves.
We can add double buffering to our Cursor Applet by using an offscreen Image:
Example 5-7: Flicker-Free Animated Cursor Appplet.
import java.awt.*; import java.applet.*; import java.net.*; public class AnimatedCursorApplet extends Applet implements Runnable { private int mouse_x, mouse_y; private Image CursorImages[]; private int CursorIndex = 0; private Thread anim = null; private boolean paused = false; private Image OffscreenImage; //This will be our offscreen canvas private Graphics OffscreenGraphics; //And this will be our interface to it public void init() { resize(250,250); OffscreenImage = createImage(250,250); //Make a new image the same size as us OffscreenGraphics = OffscreenImage.getGraphics(); //Bind our interface to the Image anim = new Thread(this); mouse_x = 125; mouse_y = 125; CursorImages = new Image[10]; int i; String CursorParam; URL CursorURL; for (i=0; i<5; i++) { CursorParam = getParameter("CURSORFILE"+i); try { CursorURL = new URL(CursorParam); CursorImages[i] = getImage(CursorURL); } catch (MalformedURLException e) { CursorImages[i] = createImage(0,0); } } } public void start() { anim.start(); } public void stop() { if (anim!=null && anim.isAlive()) { anim.stop(); } } public synchronized void update(Graphics g) { paint(OffscreenGraphics); //Do applet painting to our canvas g.drawImage(OffscreenImage,0,0,this); //Paint the real graphics window } public void paint(Graphics g) { int px, py; Image Cursor = CursorImages[CursorIndex]; g.setColor(Color.white); g.fillRect(0,0,249,249); //Paint over old image g.setColor(Color.black); g.drawRect(0,0,249,249); //Draw the border px = mouse_x - Cursor.getWidth(this)/2; py = mouse_y - Cursor.getHeight(this)/2; g.drawImage(Cursor,px,py,this); } public boolean mouseMove(Event evt, int x, int y) { mouse_x = x; mouse_y = y; return true; } public boolean mouseDown(Event evt, int x, int y) { if (paused) { anim.resume(); paused = false; } else { anim.suspend(); paused = true; } return true; } public void run() { while (anim!=null) { try { anim.sleep(50); } catch (InterruptedException e) {} CursorIndex = CursorIndex + 1; if (CursorIndex==5) { CursorIndex = 0; } repaint(); } } }
Moving On
After reading this chapter, you should be able to code multimedia Java applets. You can now get picture and sound data from the network, get runtime parameters from HTML code, handle user interface events, and bring your applet to life with threads. These methods are necessary for creating applets that incorporate multimedia, but they are not sufficient for writing applets that can interact with the user at a high level, who uses text and other input devices.
By now you should have a good feel for the use of the Java language. The following chapters will discuss the rich hierarchy of classes the Java API provides. These classes and interfaces add useful functionality to the Java language, providing an elegant implementation of, among other things, many of the advanced data structures used by programmers and a complete graphical windowing toolkit. They also are a good example of the power of reusable, extensible objects.