Android Service with MediaPlayer gets recreated or destroyed - android

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.

Related

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));

Which function is called when application is removed from task manager

I need to make status of user offline. When I press home button onStop() is called, that's fine. When I press back button onDestroy() is invoked. But when I close the app from recent apps by swiping it, onStop() or onDestroy() isn't called.
I need to know when the app is closed from recent apps to do something (e.g make user offline).
Make a service :
public class MyService extends Service {
private DefaultBinder mBinder;
private AlarmManager alarmManager ;
private PendingIntent alarmIntent;
private void setAlarmIntent(PendingIntent alarmIntent){
this.alarmIntent=alarmIntent;
}
public void onCreate() {
alarmManager (AlarmManager)getSystemService(Context.ALARM_SERVICE);
mBinder = new DefaultBinder(this);
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public void onTaskRemoved (Intent rootIntent){
alarmManager.cancel(alarmIntent);
this.stopSelf();
}
}
Make a custom class :
public class DefaultBinder extends Binder {
MyService s;
public DefaultBinder( MyService s) {
this.s = s;
}
public MyService getService() {
return s;
}
}
Add to your activity :
MyService service;
protected ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder binder) {
service = ((DefaultBinder) binder).getService();
service.setAlarmIntent(pIntent);
}
public void onServiceDisconnected(ComponentName className) {
service = null;
}
};
protected void onResume() {
super.onResume();
bindService(new Intent(this, MainService.class), mConnection,
Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
if (mConnection != null) {
try {
unbindService(mConnection);
} catch (Exception e) {}
}
}
But when I close the app from recent apps by swiping it, onStop() or onDestroy() isn't called.
Methods of Activity lifecycle that are to be called when Activity is no longer visible are not guaranteed to be called when removed from the recent tasks (treat it as "soft" version of killing an app by the system due to low memory).
I need to know when the app is closed from recent apps to do something (e.g make user offline)
I suggest one of the following:
(If applicable) use Activity's onResume()/onPause() to "make user online/offline";
use Service that sticks to the application meaning that if the app is killed after Service's onStartCommand() returns, the service will be recreated and onStartCommand() will be called again. At this point you could "make user offline". The chain of lifecycle method calls would be:
Activity's onStop() -> onDestroy()* ->
Service's onTaskRemoved()* ->
Application's onCreate() -> Service's onCreate() ->
Service's onStartCommand()
The Intent passed to the method will help you recognize which component triggered the start request:
Intent != null, meaning the request has been received from a running Activity instance
Intent = null, meaning the request has been sent by the (newly created) Application instance
* Not guaranteed to be called
No, there is no clean way to get the application termination time. But I could suggest you a dirty trick to use a service to update your application (offline functionalities) for every after n minutes.
When OS kill your application, it will remove all associated services and broadcast receiver along with it.

Started and Bound service is mysteriously stopped and restarted

My activity attempts to start and then bind a service that's supposed to run independently of it. This service turns the GPS on.
onCreate calls getApplicationContext.StartService, and onResume calls getApplicationContext.BindService. OnPause calls getApplicationContext.unbindService, although it never appears to run properly (the service connection never logs an unbind, although binds are logged when I treat it similarly).
Unfortunately, when I open up my Recents list, and slide the activity away, the Service stops and then re-starts itself almost immediately, dropping the GPS connection. What would cause this sort of behavior?
#Override
protected void onStart() {
super.onStart();
}
#Override
protected void onResume() {
super.onResume();
// Start up the service
Intent intent = new Intent(getApplicationContext(), LockService.class);
getApplicationContext().bindService(intent, myConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onPause() {
super.onPause();
Log.i(tag, "onPause");
if (isBound) {
getApplicationContext().unbindService(myConnection);
}
}
...
// Bound service stuff
LockService myService;
boolean isBound = false;
private ServiceConnection myConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
LockBinder binder = (LockBinder) service;
myService = binder.getService();
isBound = true;
final boolean status = myService.getStatus();
Log.i(tag, "service bound");
}
#Override
public void onServiceDisconnected(ComponentName className) {
isBound = false;
Log.i(tag, "service unbound");
}
};
Edit: I checked into this answer, but this just prevents the service from restarting immediately (it still gets hung up, and re-opening the activity re-initializes it):
Android onCreate Service called when Activity onDestroy called
Edit 2: I had gotten my hopes up a bit too far with this answer. It doesn't seem to fix anything either.
How to force the onServiceDisconnected() get called?
Edit 3: This might be a KitKat thing.
In android 4.4, swiping app out of recent tasks permanently kills application with its service . Any idea why?
Because swiping an app out of the Recents list kills it in Android version 4.4 (KK), I have opted to simply not show my app in the Recents list at all.
Oh well, it didn't really need to live there anyway. It exists quite happily inside the notification bar. I pity anyone who isn't so lucky, and needs to forcibly restart the service via a timer and some hackneyed code.

Activity rotation cause service to be killed

I have a service which plays music and an activity which provides the GUI for interacting with the service.The activity opens on list item click(I have a list of recordings) and it binds the service(and create it) at onCreate() method.
When onDestroy() is called, I unbind the service (this will destroy the service) - this should be OK since I do not want the service to run if the activity is exited, but the problem appear on orientation change because it re-creates the activity again and the service too(and the track is stopped and played again from the beginning when rotating the device).
I know about some flags (orientationChange) that might be useful, but is not a good practice for me since I want a different layout on landscape.
Also I could make the music player service to run as long as my app runs, but isn't a good idea since the user may not want to open the player, but want just to record, so the player service isn't necessarily here.
Here are some code snippets:
#Override
protected void onCreate(Bundle savedInstanceState) {
LocalBroadcastManager.getInstance(this).registerReceiver(mLocalReceiver, new IntentFilter(PlayerBroadcastReceiver.ACTION_PLAYER_SERVICE_STARTED));
setContentView(R.layout.media_player_screen);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
AudioPlayerServiceBridge.getInstance().addCallback(this);
AudioPlayerServiceBridge.getInstance().doBindService(this);
init(savedInstanceState);
super.onCreate(savedInstanceState);
}
#Override
protected void onDestroy() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mLocalReceiver);
mLocalReceiver.removeCallback();
Log.d(AudioPlayerActivity.class.getName(), "onDestroy() -> "+AudioPlayerActivity.class.getName());
AudioPlayerServiceBridge.getInstance().doUnbindService(this);
AudioPlayerServiceBridge.getInstance().removeCallback(this);
super.onDestroy();
}
and the service connection manager:
public void doBindService(Context context) {
// Establish a connection with the service. We use an explicit
// class name because there is no reason to be able to let other
// applications replace our component.
if(!mIsBound){
context.bindService(new Intent(context,
AudioPlayerService.class), serviceConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
}
public void doUnbindService(Context context) {
if (mIsBound) {
// If we have received the service, and hence registered with
// it, then now is the time to unregister.
if (mServiceMessenger != null) {
Message msg = Message.obtain(null, AudioPlayerService.MSG_UNREGISTER_CLIENT);
msg.replyTo = mMessenger;
mServiceMessenger.send(msg);
}
// Detach our existing connection.
context.unbindService(serviceConnection);
mIsBound = false;
}
}
Please show me if possible a good practice to handle this problem.
The answer is:
I should start the service with : startService(new Intent(this, service.class)) AND START BINDING after that. This method prevent the service to be killed when doUnbind() is called. So the onCreate() method is changed now in:
#Override
protected void onCreate(Bundle savedInstanceState) {
LocalBroadcastManager.getInstance(this).registerReceiver(mLocalReceiver, new IntentFilter(PlayerBroadcastReceiver.ACTION_PLAYER_SERVICE_STARTED));
setContentView(R.layout.media_player_screen);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
if(savedInstanceState == null)
startService(new Intent(this, AudioPlayerService.class));
AudioPlayerServiceBridge.getInstance().addCallback(this);
AudioPlayerServiceBridge.getInstance().doBindService(this);
init(savedInstanceState);
super.onCreate(savedInstanceState);
}
onDestroy() method:
#Override
protected void onDestroy() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mLocalReceiver);
mLocalReceiver.removeCallback();
Log.d(AudioPlayerActivity.class.getName(), "onDestroy() -> "+AudioPlayerActivity.class.getName());
AudioPlayerServiceBridge.getInstance().doUnbindService(this);
AudioPlayerServiceBridge.getInstance().removeCallback(this);
super.onDestroy();
}
and stop the service(if you want) in onBackPressed():
#Override
public void onBackPressed() {
Log.d(AudioPlayerActivity.class.getName(), "onBackPressed() -> "+AudioPlayerActivity.class.getName());
isPaused = true;
Log.d(AudioPlayerActivity.class.getName(), "Sending message to player service: MSG_RELEASE_PLAYER");
AudioPlayerServiceBridge.getInstance().sendAsyncCall(AudioPlayerService.MSG_RELEASE_PLAYER);
if(mSeekBarChanger != null){
mSeekBarChanger.stopThread();
}
AudioPlayerServiceBridge.getInstance().doUnbindService(this);
stopService(new Intent(this, AudioPlayerService.class));
super.onBackPressed();
}

