7

Wondering how next songs are played once app is closed as if playing an entire CD or playlist...

ThinkingStiff
  • 64,767
  • 30
  • 146
  • 239
Metallicraft
  • 2,211
  • 5
  • 31
  • 52

5 Answers5

15

The media player only plays one audio track. What media players do, is listen on the onCompletion event and play the next track.

The MediaPlayer is bound to the process, not the activity, so it keeps playing as long as the process runs. The activity might be paused or destroyed, but that won't affect the internal thread that MediaPlayer uses.

I'm building an audio player to learn Android, you can see the service that plays audio files here

edit

regarding the first comment: The service keeps running on the background and keeps running after you "exit" the application, because the lifecycle of the service and Activities are different.

In order to play the next track, the service registers a callback on the MediaPlayer so the service is informed when an audio stream completed. When the audio completes, the service cleans up the resources used by the MediaPlayer, by calling MediaPlayer.release(), and then creates a fresh new media player with the next audio track to play and registers itself to be notified again when that audio track completes, ad infinitum :).

The MediaPlayer class doesn't understand playlists, so the service is responsible for playing a track after the previous track completes.

In the AudioPlayer service I've created, an activity queues tracks in the AudioPlayer and the AudioPlayer is responsible for playing them in order.

I hope it's clear and again, if you have some time, please check the code of AudioPlayer service I've put above. It's not pure beauty, but it does its job.

Augusto
  • 28,839
  • 5
  • 58
  • 88
  • Is a service destroyed and recreated each time a new song is to play then? Or is a whole list of songs sent to one service to go thru? – Metallicraft Jul 12 '11 at 18:10
  • I created a project with a button, not a list like you have, to try out what you're doing. It's been very helpful. I've not done the service binding before. When I run it though and press Back, or open preferences , or even the home key, the audio stops. Is there something special to make it persist? – Metallicraft Jul 13 '11 at 02:31
  • Hmm, i have toasts that popup showing phone state...those are still firing though (even though the music stopped). So, that seems to mean the service is still running... – Metallicraft Jul 13 '11 at 03:00
3

You can create a Service to keep the MediaPlayer playing after your app either exits or is paused. To get the MediaPlayer to play consecutive tracks you can register an onCompletionListener that will decide which track to play next. Here is a simple example service that does this:

package edu.gvsu.cis.muzak;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.net.Uri;
import android.os.IBinder;
import android.util.Log;

public class MuzakService extends Service {

    private static final String DEBUG_TAG = "MuzakService";
    private MediaPlayer mp;
    private String[] tracks = {
            "http://freedownloads.last.fm/download/288181172/Nocturne.mp3",
            "http://freedownloads.last.fm/download/367924875/Behemoths%2BSternentanz.mp3",
            "http://freedownloads.last.fm/download/185193341/Snowflake%2BImpromptu.mp3",
            "http://freedownloads.last.fm/download/305596593/Prel%25C3%25BAdio.mp3",
            "http://freedownloads.last.fm/download/142005075/Piano%2BSonata%2B22%2B-%2Bmovement%2B2%2B%2528Beethoven%2529.mp3",
            "http://freedownloads.last.fm/download/106179902/Piano%2BSonata%2B%25231%2B-%2Bmovement%2B%25234%2B%2528Brahms%2529.mp3",

    };
    private int currentTrack = 0;

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(DEBUG_TAG, "In onCreate.");

        try {
            Uri file = Uri.parse(tracks[this.currentTrack]);
            mp = new MediaPlayer();
            mp.setDataSource(this, file);
            mp.prepare();
            mp.setOnCompletionListener(new OnCompletionListener() {

                @Override
                public void onCompletion(MediaPlayer mp) {
                    currentTrack = (currentTrack + 1) % tracks.length;
                    Uri nextTrack = Uri.parse(tracks[currentTrack]);
                    try {
                        mp.setDataSource(MuzakService.this,nextTrack);
                        mp.prepare();
                        mp.start();
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } 

                }

            });

        } catch (Exception e) { 
            Log.e(DEBUG_TAG, "Player failed", e);
        }
    }


    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        Log.d(DEBUG_TAG, "In onDestroy.");
        if(mp != null) {
            mp.stop();
        }
    }

    @Override
    public int onStartCommand(Intent intent,int flags, int startId) {
        super.onStart(intent, startId);
        Log.d(DEBUG_TAG, "In onStart.");
        mp.start();
        return Service.START_STICKY_COMPATIBILITY;
    }


    @Override
    public IBinder onBind(Intent intent) {
        Log.d(DEBUG_TAG, "In onBind with intent=" + intent.getAction());
        return null;
    }

}

