Service and MediaPlayer - Control screen rotation and App exit - android

SOLVED - See answer.
I'm dealing with an app that uses a service for playing music with the MediaPlayer class.
I'm having problem with screen rotation and when I leave the app. I lose the reference or state of the MediaPlayer object or the service itself. Been working so many hours with this that not really know what is wrong.
I start and bind to the service in Activity's onResume:
#Override
protected void onResume()
{
Intent intent=new Intent(getApplicationContext(), MusicService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
super.onResume();
}
I get the reference to the service with a ServiceConnection:
private ServiceConnection serviceConnection=new ServiceConnection()
{
public void onServiceConnected(ComponentName className, IBinder binder)
{
service=((MusicService.MyBinder)binder).getService();
}
public void onServiceDisconnected(ComponentName className)
{
service=null;
}
};
At this point I can call any method from my service: service.playMusic(); and works fine.
I unbind in onDestroy:
#Override
protected void onDestroy()
{
unbindService(serviceConnection);
super.onDestroy();
}
And this is my Service class:
public class MusicService extends Service
{
private final IBinder binder=new MyBinder();
private MediaPlayer player;
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
return Service.START_REDELIVER_INTENT;
}
#Override
public IBinder onBind(Intent intent)
{
player=MediaPlayer.create(getApplicationContext(), R.raw.tumbler);
player.setLooping(true);
player.setVolume(80, 80);
return binder;
}
public class MyBinder extends Binder
{
public MusicService getService()
{
return MusicService.this;
}
}
public void play()
{
player.start();
Log.d("MUSIC SERVICE", "play!");
}
}
So, problems are:
When I exit, the service and music keeps playing. I want to stop it. If I enter the app again, same service is launched again and the music is playing twice. I want to stop it.
When I rotate the screen, the MediaPlayer keeps playing (as I want) but I can't call anymore pause(), start() and so on because it state has changed (I get the message ' MediaPlayer﹕ pause called in state 8' and `MediaPlayer﹕ start called in state 0').
Please, need some help with this. Thanks in advance!

#Override
public IBinder onBind(Intent intent) {
player=MediaPlayer.create(getApplicationContext(), R.raw.tumbler);
player.setLooping(true);
player.setVolume(80, 80);
return binder;
}
You're re-initializing the MediaPlayer every time you bind to the service and never stopping the previous instance. Don't start the media in onBind(), wait for a command to the service to start/stop the media player. Services outlive the Activity (that's what they're designed for) so it's no surprise that it keeps playing after the Activity is destroyed. Unless you specifically told it otherwise, that's the expected behavior. If you're wanting a media player that just keeps going while the Activity is open, you might want to look into using a headless Fragment to keep a retained Fragment that can manage the MediaPlayer for you in onCreate()/onDestroy().

Well I think I got it.
I had a mess in my mind (and code) but finally it's working.
If this is not the correct way to do this, please let me know.
The first of my problems was: When the App exits the Service wasn't destroyed and the MediaPlayer was playing until I force-close the App. If I didn't force-close, on App relaunch I had 2 MediaPlayers playing simultaneously and so on.
Cause: I was not terminating my Service manually.
Solution: On Activity's onDestroy(), call unbindService() and stopService():
#Override
protected void onDestroy()
{
super.onDestroy();
unbindService(serviceConnection);
stopService(new Intent(getApplicationContext(), MusicService.class));
}
The second problem was: When the screen was rotated I lost the MediaPlayer's object reference, and music was out of control because I couldn't access the object.
Cause: Bad design. I was setting up the MediaPlayer into Service's onBind() method. Thanks to #kcoppock. He noticed that. So, when the screen was rotated, Activity's onCreate() was called and I have bindService() on it, causing the creation of the new MediaPlayer object without even cleaning up the former instance.
Solution: Just move out that code to another part that is not called automatically, and call it manually once, when the App starts, and not every time that Activity is recreated by configChanges.
So, basically that is how I fixed it, hope it may help to other users.
Cheers.

Related

music pause when app killed when using service

I am using a Service class to play music in the background. While I am displaying the notification bar on top, there is issue in my app. When I kill the app, the music stops for about 1 second and then it starts again. I can't figure out what's the issue.
I was following this tutorial.
public class MyMusicService extends Service {
MediaPlayer mediaPlayer;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
public MyMusicService() {
super();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (mediaPlayer!= null)
{
mediaPlayer.release();
}
mediaPlayer =MediaPlayer.create(this,R.raw.song);
mediaPlayer.setLooping(true);
mediaPlayer.start();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
mediaPlayer.stop();
}
}
Use onTaskRemoved on your main activity and release your mediaplayer on this event.
That tutorial shows you how to build a BACKGROUND service, which can get killed by the system. To play music you should use a FOREGROUND service.
https://developer.android.com/guide/components/services.html#Foreground
According to: https://developer.android.com/reference/android/app/Service.html
Change
START_STICKY
to
START_NOT_STICKY
EDIT:
The issue with music stopping for a while is connected with the fact that you run app and service in the same process. And 'killing app' means stopping the whole process bound to it. What you need to do is to run your service in seperate process:
Start a service in a separate process android