Stopping a foreground service on Android when the application crashes

I have an Android application where I implement a Service which interacts with some hardware over a Bluetooth serial connection. The setup of this connection is slow, so I decided to keep the service in the foreground, so if/when you want to view another application, the connection is ready to go (pseudocode follows):
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
start();
return (START_STICKY);
}
#Override
public void onDestroy() {
stop();
}
start() and stop() are private methods which start communication with the hardware, and in start's case, creates a Notification for use in startForeground() My Activity will call
#Override
public void onStart() {
super.onStart();
// Start the service
Intent intent = new Intent(getApplicationContext(), MyService.class);
ComponentName theService = startService(intent);
//this is to register the functions I need to handle functions my Activity calls
// to the service
bindService(intent, svcConn, BIND_AUTO_CREATE);
}
#Override
public void onStop() {
super.onStop();
if (theService != null) {
unbindService(svcConn);
theService = null;
if (isFinishing()) {
stopService(new Intent(getApplicationContext(), MyService.class));
}
}
}
I've had to add a "Quit" menu item to make sure that the Service shuts down. Worse, if my app crashes, I have to go in and manually kill the Service. Is there a way to elegantly kill the Service if things go horribly wrong, or am I abusing the purpose of a Service, and should find an alternative method of doing what I'd like to do?
Perhaps you can add a hook for your application's main thread(UI thread) for crash, see below:
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
//Kill the service.
}
});
throw new RuntimeException("Uncaught Exception");

Categories

Resources