In this series, we are creating a music player on Android using the
In the final installment of this series, we'll add user control over the playback and we'll also make sure the app will continue to function in various application states. Later, we will follow the series up with additional enhancements that you may wish to add, such as audio focus control, video and streaming media playback, and alternate ways of presenting the media files.
Extend the opening line of the class declaration to implement some interfaces we will use for music playback:
Eclipse will display an error over the class name. Hover over the error and choose Add unimplemented methods. We will add code to the methods in a few moments. The interfaces we are implementing will aid the process of interacting with the
Your class will also need the following additional imports:
We will pass the list of songs into the
Inside
Next, let's add a method to initialize the
Inside this method, we configure the music player by setting some of its properties as shown below:
The wake lock will let playback continue when the device becomes idle and we set the stream type to music. Set the class as listener for (1) when the
Notice that these correspond to the interfaces we implemented. We will be adding code to the
We will call this method later in the tutorial. This will form part of the interaction between the
We will also access this from the
And you'll also need to declare three new instance variables:
We are going to play the music in the
The callback methods will inform the class when the
Don't forget to alter the package and class names to suit your own if necessary.
When the
Return to your
Now amend the
Add the
This will execute when the user exits the app, at which point we will stop the service.
Inside the method, start by resetting the
Next, get the song from the list, extract the ID for it using its
We can now try setting this URI as the data source for the
After the
In order for the user to select songs, we also need a method in the
We will call this when the user picks a song from the list.
We set the song position as the tag for each item in the list view when we defined the
Before you run your app, implement the end button we added to the main menu. In your main
Inside the method, add a switch statement for the actions:
We will implement the shuffle function in the next tutorial. For the end button, we stop the
When you run the app at this point, you will be able to play songs by selecting them from the list and you can also exit the app using the end button.
MediaPlayer
and MediaController
classes. In the first part, we created the app and prepared the user interface for playback. We presented the list of songs on the user device and specified a method to execute when the user makes a selection. In this part of the series, we will implement a Service
class to execute music playback continuously, even when the user is not directly interacting with the application.Introduction
We will need the app to bind to the music playingService
in order to interact with playback, so you will learn some of the basic aspects of the Service
life cycle in this tutorial if you haven't explored them before. Here is a preview of the final result that we're working towards:In the final installment of this series, we'll add user control over the playback and we'll also make sure the app will continue to function in various application states. Later, we will follow the series up with additional enhancements that you may wish to add, such as audio focus control, video and streaming media playback, and alternate ways of presenting the media files.
1. Create a Service
Step 1
Add a new class to your app, naming it MusicService or another name of your choice. Make sure it matches the name you listed in the Manifest. When creating the class in Eclipse, chooseandroid.app.Service
as its superclass. Eclipse should enter an outline:1 2 3 4 5 6 7 8 9 | public class MusicService extends Service { @Override public IBinder onBind(Intent arg0) { // TODO Auto-generated method stub return null ; } } |
1 2 3 | public class MusicService extends Service implements MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener { |
MediaPlayer
class.Your class will also need the following additional imports:
1 2 3 4 5 6 7 8 | import java.util.ArrayList; import android.content.ContentUris; import android.media.AudioManager; import android.media.MediaPlayer; import android.net.Uri; import android.os.Binder; import android.os.PowerManager; import android.util.Log; |
Step 2
Add the following instance variables to the newService
class:1 2 3 4 5 6 | //media player private MediaPlayer player; //song list private ArrayList<Song> songs; //current position private int songPosn; |
Service
class, playing from it using the MediaPlayer
class and keeping track of the position of the current song using the songPosn
instance variable. Now, implement the onCreate
method for the Service
:1 2 3 | public void onCreate(){ //create the service } |
onCreate
, call the superclass method, instantiating the position and MediaPlayer
instance variables:1 2 3 4 5 6 | //create the service super .onCreate(); //initialize position songPosn= 0 ; //create player player = new MediaPlayer(); |
MediaPlayer
class, after the onCreate
method:1 2 3 | public void initMusicPlayer(){ //set player properties } |
1 2 3 | player.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK); player.setAudioStreamType(AudioManager.STREAM_MUSIC); |
MediaPlayer
instance is prepared, (2) when a song has completed playback, and when (3) an error is thrown:1 2 3 | player.setOnPreparedListener( this ); player.setOnCompletionListener( this ); player.setOnErrorListener( this ); |
onPrepared
, onCompletion
, and onError
methods to respond to these events. Back in onCreate
, invoke initMusicPlayer
:1 | initMusicPlayer(); |
Step 3
It's time to add a method to theService
class to pass the list of songs from the Activity
:1 2 3 | public void setList(ArrayList<Song> theSongs){ songs=theSongs; } |
Activity
and Service
classes, for which we also need a Binder
instance. Add the following snippet to the Service
class after the setList
method:1 2 3 4 5 | public class MusicBinder extends Binder { MusicService getService() { return MusicService. this ; } } |
Activity
class.2. Start the Service
Step 1
Back in your app's mainActivity
class, you will need to add the following additional imports:1 2 3 4 5 6 7 | import android.os.IBinder; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.view.MenuItem; import android.view.View; |
1 2 3 | private MusicService musicSrv; private Intent playIntent; private boolean musicBound= false ; |
Service
class, but control it from the Activity
class, where the application's user interface operates. To accomplish this, we will have to bind to the Service
class. The above instance variables represent the Service
class and Intent
, as well as a flag to keep track of whether the Activity
class is bound to the Service
class or not. Add the following to your Activity
class, after the onCreate
method:01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 | //connect to the service private ServiceConnection musicConnection = new ServiceConnection(){ @Override public void onServiceConnected(ComponentName name, IBinder service) { MusicBinder binder = (MusicBinder)service; //get service musicSrv = binder.getService(); //pass list musicSrv.setList(songList); musicBound = true ; } @Override public void onServiceDisconnected(ComponentName name) { musicBound = false ; } }; |
Activity
instance has successfully connected to the Service
instance. When that happens, we get a reference to the Service
instance so that the Activity
can interact with it. It starts by calling the method to pass the song list. We set the boolean flag to keep track of the binding status. You will need to import the Binder
class we added to the Service
class at the top of your Activity
class:1 | import com.example.musicplayer.MusicService.MusicBinder; |
Step 2
We will want to start theService
instance when the Activity
instance starts, so override the onStart
method:1 2 3 4 5 6 7 8 9 | @Override protected void onStart() { super .onStart(); if (playIntent== null ){ playIntent = new Intent( this , MusicService. class ); bindService(playIntent, musicConnection, Context.BIND_AUTO_CREATE); startService(playIntent); } } |
Activity
instance starts, we create the Intent
object if it doesn't exist yet, bind to it, and start it. Alter the code if you chose a different name for the Service
class. Notice that we use the connection object we created so that when the connection to the bound Service
instance is made, we pass the song list. We will also be able to interact with the Service
instance in order to control playback later.Return to your
Service
class to complete this binding process. Add an instance variable representing the inner Binder
class we added:1 | private final IBinder musicBind = new MusicBinder(); |
onBind
method to return this object:1 2 3 4 | @Override public IBinder onBind(Intent intent) { return musicBind; } |
onUnbind
method to release resources when the Service
instance is unbound:1 2 3 4 5 6 | @Override public boolean onUnbind(Intent intent){ player.stop(); player.release(); return false ; } |
3. Begin Playback
Step 1
Let's now set the app up to play a track. In yourService
class, add the following method:1 2 3 | public void playSong(){ //play a song } |
MediaPlayer
since we will also use this code when the user is playing subsequent songs:1 | player.reset(); |
Song
object, and model this as a URI:1 2 3 4 5 6 7 8 | //get song Song playSong = songs.get(songPosn); //get id long currSong = playSong.getID(); //set uri Uri trackUri = ContentUris.withAppendedId( android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, currSong); |
MediaPlayer
instance, but an exception may be thrown if an error pops up so we use a try/catch
block:1 2 3 4 5 6 | try { player.setDataSource(getApplicationContext(), trackUri); } catch (Exception e){ Log.e( "MUSIC SERVICE" , "Error setting data source" , e); } |
catch
block, complete the playSong
method by calling the asynchronous method of the MediaPlayer
to prepare it:1 | player.prepareAsync(); |
Step 2
When theMediaPlayer
is prepared, the onPrepared
method will be executed. Eclipse should have inserted it in your Service
class. Inside this method, start the playback:1 2 3 4 5 | @Override public void onPrepared(MediaPlayer mp) { //start playback mp.start(); } |
Service
class to set the current song. Add it now:1 2 3 | public void setSong( int songIndex){ songPosn=songIndex; } |
Step 3
Remember that we added anonClick
attribute to the layout for each item in the song list. Add that method to the main Activity
class:1 2 3 4 | public void songPicked(View view){ musicSrv.setSong(Integer.parseInt(view.getTag().toString())); musicSrv.playSong(); } |
Adapter
class. We retrieve it here and pass it to the Service
instance before calling the method to start the playback.Before you run your app, implement the end button we added to the main menu. In your main
Activity
class, add the method to respond to menu item selection:1 2 3 4 | @Override public boolean onOptionsItemSelected(MenuItem item) { //menu item selected } |
01 02 03 04 05 06 07 08 09 10 11 | switch (item.getItemId()) { case R.id.action_shuffle: //shuffle break ; case R.id.action_end: stopService(playIntent); musicSrv= null ; System.exit( 0 ); break ; } return super .onOptionsItemSelected(item); |
Service
instance and exit the app. Pressing the back button will not exit the app, since we will assume that the user wants playback to continue unless they select the end button. Use the same process if the the app is destroyed, overriding the activity's onDestroy
method:1 2 3 4 5 6 | @Override protected void onDestroy() { stopService(playIntent); musicSrv= null ; super .onDestroy(); } |
No comments:
Post a Comment