13

I have an IntentService and I want to make it sticky with an ongoing notification. The problem is that the notification appears and then disappears immediately. The service continues to run. How should I use startForeground() in an IntentService?

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    super.onStartCommand(intent, flags, startId);
    Notification notification = new Notification(R.drawable.marker, "Notification service is running",
            System.currentTimeMillis());
    Intent notificationIntent = new Intent(this, DashboardActivity.class);
    notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|
        Intent.FLAG_ACTIVITY_SINGLE_TOP);
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
    notification.setLatestEventInfo(this, "App",
            "Notification service is running", pendingIntent);
    notification.flags|=Notification.FLAG_NO_CLEAR;
    startForeground(1337, notification);
    return START_STICKY;
}

@Override
protected void onHandleIntent(Intent intent) {

    String id = intent.getStringExtra(ID);
    WebSocketConnectConfig config = new WebSocketConnectConfig();
    try {
        config.setUrl(new URI("ws://" + App.NET_ADDRESS
                + "/App/socket?id="+id));
    } catch (URISyntaxException e) {
        e.printStackTrace();
    }
    ws = SimpleSocketFactory.create(config, this);
    ws.open();
}

Thanks

Bugs Happen
  • 2,169
  • 4
  • 33
  • 59
Matroska
  • 6,885
  • 14
  • 63
  • 99

2 Answers2

23

This should not be an IntentService. As written, your IntentService will live for a millisecond or so. Once onHandleIntent() returns, the service is destroyed. This should be a regular Service, where you fork your own thread and manage the lifetime of the thread and the service.

The reason your Notification is going away immediately is because the service is going away immediately.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • 5
    Downvoted because: It's likely an incorrect answer. Excerpt from IntentService docs: "All requests are handled on a single worker thread -- *they may take as long as necessary* (and will not block the application's main loop), but only one request will be processed at a time." Nothing said about millisecond or so. https://developer.android.com/reference/android/app/IntentService.html – GregoryK Dec 02 '16 at 07:48
  • 3
    As written, this `IntentService` will live for a millisecond or so, for the simple reason that the code in `onHandleIntent()` will only take a millisecond or so to run. – CommonsWare Jul 17 '17 at 11:04
  • 3
    @GregoryK Actually the answer is correct because `onHandleIntent` method doesn't contains any thread blocking code to prevent `IntentService` destroying. – Arcao Dec 07 '17 at 21:58
  • this answer is not correct, as per documentation: `This method may take several seconds to complete, so it should only be called from a worker thread.` – sam_k Jan 25 '19 at 06:12
6

As documentation for IntentService states:

...the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.

So, I suppose the problem, is that your service is out of work after onHandleIntent() is finished. Hence, service stops itself and notification is dissmissed. So, concept of IntentService is probably not the best case for your task.


As question's title is "StartForeground for IntentService", I would like to clarify some things:

It's really simple to make your IntentService to run in foreground (see code below), but for sure you need to consider several things:

  • Don't run service in foreground if it takes a few seconds only - that might bug your users. Imagine you run short tasks periodically - that will cause notification to appear and disappear - uhhhh*

  • You might need to make your service capable to keep device awake (but that's another story, which is covered pretty well on stackoverflow)*

  • If you queued up multiple Intents to your IntentService the below code will end up showing/hiding notification. (So there might be better solution for your case - as @CommonsWare suggests to extend Service and do everything yourself, however would like to mention - there is nothing in javadoc for IntentService saying that it works only a few seconds - it works as long as it has to do something.)


public class ForegroundService extends IntentService {

    private static final String TAG = "FrgrndSrv";

    public ForegroundService() {
        super(TAG);
    }

    @Override
    protected void onHandleIntent(Intent intent) {

        Notification.Builder builder = new Notification.Builder(getBaseContext())
                .setSmallIcon(R.drawable.ic_foreground_service)
                .setTicker("Your Ticker") // use something from something from R.string
                .setContentTitle("Your content title") // use something from something from
                .setContentText("Your content text") // use something from something from
                .setProgress(0, 0, true); // display indeterminate progress

        startForeground(1, builder.build());
        try {
            doIntesiveWork();
        } finally {
            stopForeground(true);
        }
    }

    protected void doIntesiveWork() {
        // Below should be your logic that takes lots of time
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
GregoryK
  • 3,011
  • 1
  • 27
  • 26
  • 1
    A general rule for computer programming is: never make assumptions about a resource that something else acquired on your behalf. You did not create the background thread used by `IntentService`; hence, you should not make any assumptions about how long you can safely use it. We ran into this very problem with `AsyncTask`, where Google changed the rules for `execute()`, limiting you to a single thread across all tasks. If you want to hold a thread for longer than a brief period, create your own thread. Downvoting for recommending embarrassing programming practices. – CommonsWare Dec 02 '16 at 12:06
  • 3
    @CommonsWare I see your point and understand it. That makes sense. Actually, I was a bit disagree about the statement "As written, your IntentService will live for a millisecond or so." I'm not trying to make assumptions, API java says: "IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) on demand. Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work." – GregoryK Dec 02 '16 at 12:47