53

I have a service that is defined as:

public class SleepAccelerometerService extends Service implements SensorEventListener

Essentially, I am making an app that monitors accelerometer activity for various reasons while the user sleeps with his or her phone/device on the bed. This is a long-running service that MUST NOT be killed during the night. Depending on how many background apps and periodic processes occur during the night, android sometimes kills off my process, thereby ending my service. Example:

10-04 03:27:41.673: INFO/ActivityManager(1269): Process com.androsz.electricsleep (pid 16223) has died.
10-04 03:27:41.681: INFO/WindowManager(1269): WIN DEATH: Window{45509f98 com.androsz.electricsleep/com.androsz.electricsleep.ui.SleepActivity paused=false}

I do not want to force the user to have 'SleepActivity' or some other activity in my app as the foreground. I can't have my service run periodically, because it is constantly intercepting onSensorChanged.

Any tips? source code is here: http://code.google.com/p/electricsleep/

chubao
  • 5,871
  • 6
  • 39
  • 64
Jon Willis
  • 6,993
  • 4
  • 43
  • 51

7 Answers7

72

For Android 2.0 or later you can use the startForeground() method to start your Service in the foreground.

The documentation says the following:

A started service can use the startForeground(int, Notification) API to put the service in a foreground state, where the system considers it to be something the user is actively aware of and thus not a candidate for killing when low on memory. (It is still theoretically possible for the service to be killed under extreme memory pressure from the current foreground application, but in practice this should not be a concern.)

The is primarily intended for when killing the service would be disruptive to the user, e.g. killing a music player service would stop music playing.

You'll need to supply a Notification to the method which is displayed in the Notifications Bar in the Ongoing section.

David Webb
  • 190,537
  • 57
  • 313
  • 299
  • 2
    Thank you. I had heard of startForeground in passing before, but I didn't expect that startForeground would be a method in Service (though it makes sense that it would be there now that I think about it.) – Jon Willis Oct 04 '10 at 16:09
  • 4
    I have a query. What if I do not want to show any notification but still want to use startForeground() method. – Farhana Haque Oct 17 '11 at 05:53
  • 4
    @farhanahaque - You can't. You should only be using `startForeground()` for if killing the service would be disruptive to the user - e.g. doing something like playing music. This being the case it should be something that the user is aware of and so has visibility of via a Notification. `startForeground()` is not meant to be an alternative to responding to lifecycle events appropriately. – David Webb Oct 17 '11 at 10:06
  • @DaveWebb What can be used to restart such a service in the event of a crash or in the rare event that it does get killed under extreme memory pressure? – gonzobrains Jun 11 '13 at 05:10
  • 1
    @gonzobrains:Inside the onStart command you can set an alarm which will make sure that your service is restarted in case it gets killed in between.Here is the code : PendingIntent localPendingIntent = PendingIntent.getService(this, 0, new Intent(this, UsbService.class), 0); AlarmManager localAlarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE); Calendar localCalendar = Calendar.getInstance(); localCalendar.setTimeInMillis(System.currentTimeMillis()); localCalendar.add(13, 50); localAlarmManager.set(0, localCalendar.getTimeInMillis(), localPendingIntent); – Basher51 Jul 20 '14 at 14:01
  • I want two services to be `onForground`. How can I do that? http://stackoverflow.com/questions/35168209/are-their-any-negatives-impacts-of-launching-two-continues-services-at-the-same – Ruchir Baronia Feb 03 '16 at 04:20
  • I followed your steps, but I have a question. http://stackoverflow.com/questions/35168769/start-in-foreground – Ruchir Baronia Feb 03 '16 at 05:27
16

When you bind your Service to Activity with BIND_AUTO_CREATE your service is being killed just after your Activity is Destroyed and unbound. It does not depend on how you've implemented your Services unBind method it will be still killed.

The other way is to start your Service with startService method from your Activity. This way even if your Activity is destroyed your service won't be destroyed or even paused but you have to pause/destroy it by yourself with stopSelf/stopService when appropriate.

Witold Graca
  • 254
  • 2
  • 4
12

As Dave already pointed out, you could run your Service with foreground priority. But this practice should only be used when it's absolutely necessary, i.e. when it would cause a bad user experience if the Service got killed by Android. This is what the "foreground" really means: Your app is somehow in the foreground and the user would notice it immediately if it's killed (e.g. because it played a song or a video).

In most cases, requesting foreground priority for your Service is contraproductive!