You can start this Service up in an Activity as follows:

Intent serv = new Intent(this,MuzakService.class);
startService(serv);

and stop it like this:

Intent serv = new Intent(this,MuzakService.class);
stopService(serv);
Nikhil Agrawal
  • 26,128
  • 21
  • 90
  • 126
jengelsma
  • 8,192
  • 4
  • 21
  • 21
  • How does MediaController fit into all of this if the audio is playing in a service? – Metallicraft Jul 12 '11 at 17:55
  • Your code has the tracks embedded in the service, is this different than if there was a playlist? Or can you send a list of songs in an array or something into the service? – Metallicraft Jul 12 '11 at 18:06
  • Sure, in the spirit of keeping the example simple I just embedded a list of tracks in the Service. You could pass the playlist/tracks to the service via a Bundle, as described [here](http://stackoverflow.com/questions/5288642/how-to-pass-the-parameters-to-service-from-an-activity). If you wanted more elaborate control you could create a bound service. To understand how that sort of service differs from the above code, read [this](http://developer.android.com/guide/topics/fundamentals/services.html). – jengelsma Jul 12 '11 at 18:35
  • You could use the MediaController if you want an interactive user interface or you could roll your own user interface. There is some sample code [here](http://stackoverflow.com/questions/3747139/how-to-show-mediacontroller-while-playing-audio-in-android/5265629#5265629) showing how to use the MediaController with a MediaPlayer. – jengelsma Jul 12 '11 at 18:46
  • I've seen the MediaController link you mentioned. That does not use MediaPlayer as a service though. If media player is a service, does mediacontroller (presumably in the activity that created the mediaplayer service) have a hook or something to get at mediaplayer? If the activity goes into the background and is brought back to front sometime later, does mediacontroller still have a "connection" to the mediaplayer? – Metallicraft Jul 12 '11 at 22:34
1

Please note that Service run in fore ground too.

Please see the official documentation. It explains with a sample code. Using a Service with MediaPlayer: http://developer.android.com/guide/topics/media/mediaplayer.html#mpandservices

Running as a foreground service

Services are often used for performing background tasks

But consider the case of a service that is playing music. Clearly this is a service that the user is actively aware of and the experience would be severely affected by any interruptions. Additionally, it's a service that the user will likely wish to interact with during its execution. In this case, the service should run as a "foreground service." A foreground service holds a higher level of importance within the system—the system will almost never kill the service, because it is of immediate importance to the user. When running in the foreground, the service also must provide a status bar notification to ensure that users are aware of the running service and allow them to open an activity that can interact with the service.

In order to turn your service into a foreground service, you must create a Notification for the status bar and call startForeground() from the Service

Sree Rama
  • 1,207
  • 12
  • 25
1

mediap player should run from the service, here i have passed the arraylist of songs from the activity to service and all the songs are run by reading the arraylist

public class MyService extends Service implements OnCompletionListener,
  MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener{

 Context context; 
 private static final String ACTION_PLAY = "PLAY";
 private static final String TAG = "SONG SERVICE";
 MediaPlayer mediaPlayer; 
 private int currentTrack = 0;
 ArrayList<String> list;
 public MyService() {
  context=getBaseContext();  
 }

 @Override
 public IBinder onBind(Intent intent) {
  throw new UnsupportedOperationException("Not yet implemented");
 }

 @Override
 public int onStartCommand(Intent intent, int flags, int startId) {
  list = (ArrayList<String>)intent.getSerializableExtra("arraylist");
  int count=0;
  Log.d(TAG, "total count:"+list.size());
  //playing song one by one
  for (String string : list) {
   //play(string);
   count++;
   Log.d(TAG, "count:"+list);
  }
  play(currentTrack);
  Log.d(TAG, "count:"+count);
  if(count==list.size()){
   //stopSelf();
   Log.d(TAG, "stoping service");
   //mediaPlayer.setOnCompletionListener(this);
  }else{
   Log.d(TAG, "not stoping service");
  }
  
  if (!mediaPlayer.isPlaying()) {
   mediaPlayer.start();
   Log.d(TAG, "oncommat");
  }
  return START_STICKY;
 }
 
 @Override
 public void onCreate() {  
  
  Toast.makeText(this, "Service was Created", Toast.LENGTH_LONG).show();
 }


 
 @Override
 public void onStart(Intent intent, int startId) {
  // Perform your long running operations here.
  Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
 }

 @Override
 public void onDestroy() {
  Toast.makeText(this, "Service Destroyed", Toast.LENGTH_LONG).show();
  Log.d("service", "destroyed");
  if (mediaPlayer.isPlaying()) {
        mediaPlayer.stop();
      }
      mediaPlayer.release();
 }



 @Override
 public boolean onError(MediaPlayer mp, int what, int extra) {
  // TODO Auto-generated method stub
  return false;
 }

 @Override
 public void onPrepared(MediaPlayer mp) {
  // TODO Auto-generated method stub

 }
 
 private void play(int id) {
  
  
  if(mediaPlayer!=null && mediaPlayer.isPlaying()){
   Log.d("*****begin*****", "playing");
   stopPlaying();
    Log.d("*****begin*****", "stoping");
       } else{
       Log.d("*****begin*****", "nothing");
       }
  
  Log.d("*****play count*****", "="+currentTrack);
  Log.i("******playing", list.get(currentTrack));
  
  Uri myUri1 = Uri.parse(list.get(id));
  mediaPlayer = new MediaPlayer();
  mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
  //mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
  mediaPlayer.setOnPreparedListener(this); 
  //mediaPlayer.setOnCompletionListener(this); 
  mediaPlayer.setOnErrorListener(this); 
  try {
   mediaPlayer.setDataSource(context, myUri1);   
   Log.i("******playing", myUri1.getPath());
  } catch (IllegalArgumentException e) {
            Toast.makeText(context, "You might not set the URI correctly!", Toast.LENGTH_LONG).show();
        } catch (SecurityException e) {
            Toast.makeText(context, "You might not set the URI correctly!", Toast.LENGTH_LONG).show();
        } catch (IllegalStateException e) {
            Toast.makeText(context, "You might not set the URI correctly!", Toast.LENGTH_LONG).show();
        } catch (IOException e) {
            e.printStackTrace();
        }
  
  try {
   mediaPlayer.prepare();
        } catch (IllegalStateException e) {
            Toast.makeText(context, "You might not set the URI correctly!", Toast.LENGTH_LONG).show();
        } catch (IOException e) {
            Toast.makeText(context, "You might not set the URI correctly!", Toast.LENGTH_LONG).show();
        }
  
  mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
   
   @Override
   public void onCompletion(MediaPlayer mp) {
    currentTrack=currentTrack+1;
    play(currentTrack);
    /* currentTrack = (currentTrack + 1) % list.size();
     Uri nextTrack=Uri.parse(list.get(currentTrack));
     
     try {
      mediaPlayer.setDataSource(context,nextTrack);
      mediaPlayer.prepare();
     // mediaPlayer.start();
    } catch (Exception e) {
     e.printStackTrace();
    }*/
    
   }
  });
  
        mediaPlayer.start();
 }
 
 private void stopPlaying() { 
        if (mediaPlayer != null) {
         mediaPlayer.stop();
         mediaPlayer.release();
         mediaPlayer = null;
       } 
    }

 @Override
 public void onCompletion(MediaPlayer mp) {
  // TODO Auto-generated method stub
  
 }
Joseph
  • 400
  • 4
  • 19
Issac Balaji
  • 1,441
  • 1
  • 13
  • 25
0

The answer is Services in Android as described here: http://developer.android.com/guide/topics/fundamentals.html as

You are going to create a service and when you receive play command from your app, your app will send a message to background service to play the music. Services do not run in foreground, therefore even you put your screen to sleep, it plays the music.

Playing BG Music Across Activities in Android

Community
  • 1
  • 1
ahmet alp balkan
  • 42,679
  • 38
  • 138
  • 214
  • So is a service destroyed and recreated everytime a song ends? – Metallicraft Jul 12 '11 at 17:55
  • No not at all. Services do not consume much system resources unless they work actively. It can always work if you want even if your app is shut down. example services: SMS receiver, Alarm. – ahmet alp balkan Jul 12 '11 at 18:14
  • I guess I should have phrased it differently. I mean, in order to play songs continuously, do you create a service, then when the song is finished, you destroy the service and create a new one for the next song in the playlist? Or can you pass a list of songs to play, and the service just remains active until its played them all? – Metallicraft Jul 12 '11 at 18:23
  • You can pass a list or you can send message "play current playing music and start that one instead" to the service. it is all about how you program the service. you don't kill the service. if you kill the service. it respawns automatically. because services are supposed to work always as long as app says it should be running. – ahmet alp balkan Jul 13 '11 at 06:34