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
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
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.
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();
}
i have a background service on my android APP that is getting my GPS position and sending it to a remote db. It work's fine.
The problem is when i want to stop the service.... it doesn't stops :S. Also no exception or errors on logcat have appeared... it simply doesn't stops.
this is the code to start my srvice (with a button):
startService(new Intent(GPSLoc.this, MyService.class)); //enciendo el service
this is the code where I stop it (on the onactivityresult method):
stopService(new Intent(GPSLoc.this, MyService.class));
I have been debugged the app, and i checked that the stopService codeline has been called every time that i debugged it, but it doesn't stops......
i am sure that it's not stopped cause on my database i still recive gps positions from the emulator when i have press the button to stop the service.
what i am doing bad?
Have you implemented onDestroy()? If not, I believe that might be the solution - and you stop your Timer or whatever you're using to run the service within onDestroy().
A service can be stopped by calling its stopSelf() method, or by calling Context.stopService().
See this link for some more information.
i am sure that it's not stopped cause on my database i still recive gps positions from the emulator when i have press the button to stop the service.
You probably are not unregistering your LocationListener.
I had the same problem. I found that if the service has GoogleApiClient connected and still get location update, the stopService() has totally no effect, the service's industry() was not called.
To fix the problem, I created a function to stop the location service in the service code. Call the stopLocationService() from the activity, and then call stopService. Here is the code example:
public class myLocationService extends Service{
...
public void stopLocationUpdates() {
LocationService.FusedLocationApi.removeLocationUpdates(mGoogleApiClient,this);
mGoogleApiClient.disconnect();
}
...
}
In activity,
{
...
if(mService != null && isBound) {
mService.stopLocationUpdates();
doUnbindService();
stopService(new Intent(this, myLocationService.class));
}
...
}
It's very common this situation where I need to stop my service before to finish the process. In some case is not enough with stopService(intent). You should have in mind the onDestroy() implement in my service. Example:
public class MyIntentService extends IntentService {
// Defines and instantiates an object for handling status updates.
private BroadcastNotifier mBroadcaster = null;
private int progress = 0; //THIS IS MY COUNTER FOR EXAMPLE!!!
public MyIntentService() {
super("MyIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
progress = 0;
int tiempo_disponible = intent.getIntExtra("minutos_disponible", 0);
if (mBroadcaster == null){
mBroadcaster = new BroadcastNotifier(this);
}
// Broadcasts an Intent indicating that processing has started.
mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_STARTED);
mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_RUNNING);
while (progress < tiempo_disponible) {
progress++;
try {
Log.i(Constants.TAG, "Procesing " + progress);
mBroadcaster.notifyProgress(progress);
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// Reports that the feed retrieval is complete.
mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_COMPLETE);
}
#Override
public void onDestroy() {
progress = 1000000; // WHITH THAT YOU FINISH THE CICLE IF tiempo_disponible NEVER IS MAYOR THAT 1000000, YOU CAN USE OTHER CONDITIONAL!!!!!!
super.onDestroy();
}
}
In this way, when you have stopped the service using stopService method also you will have stopped the process o counter.
public void stopService(){
context.stopService(intent);
LocalBroadcastManager.getInstance(context).unregisterReceiver(responseReceiver);
responseReceiver = null;
intent = null;
}
Take care!
#yaircarreno
If you are tracking GPS location, you probably used GoogleApiClient.
The concept is that the Service WILL NOT stop,
if an GoogleApiClient instance is still connected within it.
(Or any other issue that need to be destroyed / unregistered first)
So to make it works, implement onDestroy() within your service:
#Override
public void onDestroy()
{
// Unregistered or disconnect what you need to
// For example: mGoogleApiClient.disconnect();
super.onDestroy();
}
I have found the best way to stop a service is to make stop itself. This way you are sure it actually will stop and preserve data integrity. If you want to do it from outside (activity) I usually use a global static attribute.
Per example (Kotlin) if I have MyService, MyActivity and MyObject
My Object
object MyObject{
abort = false
}
MyService
override fun onHandleIntent(intent: Intent?) {
startForeground(id,notification)
for (i in range){
if (MyObject.abort) break
// RUN SOME CODE HERE
}
stopForeground(true)
stopSelf()
}
MyActivity
fun startService() {
startForegroundService(Intent(this, OptimizationService::class.java))
}
fun stopService() {
MyObject.abort = true
}
it could be perhaps that you are creating a new Intent everytime you call the stop service.
stopService(new Intent(GPSLoc.this, MyService.class));
perhaps try :
Intent intnet = new Intent(GPSLoc.this, MyService.class); // create el service
startService(intenet);
stopService(intent);
For those who want to send a request to server periodically, this is my solution. You should have this in your Activity or Fragment Activity
{
private static final Long UPDATE_LOCATION_TIME = 30 * 60 * 1000l; // 30 minute
private AlarmManager alarm;
private PendingIntent pIntent;
...
#Override
protected void onResume() {
super.onResume();
// Run background service in order to update users location
startUserLocationService();
Log.e(TAG, "onResume");
}
#Override
protected void onStop() {
super.onStop();
stopUserLocationService();
Log.e(TAG, "onStop");
}
private void startUserLocationService() {
Log.i(TAG, "Starting service...");
Intent intent = new Intent(MainFragmentHolder.this, ServiceUserLocation.class);
pIntent = PendingIntent.getService(this, 0, intent, 0);
alarm = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Calendar cal = Calendar.getInstance();
alarm.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), UPDATE_LOCATION_TIME, pIntent);
}
private void stopUserLocationService() {
alarm.cancel(pIntent);
Intent intent = new Intent(MainFragmentHolder.this, ServiceUserLocation.class);
stopService(intent);
}
}
my problem solved by removing the added views to WindowManager ondestroy
public void onDestroy() {
isRunning = false;
super.onDestroy();
if (checkBox!=null) {
windowManager.removeView(getlayoutparm(fabsetting,fabrateus,fabexit,true));
windowManager.removeView(checkBox);
}
}
In my case the stopService is called with startService almost simultaneously so no service is there to be stopped. Try delay stopService for a few seconds. :)
#Override
public void onDestroy() {
Log.d(TAG, "onDestroy");
super.onDestroy();
if (mLocationManager != null) {
for (int i = 0; i < mLocationListeners.length; i++) {
try {
mLocationManager.removeUpdates(mLocationListeners[i]);
} catch (Exception ex) {
Log.d(TAG, "fail to remove location listners, ignore", ex);
}
}
}
}