Why is that? When Android decides to kill a Service, it does so because it's short of resources (usually RAM). Based on the different priority classes, Android decides which running processes, and this included services, to terminate in order to free resources. This is a healthy process that you want to happen so that the user has a smooth experience. If you request foreground priority, without a good reason, just to keep your service from being killed, it will most likely cause a bad user experience. Or can you guarantee that your service stays within a minimal resource consumption and has no memory leaks?1

Android provides sticky services to mark services that should be restarted after some grace period if they got killed. This restart usually happens within a few seconds.

Image you want to write an XMPP client for Android. Should you request foreground priority for the Service which contains your XMPP connection? Definitely no, there is absolutely no reason to do so. But you want to use START_STICKY as return flag for your service's onStartCommand method. So that your service is stopped when there is resource pressure and restarted once the situation is back to normal.

1: I am pretty sure that many Android apps have memory leaks. It something the casual (desktop) programmer doesn't care that much about.

Flow
  • 23,572
  • 15
  • 99
  • 156
  • 1
    `START_STICKY` is not meant for that use, but to guarantee that a time-limited task is concluded after a process kill. If for some reason the service doesn't 'stick' (e.g. is unable to reconnect to the XMPP server) the system may decide it tried enough times and simply stop restarting the service, not to mention this would require the service to be kept running in a loop while the connection is up which simply cannot be done. That said, I can't find a solution for keeping/resuming the connection across process kills (other than scheduling periodic reconnection attempts via `AlarmManager`). – Piovezan Oct 20 '14 at 12:32
  • 4
    **`START_STICKY` is exactly what you want to use here.** *"…tried enough times and simply stop restarting the service,…"* No, the Android system is not aware of XMPP connection (re-)tries a sticky Android service performs. It just sees the service running **and keeps it running**. That's the contract the Android API provides if a service is started sticky. *"…mention this would require the service to be kept running in a loop…"*, Yes, that's exactly why (most) Android Services have a Looper (which is usually abstracted away from the user using an Android Service). – Flow Oct 20 '14 at 12:43
  • Sorry, I mistook it from [START_REDELIVER_INTENT](http://stackoverflow.com/a/9441795/2241463). Although a service certainly has a Looper for handling the intent queue passed to it, we are not expected to keep it running that way in the case of a persistent connection, but passing on a single intent and keeping a while loop inside the `Service.onStartCommand()` or `IntentService.onHandleIntent()` method instead, which is not viable. – Piovezan Oct 20 '14 at 13:03
  • *"keeping a while loop inside"*. What makes you think you need such a while loop? You don't need one. – Flow Oct 20 '14 at 13:59
  • If you don't have a `while` loop, the service will be finished as soon as `onStartCommand()` or `onHandleIntent()` completes (if there are no further intents in queue) and the connection will go away (unless the reference to the connection itself is kept by another class). Either way the service is gone and will not be restarted after the app's process is killed. – Piovezan Oct 20 '14 at 14:08
  • No, the service will not be finished, as it's a *sticky service*. The connection won't go away. It will be restarted, also because it's a sticky service. – Flow Oct 20 '14 at 14:49
  • 2
    I checked the docs again and I think you're right. I misunderstood the concept of Android Service. Kept wondering why a `stopSelf()` method was ever needed... Turns out that the Loopers from idling services are rather cheap for the system to keep around if services are not stopped, I just wasn't expecting them to be designed that way. Thanks for the clarification! – Piovezan Oct 20 '14 at 15:29
  • `stopSelf()` comes in handy if you want to be nice to the system and release the resource acquired by your service when you know you wont need him any more (or in the near future). But remember that even if you don't use `stopSelf()` Android will eventually stop your service, and if its being restarted depends on if the service is sticky or not. – Flow Oct 20 '14 at 15:40
5

I had a similar issue. On some devices after a while Android kills my service and even startForeground() does not help. And my customer does not like this issue. My solution is to use AlarmManager class to make sure that the service is running when it's necessary. I use AlarmManager to create a kind of watchdog timer. It checks from time to time if the service should be running and restart it. Also I use SharedPreferences to keep the flag whether the service should be running.

Creating/dismissing my watchdog timer:

void setServiceWatchdogTimer(boolean set, int timeout)
{
    Intent intent;
    PendingIntent alarmIntent;
    intent = new Intent(); // forms and creates appropriate Intent and pass it to AlarmManager
    intent.setAction(ACTION_WATCHDOG_OF_SERVICE);
    intent.setClass(this, WatchDogServiceReceiver.class);
    alarmIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    AlarmManager am=(AlarmManager)getSystemService(Context.ALARM_SERVICE);
    if(set)
        am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeout, alarmIntent);
    else
        am.cancel(alarmIntent);
}

