Media Player

Intro

At some point while developing an app, you may find the need to play audio and/or video files.  These files can be prepackaged as part of your app's distribution, or you may stream media from a network resource.  In all cases, in order to accomplish this you'll need to use the MediaPlayer.  The MediaPlayer class is capable of handling many common types of media, including:

For a full list, see here: Supported Media Formats.

This lesson will show you how to play audio files packaged with your app by using the MediaPlayer.  Furthermore, it will demonstrate how to play streaming video from a network resource using a VideoView. 

MediaPlayer States

When choosing to play media in your app, there are several considerations that need to be made.  What if someone calls in the middle playback?  What happens when your device goes into standby mode to save battery life?  How does you app respond to orientation changes during playback?  This is by no means an exhaustive list.  I'm sure you can imagine several more scenarios that could occur while you're listening to music or playing a video.  All of these are valid concerns, and should be addressed accordingly.  In order to do so, it's important that you have a comprehensive understanding of the MediaPlayer life cycle, represented in the following state diagram (courtesy of Google):

Take a few minutes to review the state diagram before moving on.  The blue ovals represent the possible states of your MediaPlayer, and the directional lines between states represent transitions, and the action that cause each respective transition in the form of method calls (are you having flashbacks from your Theory class yet?).

Audio

You have a couple options when it comes to where you place your audio files in the app structure.  You can choose to place the audio files in the assets folder, like you did with images in the previous lesson.  This lesson introduces you to another folder, called raw, which is a subdirectory of the resources folder.  Files placed in both the assets and raw folder are left uncompressed when your app is built, but there are a couple important differences between the two.  First, because the raw folder is a subdirectory of the res folder, you are not allowed to alter the directory structure or create you own sub-folders like you can in the assets folder.  But since the raw folder is a resource folder, you are able to use the Android resource managing system, meaning that you can reference your file using by its integer id using the R format (as you do with drawables and layout files).  The result is faster file loading because we eliminate the need to search for a file using it name String.  

In its simplest form, we can use the MediaPlayer to play an audio file stored in our app's resources > raw folder like this: 

MediaPlayer mediaPlayer = MediaPlayer.create(getBaseContext(), R.raw.sound_file_name);
mediaPlayer.start(); 

Notice that this example uses a method called create(), which isn't shown on the diagram above.  Have no fear, this is for your own benefit.  The create() method is a helper method that encompasses both the setDataSource() and prepare() methods of the MediaPlayer for you.  Once you call create, the MediaPlayer is ready to go, and you file can be played by simply calling start().

IMPORTANT: Do NOT call prepare() after making a call to create()

 

It's important that your app releases the MediaPlayer when it is no longer needed.  Failure to do so could result in an Exception if all system resources are exhausted.  The release() method call should be placed in the proper Activity lifecycle method as appropriate for your application (e.g. onStop() or onDestroy()).

mediaPlayer.release();
mediaPlayer = null;

Notice that in the above example the MediaPlayer object is nullified as well.  It should be recreated in the corresponding lifecycle method (onStart(), onResume(), etc.).

There are methods associated with each MediaPlayer lifecycle transition, including:

Task: Create a new application in Android Studio.  Place the following sound file in the res > raw folder: sound_file_1.mp3.  In the app's main layout file, add buttons that, when pressed, start, pause and stop the audio file in a MediaPlayer.  Be sure to reference the state diagram!  

Think you got it figured out?  Here's my solution:

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <ImageButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@android:drawable/ic_media_play"
        android:onClick="playPlayer" />
    
    <ImageButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@android:drawable/ic_media_pause"
        android:onClick="pausePlayer" />
    
    <ImageButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@android:drawable/ic_menu_close_clear_cancel"
        android:onClick="stopPlayer" />
    
</LinearLayout>

MainActivity.java

public class MainActivity extends Activity {
	
	MediaPlayer mediaPlayer;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}
	
	@Override
	public void onStart(){
		super.onStart();
		initializePlayer();
	}
	
	@Override
	public void onStop(){
		super.onStop();
		destroyPlayer();
	}
	
	private void initializePlayer(){
		if(mediaPlayer == null){
		    mediaPlayer = MediaPlayer.create(getBaseContext(), 
                                      R.raw.sound_file_1);
		}
	}
	
	private void destroyPlayer(){
		if(mediaPlayer.isPlaying()){
			mediaPlayer.stop();
		}
		mediaPlayer.release();
		mediaPlayer = null;
	}
	
	public void playPlayer(View view){
		initializePlayer();
		mediaPlayer.start(); 
	}
	
	public void pausePlayer(View view){
		mediaPlayer.pause();
	}
	
	public void stopPlayer(View view){
		destroyPlayer();
	}

}

Streaming Media

You don't have to package your media files with your app.  Actually, many apps stream their media directly from the internet.  In order to do this, you have to make a few changes to the way we create the MediaPlayer.  Because the file needs to be fetched and loaded from the internet, we want that behavior to happen in the background.  In order for this to happen, the audio stream must be prepared asynchronously by using the prepareAsync() method in place of prepare().  This also means that instead of using the create() method, we must go through the steps of setting the media data source and preparing the player (both of which were handled previously by the create method).  You must also tell the MediaPlayer what type of the stream it should expect.  Finally, because we are retrieving the steam asynchronously, we need some way of knowing when the stream is ready to be played.  To do this, we must use an OnPreparedListener to capture the "prepared" event.  In the below example, the initializePlayer() method shown above was modified to enable streaming audio.  The source used is the 24 hour news feed from National Public Radio:  

private void initializePlayer() {
    if (mediaPlayer == null) {
	mediaPlayer = new MediaPlayer();

        // tell the player that we're playing a stream
	mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
	mediaPlayer.setOnPreparedListener(new OnPreparedListener(){

		@Override
		public void onPrepared(MediaPlayer mp) {
			Log.i("PEPIN", "Stream Prepared");
			mediaPlayer.start();
		}
				
	});

	try {
                // these two methods were previously handled by create()
                // notice the data source is a URL as opposed to a resource
                String url = "http://nprdmp.ic.llnwd.net/stream/nprdmp_live01_mp3";
		mediaPlayer.setDataSource(url);

                // do the work in another thread
		mediaPlayer.prepareAsync();

	} catch (Exception e) {
		Log.e("PEPIN", "initializePlayer() Exception");
		e.printStackTrace();
	}
    } else {
	mediaPlayer.start();
    }
}

Remember, we're using the internet...don't forget to set the proper permissions in the manifest file:

<uses-permission android:name="android.permission.INTERNET" />