Programming Java 2 Micro Edition for Symbian OS 2004 (779882), страница 34
Текст из файла (страница 34)
However, you will normally want the additional flexibilityprovided by working with a Player (configured for tones) and aToneControl (see Section 3.4.1.7).The final topic for this brief introduction to the Mobile Media API is thePlayerListener interface. This provides a playerUpdate() methodfor receiving asynchronous events from a Player. Any user-defined classmay implement this interface and then register the PlayerListenerusing the addPlayerListener() method. The PlayerListenerlistens for a range of standard pre-defined events including STARTED,STOPPED and END_OF_MEDIA, with obvious meanings.
For a listing ofall the standard events refer to the MMAPI specification.3.4.1.4 Working with AudioIn this section we shall demonstrate how to play audio files. We shallillustrate the section with code from a simple Audio Player MIDlet (seeFigure 3.24).Figure 3.24 The Audio Player MIDlet running on a Nokia Series 60 phone.OPTIONAL J2ME APIS IN THE JTWI165javax.microedition.lcdui.FormInitialViewPlayerViewMIDletControllerAudioPlayerjavax.microedition.media.control.VolumeControljavax.microedition.media.PlayerFigure 3.25 UML class diagram of the Audio Player MIDlet.The Audio Player MIDlet consists of four classes: a controller, MIDletController; two views, InitialView and PlayerView; and theplayer, AudioPlayer. We shall look in detail only at the controllerand the player, which contain all the information of interest from theviewpoint of MMAPI.
A UML class diagram of the Audio Player MIDletis shown in Figure 3.25.The AudioPlayer ClassThe AudioPlayer class performs the actual playing of the audio fileand we shall consider it first. The key signatures are shown below:import javax.microedition.media.*;import javax.microedition.media.control.*;import java.io.*;// Encapsulates the audio Playerpublic class AudioPlayer implements Runnable {privateprivateprivateprivateMIDletController controller;Player player;VolumeControl volumeControl;String url;public AudioPlayer(MIDletController controller){...}public void initializeAudio(String url){...}public void run(){...}166MIDP 2.0 AND THE JTWIpublic void startPlayer(){...}public void stopPlayer(){...}public void closePlayer(){...}}We shall first look at the constructor.public AudioPlayer(MIDletController controller){this.controller = controller;}This simply takes a reference to the MIDletController object,allowing the AudioPlayer to make callbacks to the controller.We initialize the Player in a separate thread launched from theinitializeAudio() method shown below.
Generally it is good practice to put the (potentially time-consuming) initialization into a separatethread, performing it in the background to reduce latency.public void initializeAudio(String url){this.url = url;Thread initializer = new Thread(this);initializer.start();}Another reason for performing the initialization in a separate threadis so that it is possible to update a progress gauge, giving the uservaluable feedback as to how the audio acquisition is proceeding.
Theactual initialization takes place in the run() method mandated by theRunnable Interface.public void run(){try {player = Manager.createPlayer(url);controller.updateProgressGauge();player.addPlayerListener(controller);player.realize();controller.updateProgressGauge();player.prefetch();controller.updateProgressGauge();volumeControl =(VolumeControl)player.getControl("VolumeControl");if (volumeControl != null) {volumeControl.setLevel(50);}else {controller.removeVolumeControl();}OPTIONAL J2ME APIS IN THE JTWI167startPlayer();} catch (IOException ioe) {controller.showAlert("Unable to connect to resource",ioe.getMessage());closePlayer();} catch (MediaException me) {controller.showAlert("Unable to create player",me.getMessage());closePlayer();}}A Player is created and a PlayerListener registered with it.
Thecontroller reference serves two purposes: first to facilitate callbacksto the UI indicating the progress of the initialization; and, secondly,to act as the PlayerListener which will be notified of Playerevents. The Player is then moved through its states. In this example,we obtain a VolumeControl (providing the implementation supportsthis feature) although it is not essential for simple audio playback.Intuitively, a VolumeControl provides control over the audio playbackvolume, including a mute option. The volume range provided by aVolumeControl ranges from 0–100. Here we set the volume levelmidway.
We then start the Player.Our AudioPlayer class also contains some simple service methods.The startPlayer() method is used to start or re-start an initializedaudio player.public void replay(){try{player.start();} catch (MediaException me) {controller.showAlert("MediaException thrown",me.getMessage());}}The setVolume() method is used to change the volume level of theaudio playback via the VolumeControl:public void setVolume(int level){if (volumeControl != null) {volumeControl.setLevel(level);}}The closePlayer() method is called to release all resources associated with the audio Player by means of a call to close(). Additionally,168MIDP 2.0 AND THE JTWIthe closePlayer() method sets to null the references to the Playerand VideoControl instances to facilitate garbage collection.public void closePlayer(){if (player != null){player.close();}player = null;volumeControl = null;}The MIDletController ClassThe MIDletController sets up the instances of InitialView andPlayerView (see Figure 3.24).
The InitialView instance providesa TextField for the audio file URL and a Gauge to indicate theprogress of the audio player initialization, as well as controls to startplayback or exit the application. The PlayerView instance providesa status StringItem and an interactive volume indicator (providingthe implementation supports a VolumeControl), allowing the user toadjust the playback volume, plus controls to close the player or replay theaudio content. The MIDletController constructs the AudioPlayerinstance in response to user commands from the InitialView.
TheMIDletController receives callbacks from the AudioPlayer, viathe playerUpdate() method of PlayerListener, and updates theInitialView progress gauge as the player moves through its initialization states. The MIDletController also receives callbacks from thePlayerView via the ItemStateChanged() method of ItemListener when the user adjusts the volume slider and passes these callsto the AudioPlayer. The key signatures of the MIDletControllerclass are shown below:importimportimportimportpublicjavax.microedition.lcdui.*;javax.microedition.midlet.*;javax.microedition.media.*;javax.microedition.media.control.*;class MIDletController extends MIDlet implementsCommandListener, PlayerListener, ItemStateListener {private Display display;private Command exitCommand, playCommand, backCommand,replayCommand;private InitialView initialView;private PlayerView playerView;private AudioPlayer audioPlayer;public MIDletController() {...}public void startApp(){...}public void pauseApp(){...}public void destroyApp(boolean unconditional){...}OPTIONAL J2ME APIS IN THE JTWI169public void commandAction(Command c, Displayable s){...}public void playerUpdate(Player p, String event,Object eventData) {...}public void itemStateChanged(Item item){...}public void updateProgressGauge(){...}public void removeVolumeControl() {...}public void showAlert(String title, String message){...}public void releaseResources() {...}}Let’s first consider the MIDletController constructor:public MIDletController() {display = Display.getDisplay(this);initialView = new InitialView(this);exitCommand = new Command("Exit", Command.EXIT, 2);playCommand = new Command("Play", Command.SCREEN, 1);initialView.addCommand(exitCommand);playerView = new PlayerView(this);audioPlayer = new AudioPlayer(this);}The constructor creates instances of the main UI classes InitialViewand PlayerView plus their associated Commands.
It also creates theAudioPlayer instance which encapsulates the audio Player.Now let’s look at the MIDlet lifecycle methods. The startApp()method sets InitialView as the current screen and adds the ‘‘Play’’Command to it:public void startApp(){display.setCurrent(initialView);initialView.addCommand(playCommand);}This latter measure is to ensure that the user can continue to usethe application after resuming from the PAUSED state. If the InitialView already has a playCommand associated with it then addCommand(playCommand) does nothing.The pauseApp() method releases all resources associated with theaudio Player and removes the progress gauge (if any) from the InitialView:public void pauseApp(){releaseResources();170MIDP 2.0 AND THE JTWIinitialView.removeProgressGauge();}Again, this latter step is to ensure the MIDlet resumes from a pause inthe correct state.The destroyApp() method simply releases all resources associatedwith the audio Player:public void destroyApp(boolean unconditional){releaseResources();}Now let’s look at the commandAction() method mandated by theCommandListener interface:public void commandAction(Command c, Displayable s){if(c == playCommand){initialView.removeCommand(playCommand);initialView.addProgressGauge();audioPlayer.initializePlayer(initialView.getURL());}else if(c == replayCommand){playerView.removeCommand(replayCommand);audioPlayer.startPlayer();}else if(c == backCommand){releaseResources();initialView.removeProgressGauge();display.setCurrent(initialView);initialView.addCommand(playCommand);playerView.removeCommand(replayCommand);}else if(c == exitCommand){releaseResources();notifyDestroyed();}}When the user selects the ‘‘Play’’ Command, the initializePlayer() method is called with the URL of the audio content(obtained from the InitialView) as an argument.
In addition, a progressgauge is added to the InitialView to give visual feedback to theuser as to how the audio acquisition is progressing. The ‘‘Play’’ Command is removed from the InitialView to avoid multiple Playersbeing created.The ‘‘Re-play’’ Command is used to replay a given audio file (forwhich a Player has already been created) once its end of media hasbeen reached.The ‘‘Back’’ Command allows the user to abandon the current Playersession and return to the InitialView, either to play a different audiofile, or quit the application.OPTIONAL J2ME APIS IN THE JTWI171Finally, when the user selects the ‘‘Exit’’ Command the MIDlet releasesall resources associated with the Player and calls notifyDestroyed()to indicate to the AMS that it has moved into the DESTROYED state andcan be reclaimed.Now let us consider the playerUpdate() method mandated by thePlayerListener interface:public void playerUpdate(Player p, String event, Object eventData) {if (event == PlayerListener.STARTED) {playerView.setStatus("Player started!");if(backCommand == null){backCommand = new Command("Back", Command.BACK, 1);playerView.addCommand(backCommand);}display.setCurrent(playerView);}//add "Replay" option when audio playback is finishedelse if (event == PlayerListener.END_OF_MEDIA){playerView.setStatus("End of Media, select 'Re-play' or 'Back'");if (replayCommand == null){replayCommand = new Command("Re-play", Command.SCREEN, 1);}playerView.addCommand(replayCommand);}else if (event == PlayerListener.VOLUME_CHANGED) {VolumeControl volumeControl = (VolumeControl)eventData;int currentLevel = volumeControl.getLevel();if (playerView.getVolumeLevel() != currentLevel) {playerView.setVolumeIndicator(currentLevel);}}}In this example, three types of PlayerListener event are processed:STARTED, END_OF_MEDIA and VOLUME_CHANGED.