Receiving and processing the intent from the watchdog timer:

/** this class processes the intent and
 *  checks whether the service should be running
 */
public static class WatchDogServiceReceiver extends BroadcastReceiver
{
    @Override
    public void onReceive(Context context, Intent intent)
    {

        if(intent.getAction().equals(ACTION_WATCHDOG_OF_SERVICE))
        {
            // check your flag and 
            // restart your service if it's necessary
            setServiceWatchdogTimer(true, 60000*5); // restart the watchdogtimer
        }
    }
}

Indeed I use WakefulBroadcastReceiver instead of BroadcastReceiver. I gave you the code with BroadcastReceiver just to simplify it.

ivan
  • 359
  • 3
  • 11
4

http://developer.android.com/reference/android/content/Context.html#BIND_ABOVE_CLIENT

public static final int BIND_ABOVE_CLIENT -- Added in API level 14

Flag for bindService(Intent, ServiceConnection, int): indicates that the client application binding to this service considers the service to be more important than the app itself. When set, the platform will try to have the out of memory killer kill the app before it kills the service it is bound to, though this is not guaranteed to be the case.

Other flags of the same group are: BIND_ADJUST_WITH_ACTIVITY, BIND_AUTO_CREATE, BIND_IMPORTANT, BIND_NOT_FOREGROUND, BIND_WAIVE_PRIORITY.

Note that the meaning of BIND_AUTO_CREATE has changed in ICS, and old applications that don't specify BIND_AUTO_CREATE will automatically have the flags BIND_WAIVE_PRIORITY and BIND_ADJUST_WITH_ACTIVITY set for them.

18446744073709551615
  • 16,368
  • 4
  • 94
  • 127
2

I'm working on an app and face issue of killing my service by on app kill. I researched on google and found that I have to make it foreground. following is the code:

public class UpdateLocationAndPrayerTimes extends Service {

 Context context;
@Override
public void onCreate() {
    super.onCreate();
    context = this;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    StartForground();
    return START_STICKY;
}

@Override
public void onDestroy() {


    super.onDestroy();
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}


private void StartForground() {
    LocationChangeDetector locationChangeDetector = new LocationChangeDetector(context);
    locationChangeDetector.getLatAndLong();
    Notification notification = new NotificationCompat.Builder(this)
            .setOngoing(false)
            .setSmallIcon(android.R.color.transparent)

            //.setSmallIcon(R.drawable.picture)
            .build();
    startForeground(101,  notification);

    }
}

hops that it may helps!!!!

Irfan Ul Haq
  • 1,065
  • 1
  • 11
  • 19
2

Keep your service footprint small, this reduces the probability of Android closing your application. You can't prevent it from being killed because if you could then people could easily create persistent spyware

BeRecursive
  • 6,286
  • 1
  • 24
  • 41
  • 1
    Or you could start another service and allow them to watch one another to ensure that it they do get killed then the other restarts it. Ugly but possibly effective – BeRecursive Oct 04 '10 at 15:46
  • 2
    I do need to optimize the memory that my service consumes (I'm going to just dump the data to a SQLite db on file), especially because it inherently grows over time, however, I'm wondering why my service is being killed while other services continue to run. I know there is a priority system built into services- how might I increase my service's priority? – Jon Willis Oct 04 '10 at 15:56
  • 1
    @Jon - If your service is killed it will eventually be restarted. I know you said you didn't want to but the only way to increase priority is by convincing the OS that the service is currently active to the user (in the foreground - using startForeground(int, Notification)) – BeRecursive Oct 04 '10 at 16:10
  • 9
    It is *not* true that keeping it small reduces the probability of it being killed (temporarily until it restarts). The exact algorithm used here has varied across releases, but generally it *ensures* that background services will occasionally be killed/restarted, regardless of the memory they are using. – hackbod Oct 04 '10 at 16:59
  • I want two services to be `onForground`. How can I do that? http://stackoverflow.com/questions/35168209/are-their-any-negatives-impacts-of-launching-two-continues-services-at-the-same – Ruchir Baronia Feb 03 '16 at 04:21