The same background music playing in all activities

I use services to play background music in all activities and it works. The problem is the music continues playing even if my app is in background (when user exit with home button or back button). How can I solve this?
Services class BackgroundSoundService
public class BackgroundSoundService extends Service {
private static final String TAG = null;
MediaPlayer player;
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
player = MediaPlayer.create(this, R.raw.slow_shock);
player.setLooping(true); // Set looping
player.setVolume(100,100);
}
public int onStartCommand(Intent intent, int flags, int startId) {
player.start();
return START_NOT_STICKY;
}
public void onStart(Intent intent, int startId) {
// TO DO
}
public IBinder onUnBind(Intent arg0) {
// TO DO Auto-generated method
return null;
}
public void onStop() {
}
public void onPause() {
}
#Override
public void onDestroy() {
player.stop();
player.release();
}
#Override
public void onLowMemory() {
}
}
Starting with
Intent svc = new Intent(this, BackgroundSoundService.class);
startService(svc);
Android Manifest:
<service android:enabled="true" android:name=".BackgroundSoundService" />
This is happened because the Service is still bounded in the activity. To terminate the service when multiple activity is bound to the service, you need to unbind the service from all of them as in the documentation says:
The service lifecycle—from when it's created to when it's
destroyed—can follow either of these two paths:
A started service
The service is created when another component calls startService().
The service then runs indefinitely and must stop itself by calling
stopSelf(). Another component can also stop the service by calling
stopService(). When the service is stopped, the system destroys it.
A bound service
The service is created when another component (a client) calls
bindService(). The client then communicates with the service through
an IBinder interface. The client can close the connection by calling
unbindService(). Multiple clients can bind to the same service and
when all of them unbind, the system destroys the service. The service
does not need to stop itself.
These two paths are not entirely separate. You can bind to a service
that is already started with startService(). For example, you can
start a background music service by calling startService() with an
Intent that identifies the music to play. Later, possibly when the
user wants to exercise some control over the player or get information
about the current song, an activity can bind to the service by calling
bindService(). In cases such as this, stopService() or stopSelf()
doesn't actually stop the service until all of the clients unbind.
Then call Context.stopService() to stop it:
context.stopService(new Intent(context, BackgroundSoundService.class));

MediaBrowserServiceCompat instance is recreated every runtime changes

