I am developing an audio streaming app for android devices and I have some issues about the handling of background audio service.
The expected behaviour of my app is that the audio source will be played continuously, even if the device is in stand-by mode with the screen off, but sometimes the background audio service is interrupted without any apparent reason.
To fix this problem, I have started the service with the option START_STICKY.
In this way, the service will be restarted even if it is closed for any reason, but now I can't close the service with a force close of the app and sometimes the service it will be restarted even if I want to close it!
Does anyone can suggest me a correct way to handle this job?
Here is my code:
At first I have declared the intent in the MainActivity:
Intent playbackServiceIntent;
Inside the onCreate() method of the MainActivity I have registered the service:
playbackServiceIntent = new Intent(this, BackgroundAudioService.class);
To start the audio service through the play button:
startService(playbackServiceIntent);
To stop the service through the stop button
stopService(playbackServiceIntent);
I have alse overrided the onDestroy() method of the MainActivity:
#Override
public void onDestroy() {
stopService(playbackServiceIntent);
//stopService(new Intent(this,BackgroundAudioService.class));
super.onDestroy();
}
Finally, the BackgroundAudioService class is defined as follow:
public class BackgroundAudioService extends Service implements OnCompletionListener {
public MediaPlayer mediaPlayer;
public WifiLock wifiLock;
PhoneStateListener mPhoneStateListener;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
mediaPlayer = new MediaPlayer(); // raw/s.mp3
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
mediaPlayer.setDataSource(url);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");
wifiLock.acquire();
mediaPlayer.setOnBufferingUpdateListener(new OnBufferingUpdateListener() {
public void onBufferingUpdate(MediaPlayer mp, int percent) {
}
});
mPhoneStateListener = new PhoneStateListener()
{
protected boolean mWasPlayingWhenCalled = false;
#Override
public void onCallStateChanged(int state, String incomingNumber)
{
if( state == TelephonyManager.CALL_STATE_RINGING )
{ // Incoming call: Pause music
mediaPlayer.pause();
mWasPlayingWhenCalled = true;
}
else if(state == TelephonyManager.CALL_STATE_IDLE )
{ // Not in call: Play music
if( mWasPlayingWhenCalled )
{
mediaPlayer.start();
mWasPlayingWhenCalled = false;
}
}
else if( state == TelephonyManager.CALL_STATE_OFFHOOK )
{ // A call is dialing, active or on hold
mediaPlayer.pause();
mWasPlayingWhenCalled = true;
}
super.onCallStateChanged(state, incomingNumber);
}
};
TelephonyManager mgr = (TelephonyManager)getSystemService(TELEPHONY_SERVICE);
mgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (!mediaPlayer.isPlaying()) {
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
mediaPlayer.start();
Intent intent = new Intent();
intent.setAction("com.my_app.CUSTOM_INTENT");
sendBroadcast(intent);
}
});
Time t=new Time();
t.setToNow();
}
return START_STICKY;
}
public void onDestroy() {
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
wifiLock.release();
mediaPlayer.release();
TelephonyManager mgr1 = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
if(mgr1 != null) {
mgr1.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
}
Time t=new Time();
t.setToNow();
}
public void onCompletion(MediaPlayer _mediaPlayer) {
stopSelf();
Time t=new Time();
t.setToNow();
}
}
I tried almost everything found on the internet and I can't stop the media player once it starts. I'm using broadcast receiver and I'm controlling the media player using SMS. Here is my code.
public class Receiver extends BroadcastReceiver{
String body;
String address;
public static final String SMS_EXTRA_NAME="pdus";
MediaPlayer mp = new MediaPlayer();
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
SharedPreferences obj1=context.getSharedPreferences("mypref", Context.MODE_PRIVATE);
String newstring=obj1.getString("key1", null);
String name=newstring;
Bundle bund=intent.getExtras();
String space="";
if(bund!=null)
{
Object[] smsExtra=(Object[])bund.get(SMS_EXTRA_NAME);
for(int i=0;i<smsExtra.length;i++)
{
SmsMessage sms=SmsMessage.createFromPdu((byte[])smsExtra[i]);
body=sms.getMessageBody().toString();
address=sms.getOriginatingAddress();
if(body.equals("ON"))
{
if(mp.isPlaying())
{
mp.stop();
}
try {
mp.reset();
AssetFileDescriptor afd;
afd = context.getAssets().openFd("file.mp3");
mp.setDataSource(afd.getFileDescriptor(),afd.getStartOffset(),afd.getLength());
mp.prepare();
mp.start();
mp.setLooping(true);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
else if(body.equals("OFF"))
{
if (mp.isPlaying()==true||mp!=null)
{
try{
mp.stop();
mp.release();
} catch(Exception e){
System.out.println("Exception"+e);
}
}
}
}
}
}
}
The media player is turning on when I send "ON", but it won't turn off. And yes I have given the required permissions in the Manifest file.
The BroadcastReciever it stays alive for around 9 seconds, you should not create big operation in it. However, you can let it start an operation like start acitivty or service and there you play a track or start download a file ...etc
If you want to only start a player and no need for user interaction, I suggest that you start a service and there you play your what you want.
I spent a lot of time studying this problem, and found out that:
The problem here is that I create a MediaPlayer inside a thread that is managed by the IntentService. And at the time of starting playback the thread is no longer valid.
So the way out is:
final Handler handler = new Handler(getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
mediaPlayer.start();
}
});
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
}
, 30 * 1000);
It helped me stop the mediaplayer.
I am trying to develop a simple media player to play mp3's of the sdcard/music directory for Android 2.3.3. The problem is when I hit the power button or when the device goes to sleep, the music stops. From googling, and searching stackoverflow, I found that I need to use the wake lock options, but no matter what I do, the music stops when the device goes to sleep (Pressing F7 on the emulator stops the music as well). Since I've been fighting this for way too long, I thought I'd ask for help. I would sincerely appreciate any input. Thanks. Here's my code:
FileInputStream fis = new FileInputStream(songList.get(0));
FileDescriptor fd = fis.getFD();
if (mediaPlayer != null) {
if (mediaPlayer.isPlaying()) {
mediaPlayer.release();
mediaPlayer = null;
}
}
mediaPlayer = new MediaPlayer();
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
mediaPlayer.setDataSource(fd);
mediaPlayer.prepare();
playPauseButton.setImageResource(android.R.drawable.ic_media_pause);
mediaPlayer.seekTo(songPosition);
mediaPlayer.start();
appMsg.setText(songList.get(0));
I think you should run the media in background using services
So you create a service and put your media code in it and attach it to start and stop buttons maybe somthing like this :
public class MediaPlayerService extends Service {
MediaPlayer myMediaPlayer;
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate() {
FileInputStream fis = new FileInputStream(songList.get(0));
FileDescriptor fd = fis.getFD();
if (mediaPlayer != null) {
if (mediaPlayer.isPlaying()) {
mediaPlayer.release();
mediaPlayer = null;
}
}
mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(fd);
mediaPlayer.prepare();
playPauseButton.setImageResource(android.R.drawable.ic_media_pause);
mediaPlayer.seekTo(songPosition);
appMsg.setText(songList.get(0));
}
#Override
public void onStart(Intent intent, int startid) {
myMediaPlayer.start();
}
#Override
public void onDestroy() {
myMediaPlayer.stop();
}
}
After that you start that service when a start button is pressed using methodestartService and stop it using methode stopService in your Activity class
I am starting a sound from a background service (IntentService), which is triggered by a system alarm (the thread of the service will most often be dead when the sound ends).
The relevant code is this:
Uri alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
if (alert == null)
alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
MediaPlayer mMediaPlayer = new MediaPlayer();
mMediaPlayer.setDataSource(this, alert);
final AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
if (audioManager.getStreamVolume(AudioManager.STREAM_NOTIFICATION) != 0) {
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_NOTIFICATION);
mMediaPlayer.setLooping(false);
mMediaPlayer.prepareAsync();
mMediaPlayer.start();
}
This stuff works fine, but every time the sound is played, I get this in log cat:
WARN/MessageQueue(7508): Handler{482f97e0} sending message to a Handler on a dead thread
I think this could be due to a callback to the background thread when the sound is finished, or my repeated use of a media player before having finalized the previous one. Any ideas?
Very old question, but #Alex' xkcd link convinced to me answer it anyway.
I have a very similar situation, and was able to achieve the desired result by instantiating the MediaPlayer through a Runnable. In my case an IntentService calls an ongoing Service, which is responsible for the media playback. My solution looks as follows (relevant code only):
public class HelperService extends Service {
public void play() {
Thread thread = new Thread(new Runnable() {
public void run() {
soundStart();
}
});
thread.start();
}
private void soundStart() {
try {
AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(R.raw.sound);
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_RING);
mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
mp.reset();
return false;
}
});
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
// do stuff
}
});
mMediaPlayer.prepare();
mMediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Wondering how next songs are played once app is closed as if playing an entire CD or playlist...
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.
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);
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
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
}
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