5

I am trying to keep alive a service that reacts to screen on/off changes. The service would work perfectly for awhile, but then eventually it would be killed. I am now attempting to use startForeground() to keep the process alive, but it still seems to be dying. I understand that there is no way to keep a process alive forever, without error, but I feel like I must be doing something wrong, as adding startForeground() added no extra life to the process. Also, as a side note, Logcat complains about a leak, as unregisterReceiver() is not called (except manually by a button press from the user).. however, due to the nature of what I am trying to accomplish, the receiver needs to run until explicitly told to stop.

Any suggestions?

Relevant Code:

public class UpdateService extends IntentService {

        public UpdateService() {
        super(null);

    }

        @Override
        protected void onHandleIntent(Intent intent) {

            final int myID = 1234;


            Intent notificationintent = new Intent(this, Main.class);
            notificationintent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
            PendingIntent pendIntent = PendingIntent.getActivity(this, 0, notificationintent, 0);


            Notification notice = new Notification(R.drawable.icon_image, "***********", System.currentTimeMillis());


            notice.setLatestEventInfo(this, "*************", "***********", pendIntent);

            notice.flags |= Notification.FLAG_NO_CLEAR;
            startForeground(myID, notice);

            boolean screenOn = intent.getBooleanExtra("screen_state", false);


// Blah Blah Blah......


        }

        @Override
        public IBinder onBind(Intent arg0) {
            // TODO Auto-generated method stub
            return null;
        }

}
Frank Bozzo
  • 15,033
  • 6
  • 25
  • 29
  • 1
    I had similar issue, in my case i had to keep the flash light on , but it was stopped in few seconds ,only viable option that i found is to use another Service inside intent service and run that service as Foreground service and this solved the problem.Let me know if you want me to post further details on this – user2548816 Aug 14 '16 at 19:00
  • hi @user2548816, I know its been a while but I would be interested in hearing your solution. Can you please elaborate more? – kilokahn Aug 06 '18 at 20:25

3 Answers3

7

(Updated) I suppose there are the following possible cases:

1) 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, it might be that your service is normally stopped after onHandleIntent() is finished (especially, as you mentioned that startForeground() added no extra life to the process).

2) You might try to check if it's somehow can be related to device going to sleep (or maybe you are starting your service by schedule and awkening device - in this case you might need to acquire WakeLock)

3) In the very rare cases, the system still can kill foreground process - so if you do a lot of allocations (really lot) and some other work in onHandleIntent() (instead of "Blah Blah Blah" at your code) - you might run into it - but I suppose it's not the case.

As question's title is "Using startForeground() with an IntentService" - would like to clarify that too: I believe nothing (architecture, best practices, android framework, java docs for IntentService) prevents you from running your intent service as a foreground. Of course you need to thought out carefully its usage and whether you actually need a foreground service. Some ideas are available here. For sample code see below. (Sample code can end up showing multiple notifications if you queued multiple jobs/intents into IntentService, so there might be better solution depending on your need.)


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();
        }
    }
}
Community
  • 1
  • 1
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 abysmal programming practices. – CommonsWare Dec 02 '16 at 12:05
  • 3
    @CommonsWare I see your point and understand it. That makes sense. Actually, I was quite disagree about the statement you made "It is not supposed to live for more than a few seconds, typically." I'm not trying to make assumptions, but merely read java docs and java docs doesn't have any assumptions like "It is not supposed to live for more than a few seconds, typically." – GregoryK Dec 02 '16 at 12:49
6

IntentService automatically shuts down when onHandleIntent() completes. It is to perform a brief bit of work when something occurs. It is not supposed to live for more than a few seconds, typically.

I am going to assume that this is tied to what I wrote in your last question in this area.


Something in the rest of your app will be registering and unregistering the BroadcastReceiver for the screen on/off events -- apparently, from your comments, it is an activity. If what you want to do when those things occur is very very quick (on the order of a few milliseconds), just do the work in onReceive(), and be done with it.

If, on the other hand, you have more work than a few milliseconds' worth, you will need to have that work be done by something else that can do the work on a background thread. For example, if the "something in the rest of your app" that registered the BroadcastReceiver is indeed an activity, the activity might just spawn an AsyncTask to do the work.

Another possibility is to use an IntentService. You elected to go down this path in your work prior to that last question. I do not know why. Regardless, an IntentService, like an AsyncTask, is supposed to be a short-lived component -- you send it a command via startService(), it does its work in onHandleIntent(), and it goes away.

With all that in mind, let's talk about your specific points.


The service would work perfectly for awhile, but then eventually it would be killed.

It is unclear what you think "killed" means. An IntentService automatically goes away once onHandleIntent() returns, and that ideally should occur within a handful of seconds.

I am now attempting to use startForeground() to keep the process alive, but it still seems to be dying.

Again, it is unclear what you think "dying" means. Bear in mind that the mere existence of an IntentService does not stop the CPU from shutting down once the screen turns off, and startForeground() has nothing to do with that.

Also, as a side note, Logcat complains about a leak, as unregisterReceiver() is not called (except manually by a button press from the user)..

You also need to unregister the receiver before the user exits the activity. It is usually a good idea to call registerReceiver() in onResume() and unregisterReceiver() in onPause().

Community
  • 1
  • 1
CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • 15
    There is nothing in the documentation that indicates "It is not supposed to live for more than a few seconds". You're making that up. Services can live forever if they want to. An IntentService is no different than implementing the normal Service class. – Johann Nov 05 '12 at 07:35
  • 6
    @AndroidDev: "Services can live forever if they want to" -- except that they can't, as the user and/or the OS will get rid of them when desired. "An IntentService is no different than implementing the normal Service class" -- presumably, an `Integer` is no different than the normal `Object` class, in your opinion. – CommonsWare Nov 05 '12 at 11:08
  • 8
    From the documentation: `If the service is declared to run in the foreground (discussed later), then it will almost never be killed` - http://developer.android.com/guide/components/services.html . The same documentation also says: `(IntentService) is the best option if you don't require that your service handle multiple requests simultaneously`, but it makes no mention of IntentService being a bad option if you want a long- / indefinitely-running service. – ban-geoengineering Sep 06 '14 at 09:33
  • 7
    Further, if you take a look at the source code for IntentService - http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.2_r1.1/android/app/IntentService.java - it says `(Requests) may take as long as necessary`. I.e., there is no mention of a few seconds or a handful of seconds, etc. It therefore appears that a foreground IntentService does seem to be the best and simplest way to achieve a truly ongoing service. – ban-geoengineering Sep 06 '14 at 09:40
  • the thing that i have understood working with intent service is that, it call stop self once all the code inside onHandelIntent function is executed, And to solve this issue i had to start another service inside the onHandleIntent and i have made that service as a foreground service. (May not be the best way but still i achieved what i wanted ) – user2548816 Aug 14 '16 at 19:05
  • 2
    Downvoted because: 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 few seconds or so. So, answer is misleading about IntentServices https://developer.android.com/reference/android/app/IntentService.html – GregoryK Dec 02 '16 at 07:49
0

If memory is low and your consuming too much memory and your sitting in the background for too long then you WILL be killed by the ActivityManager.

JoxTraex
  • 13,423
  • 6
  • 32
  • 45