I have a Service using the new MediaBrowserServiceCompat to interact with my Player implementation.
Here's my service's onCreate()
#Override
public void onCreate() {
super.onCreate();
audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
initMediaSession();
initMediaPlayer();
}
and my initMediaSession()
private void initMediaSession() {
Log.d(TAG, "initMediaSession: ");
mediaSessionCompat = new MediaSessionCompat(getApplicationContext(), TAG);
mediaSessionCompat.setCallback(new MediaSessionCompat.Callback() {
// I override the methods here
});
mediaSessionCompat.setActive(true);
// Method from MediaBrowserServiceCompat
setSessionToken(mediaSessionCompat.getSessionToken());
}
I then access my MediaBrowserCompat in my Activity:
#Override
public void onCreate() {
mediaBrowser = new MediaBrowserCompat(this, new ComponentName(this, MediaPlaybackService.class), mediaBrowserConnectionCallback, null);
mediaBrowser.connect();
}
// I do stuff here
#Override
protected void onDestroy() {
super.onDestroy();
if (mediaBrowser != null) {
mediaBrowser.disconnect();
}
}
Every time mediaBrowser.connect() is called my service is created and killed when mediaBrowser.disconnect() is called.
The problem is when the MediaSessionCompat is recreated I lose any metadata I created or playback state I might have saved.
Is it the way it is supposed to be or mm I doing it wrong ?
Also If it is supposed to be that way does it mean I have to save my metadata somewhere and recall MediaSessionCompat.setMetadata() every time I create a new MediaSessionCompat object? or is there a more efficient solution ?
Thank you
As mentioned in the MediaBrowserService and the modern media playback app blog:
this wraps the API for bound services, which makes sense since we’re trying to connect to a Service.
The lifecycle of a bound service is tied directly to who binds to the service:
When the last client unbinds from the service, the system destroys the service (unless the service was also started by startService()).
In your case, the service will no longer have anyone bound to it between onDestroy() and onCreate().
The example of Universal Android Music Player's MusicService is to:
Call startService(new Intent(this, MediaPlaybackService.class) when playback begins
Call stopSelf() when playback is stopped
This ensures that changes in the bound clients do not cause the Service to be destroyed mid-playback.

Android Service with MediaPlayer gets recreated or destroyed

I'm using bound service so that I am able to communicate between an activity and a service.
I'm binding to a service in onStart:
#Override
protected void onStart() {
super.onStart();
Intent bindIntent = new Intent(this, MusicService.class);
bindService(bindIntent, this, BIND_AUTO_CREATE);
}
waiting for service to bind:
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMusicService = ((MusicService.LocalBinder) service).getService();
mMusicService.setCallback(this);
}
handling disconnect from service:
#Override
public void onServiceDisconnected(ComponentName name) {
mMusicService = null;
}
unbinding from service in onDestroy:
#Override
protected void onDestroy() {
super.onDestroy();
if (mMusicService != null && isFinishing()) {
mMusicService.setCallback(null);
unbindService(this);
}
}
My problem is that when app is minimized, onDestroy gets called immediately and then onUnbind in Service gets called and music is stopped.
Here is onUnbind method (where mPlayer is MediaPlayer):
#Override
public boolean onUnbind(Intent intent) {
mPlayer.stop();
mPlayer.release();
mPlayer = null;
return super.onUnbind(intent);
}
If I don't implement onUnbind music continues to play (sometimes and sometimes it stops after some time) and when I open the app again (from minimized applications) I am able to play another song and then those two song play at same time.
I've red couple articles about music players and services on android and I thought that this was correct approach (thinking that onDestroy will be called when OS is out of memory).
Any ideas how I can re-implement my app workflow so that I will work as expected?
EDIT
At first I thought that "Don't keep activities" under developer options is a problem, but problem is still there even if I uncheck it.
And if some code from my service is needed please say I will edit my question (there's a lot of code and I'm not sure which part is important for this issue)
SOLUTION:
startForeground(<notification id>, <notification>);
to run service even if app gets killed. And when user dismisses the notification:
stopForeground(true);
stopSelf();
More about startForeground here.
It's not obvious, but you should start a Thread that runs in background and use the service to control it's state.
A service isn't a thread that hold some run state like a thread. Unless it's an IntentService. (Correct me if I'm wrong)
While activity can (and probably will) get destroyed, your app will keep running.
Activity:
#Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, BackgroundService.class);
startService(intent);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
finishOnPause = true;
}
#Override
protected void onStop() {
super.onStop();
unbindService(mServiceConnection);
}
Service:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
ensureServiceThread();
if (intent != null) {
}
return START_NOT_STICKY;
}
private void ensureServiceThread() {
if (service_thread == null) {
service_thread = new ServiceThread(this);
service_thread.start();
}
}
#Override
public void onDestroy() {
stopServiceThread();
super.onDestroy();
}
private void stopServiceThread() {
if (service_thread != null) {
service_thread.interrupt();
service_thread = null;
}
}
And you should do your work inside the Thread.
If you need context, it's your Service.
If you need to do something on Main thread - create a Handler in Service.OnCreate and do a handler.postRunnable inside a worker thread safely.
What I would do is:
- Create a service
- Create a thread
- Create a media player inside a thread (if possible, otherwise on Service creation and pass it to thread)
- Inside a thread - continuously poll media player state
- On song finished send an intent to service that change track
- stop thread/service if needed.

Communicate with foreground service android

First question here, but I've been around for a while.
What do I have:
I'm building an Android app which plays audio streams and online playlists. Everything is working fine now, but I'm having issues in communicating with my service.
The music is playing in a Service, started with startForeground, so it doesn't gets killed.
I need to communicate from my activity with the service, for getting the track name, image, and a couple of things more.
Whats my issue:
I think I need to start my service with bindService (instead of my current startService) so the activity can talk to it.
However, when I do that, my service gets killed after closing the Activity.
How can I get both? Binding and foreground service?
Thanks!
No. bindService will not start a service . It will just bind to the Service with a service connection, so that you will have the instance of the service to access/control it.
As per your requirement I hope you will have the instance of MediaPlayer in service . You can also start the service from Activity and then bind it. If the service is already running onStartCommand() will be called, and you can check if MediaPlayer instance is not null then simply return START_STICKY.
Change you Activity like this..
public class MainActivity extends ActionBarActivity {
CustomService customService = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// start the service, even if already running no problem.
startService(new Intent(this, CustomService.class));
// bind to the service.
bindService(new Intent(this,
CustomService.class), mConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
customService = ((CustomService.LocalBinder) iBinder).getInstance();
// now you have the instance of service.
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
customService = null;
}
};
#Override
protected void onDestroy() {
super.onDestroy();
if (customService != null) {
// Detach the service connection.
unbindService(mConnection);
}
}
}
I have similar application with MediaPlayer service. let me know if this approach doesn't help you.
Quoting Android documentation:
A bound service is destroyed once all clients unbind, unless the service was also started
And about the difference between started and bound just take a look to https://developer.android.com/guide/components/services.html
So, you have to create the Service using startService and then bindService, like #Libin does in his/her example. Then, the service will run until you use stopService or stopSelf or until Android decides that it needs resources and kills you.

Categories

Resources