Stopping a foreground service on Android when the application crashes - android

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

Related

service does not listen to certain broadcasts

My service:
#Override
public void onCreate() {
if (debug_mode) {Log.i(TAG,"onCreate");}
super.onCreate();
// set receivers
m_filter.addAction("PREPARE_AUDIO");
m_receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (debug_mode) {Log.i(TAG,"broadcast received: " + intent.getAction());}
if (intent.getAction().equals("PREPARE_AUDIO")) {
set_up_audio();
}
}
};
registerReceiver(m_receiver, m_filter);
}
and my activity:
#Override
protected void onStart() {
super.onCreate(savedInstanceState);
if (debug_mode) Log.i(TAG, "onCreate");
setContentView(R.layout.activity_guide);
// start service
startService(new Intent(this, PlayerService.class));
}
#Override
protected void onStart() {
if (debug_mode) {Log.i(TAG,"onStart");}
super.onStart();
// prepare audio
Intent intent = new Intent();
intent.setAction("PREPARE_AUDIO");
sendBroadcast(intent);
}
This won't trigger the BroadcastReceiver in my service. It won't trigger it either if I put the code on onCreate or onResume. It will trigger it, however, if put e.g. on some Listener associated with a Button, or in the activity's onStop callback. Why is that?
Rather than try to send a broadcast right away, just have the service do its setup work in its onCreate() or onStartCommand() method.
Note that using system broadcasts for this is a fairly bad idea, unless your service is in a separate process from your UI. Even then, you need to think through the security, as any app can tell your service what to do, by sending it broadcasts.
If your service and UI will be in the same process, use an in-process event bus (e.g., LocalBroadcastManager, greenrobot's EventBus), not only for improved security, but for better performance.

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.

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

stop IntentService from Activity

I need help with this situation:
I have activity, what starts IntentService.
Service do some job in while cycle and sleep for some time.
Main cycle of service is endless, so I need to stop it from activity again.
I must be able to end activity, start it again and stop IntentService from new "instance" of activity.
public class MyService extends IntentService {
public MyService()
{
super("MyService");
}
public MyService(String name) {
super(name);
}
#Override
protected void onHandleIntent(Intent intent) {
Log.d("SERVICE", "start");
while(true)
{
SystemClock.sleep(1000);
Log.d("SERVICE", "tick");
}
}
#Override
public void onDestroy() {
Log.d("SERVICE", "end");
super.onDestroy();
}
...
Intent intent = new Intent(getApplicationContext(), MyService.class);
startService(intent);
I tried calling stopService(), but it's not working. Is there any solution how to do this?
Thanks in advance.
Service do some job in while cycle and sleep for some time.
IMHO, this is an inappropriate use of IntentService. Please create a regular Service, with your own background thread that you manage yourself.
Is there any solution how to do this?
Create a regular Service, with your own background thread that you manage yourself. For example, you could use a ScheduledExecutorService instead of your sleep() loop, using shutdown() or shutdownNow() in the service's onDestroy().

How can we prevent a Service from being killed by OS?

I am using Service in my application and it needs to run until my application is uninstalled, but the problem is it gets killed by OS.
How can we prevent it from being killed by OS? Or if it gets killed can we restart that service again through programmatically?
You may run the service in the foreground using startForeground().
A foreground service is a service that's considered to be something
the user is actively aware of and thus not a candidate for the system
to kill when low on memory.
But bear in mind that a foreground service must provide a notification for the status bar (read here), and that the notification cannot be dismissed unless the service is either stopped or removed from the foreground.
Note: This still does not absolutely guarantee that the service won't be killed under extremely low memory conditions. It only makes it less likely to be killed.
I've been puzzled by the same issue to yours recently.but now,I've found a good solution.
First of all,you should know that, even your service was killed by OS, the onCreate method of your service would be invoked by OS in a short while.So you can do someting with the onCreate method like this:
#Override
public void onCreate() {
Log.d(LOGTAG, "NotificationService.onCreate()...");
//start this service from another class
ServiceManager.startService();
}
#Override
public void onStart(Intent intent, int startId) {
Log.d(LOGTAG, "onStart()...");
//some code of your service starting,such as establish a connection,create a TimerTask or something else
}
the content of "ServiceManager.startService()" is:
public static void startService() {
Log.i(LOGTAG, "ServiceManager.startSerivce()...");
Intent intent = new Intent(NotificationService.class.getName());
context.startService(intent);
}
However, this solution is just available for the situation of your service being killed by GC.Sometimes our service might be killed by user with Programme Manager.In this situation,your prosses will be killed,and your service will never been re-instantiated.So your service can not be restarted.
But the good news is,when the PM kill your service,it will call your onDestroy method.So we can do something with that method.
#Override
public void onDestroy() {
Intent in = new Intent();
in.setAction("YouWillNeverKillMe");
sendBroadcast(in);
Log.d(LOGTAG, "onDestroy()...");
}
The string of "YouWillNeverKillMe" is a custom action.
The most important thing of this method is,don't add any code before send the broadcast.As system will not wait for completion of onDestroy(),you must send out the broadcast as soon as posible.
Then regist a receiver in manifast.xml:
<receiver android:name=".app.ServiceDestroyReceiver" >
<intent-filter>
<action android:name="YouWillNeverKillMe" >
</action>
</intent-filter>
</receiver>
Finally,create a BroadcastReceiver,and start your service in the onReceive method:
#Override
public void onReceive(Context context, Intent intent) {
Log.d(LOGTAG, "ServeiceDestroy onReceive...");
Log.d(LOGTAG, "action:" + intent.getAction());
Log.d(LOGTAG, "ServeiceDestroy auto start service...");
ServiceManager.startService();
}
Hope this will be helpful to you,and excuse my poor written english.
Override method onStartCommand() in your service class and simply return START_STICKY (as suggested by "Its not blank"). That's all you need. If the process that runs your service gets killed (by a low memory condition for example), the Android system will restart it automatically (usually with some delay, like 5 seconds).
Don't use onStart() anymore as suggested in another answer, it's deprecated.
use
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//**Your code **
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
ref Documentation lifecycle of Service.
Edit added method.
As far i know, onDestroy() will be called only when the service is explicitly stopped(Force Stop). But this method won't get called in case the service gets killed by OS/swiping the Recent Apps list. In those cases another event handler named onTaskRemoved(Intent) gets called. This is due to a defect in Android 4.3-4.4 as per the link here. Try using the below code:-
public void onTaskRemoved(Intent intent){
super.onTaskRemoved(intent);
Intent intent=new Intent(this,this.getClass());
startService(intent);
}
I found another solution of the problem which gurantees that your service will be always alive. In my case, this scheme resloves also the problem with FileObserver, which stops work after some period of time.
Use an activity (StartServicesActivity) to start the service (FileObserverService) as Foreground service.
Use BroadcastReceiver class (in example CommonReceiver) to restart your service in some special situations and in case it was killed.
I used this code in my app "Email Pictures Automatically"
https://play.google.com/store/apps/details?id=com.alexpap.EmailPicturesFree
Here is CommonReceiver class.
public class CommonReceiver extends BroadcastReceiver {
public void onReceive(Context paramContext, Intent paramIntent)
{
paramContext.startService(new Intent(paramContext, FileObserverService.class));
}
}
Here is its definition in AndroidManifest.xml just before application closing tag.
<receiver android:name="com.alexpap.services.CommonReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.USER_PRESENT"/>
</intent-filter>
</receiver>
Start service in StartServicesActivity activity.
Intent iFileObserver = new Intent(StartServicesActivity.this, FileObserverService.class);
StartServicesActivity.this.startService(iFileObserver);
Here is onStartCommand() method of the service.
public int onStartCommand(Intent intent, int flags, int startId) {
int res = super.onStartCommand(intent, flags, startId);
/*** Put your code here ***/
startServiceForeground(intent, flags, startId);
return Service.START_STICKY;
}
public int startServiceForeground(Intent intent, int flags, int startId) {
Intent notificationIntent = new Intent(this, StartServicesActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("File Observer Service")
.setContentIntent(pendingIntent)
.setOngoing(true)
.build();
startForeground(300, notification);
return START_STICKY;
}
I tested this code using Task Killer app, and each time the service was killed, it was restarted again almost immediately (performs onStartCommand()). It is restarted also each time you turn on the phone and after rebooting.
I use this code in my application, which emails every picture you take with your phone to predefinde list of emails. The sending email and list of receiving emails are set in another activity and are stored in Shared Preferences. I took about 100 pictures in several hours and all they were sent properly to receiving emails.
#Override
public void onDestroy() {
super.onDestroy();
startService(new Intent(this, YourService.class));
}
write above code in your service and your service will never stop even user want to destroy it or they want to kill it it will never kill untill your app not get uninstall from your device
You can try to start your service repeatedly, for example every 5 sec.
This way, when your service is running, it will perform onStartCommand() every 5 sec. I tested this scheme and it is very reliable, but unfortunately it increases slightly phone overhead.
Here is the code in your activity where you start the service.
Intent iFileObserver = new Intent(StartServicesActivity.this, FileObserverService.class);
PendingIntent pendingIntentFileObserver = PendingIntent.getService(StartServicesActivity.this, 0, iFileObserver, 0);
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
Date now = new Date();
//start every 5 seconds
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, now.getTime(), 5*1000, pendingIntentFileObserver);
And here is onStartCommand() of the service.
//class variable
public static boolean isStarted = false;
public int onStartCommand(Intent intent, int flags, int startId) {
int res = super.onStartCommand(intent, flags, startId);
//check if your service is already started
if (isStarted){ //yes - do nothing
return Service.START_STICKY;
} else { //no
isStarted = true;
}
/**** the rest of your code ***/
return Service.START_STICKY;
}
First create service in another process, and write broadcaster which runs in recursion in time intervals
protected CountDownTimer rebootService = new CountDownTimer(9000, 9000) {
#Override
public void onTick(long millisUntilFinished) {
}
#Override
public void onFinish() {
sendBroadcast(reboot);
this.start();
Log.d(TAG, "rebootService sending PREVENT AUTOREBOT broadcast");
}
};
After that register broadcast receiver in main process also with timer recursion that is launched after first broadcast from service arrived
protected static class ServiceAutoRebooter extends BroadcastReceiver {
private static ServiceAutoRebooter instance = null;
private RebootTimer rebootTimer = null;
private static ServiceAutoRebooter getInstance() {
if (instance == null) {
instance = new ServiceAutoRebooter();
}
return instance;
}
public class RebootTimer extends CountDownTimer {
private Context _context;
private Intent _service;
public RebootTimer(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
#Override
public void onTick(long millisUntilFinished) {
}
#Override
public void onFinish() {
_context.startService(_service);
this.cancel();
Log.d(TAG, "Service AutoRebooted");
}
}
#Override
public void onReceive(Context context, Intent intent) {
if (rebootTimer == null) {
Log.d(TAG, "rebootTimer == null");
rebootTimer = new RebootTimer(10000, 10000);
rebootTimer._context = context;
Intent service = new Intent(context, SomeService.class);
rebootTimer._service = service;
rebootTimer.start();
} else {
rebootTimer.cancel();
rebootTimer.start();
Log.d(TAG, "rebootTimer is restarted");
}
}
}
Service will be auto-rebooted if time at RebootTimer (main process) expires, which means that "PREVENT AUTOREBOT" broadcast from service hasn't arrived
i found a solution .... late answer but i wanted to answer...
we can send a broadcast in the ondestroy of the service and create a receiver that receives the broadcast and starts the service again.... when it is destroyed by any reasons...
pls try following:
final Messenger mMessenger = new Messenger(new IncomingHandler());
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
default:
super.handleMessage(msg);
}
}
}
#Override
public void onCreate() {
super.onCreate();
makeServiceForeground();
}
#Override
public IBinder onBind(Intent arg0) {
return mMessenger.getBinder();
}
private void makeServiceForeground() {
IActivityManager am = ActivityManagerNative.getDefault();
try {
am.setProcessForeground(onBind(null), android.os.Process.myPid(), true);
} catch (RemoteException e) {
Log.e("", "cant set to foreground" + e.toString());
}
}
also need add in manifest.xml
<uses-permission android:name="android.permission.SET_PROCESS_LIMIT"/>

Categories

Resources