8

I've read just about every Stackoverflow answer that exists on this topic, but none of them worked.

Goal: Keep my service running 24/7, all the time

Problem: Whenever my device is on sleep mode for an hour or more, the service is killed


What I've tried to fix it:

  • Returning START_STICKY from onStartCommand() and using startForeground()

        public int onStartCommand(Intent intent, int flags, int startId) {
    
            notification = makeStickyNotification(); //I've simplified the irrelevant code, obviously this would be a real notification I build
    
            startForeground(1234, notification);
            return START_STICKY;
            }
    

This works fine, and it even restarts my service whenever the device is low on memory, but it is not enough to fix the problem that occurs when my device goes to sleep for a while.

  • Using Alarm Manager in onCreate() of my Activity and in onStartCommand() of my Service to call a Broadcast Receiver that calls my service

            Intent ll24 = new Intent(this, AlarmReceiver.class);
    
            PendingIntent recurringLl24 = PendingIntent.getBroadcast(this, 0, ll24, PendingIntent.FLAG_CANCEL_CURRENT);
    
            AlarmManager alarms = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    
            alarms.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 1000*60, recurringLl24); // Every minute
    

This helps keep my service active, but again, doesn't solve my problem

  • Using Schedule Task Executor to keep it alive

     if (scheduleTaskExecutor == null) {
    
            scheduleTaskExecutor = Executors.newScheduledThreadPool(1);
    
            scheduleTaskExecutor.scheduleAtFixedRate(new mainTask(), 0, 1, TimeUnit.SECONDS);
    
        }
    ...    
    class mainTask implements Runnable {
    
    public void run() {
        // 1 Second Timer
       }
    }
    

    This also just keeps the service active but doesn't keep it alive after a long sleep.

  • Separate task Manifest

    android:launchMode="singleTop"

This did nothing

How can I (1) test this issue without having to put my phone to sleep and check every hour and (2) keep my service running despite the device going to sleep?

Athira
  • 1,048
  • 2
  • 11
  • 31
Ruchir Baronia
  • 6,857
  • 4
  • 45
  • 75
  • 3
    "I have an Android Service that runs 24/7 in the background" -- why not use a foreground service? You need one for Android 8.0+ anyway. – CommonsWare Jul 21 '18 at 21:58
  • @CommonsWare I already have `startForeground(1234, notification);` in the `onStartCommand` of my service, however, the system still kills it after a while. For this reason, I was planning on using the broadcast receiver solution in conjunction with the `startForeground()` and `return START_STICKY`, but I have no way to test if this solution will work. – Ruchir Baronia Jul 21 '18 at 22:12
  • @CommonsWare I know that there is no issue with `START_STICKY` and `startForeground()`, since I tested the app on high ram usage, and the service was able to restart when the memory usage became normal again. Do you think you know why this issue only occurs when the phone is idle and not in use? – Ruchir Baronia Jul 25 '18 at 05:23
  • 3
    What does "the system still kills it after a while" mean, in computer programming terms? How exactly are you determining that "the system still kills it after a while"? In the phrase "Keep my service running 24/7, all the time", what does "running" mean, in computer programming terms? How are you determining that your service is not running? – CommonsWare Jul 25 '18 at 10:44
  • 1
    "_Goal: Keep my service running 24/7, all the time_" - The closest you can get, without any help from the system, is using a foreground service, and for good reasons. – lelloman Jul 29 '18 at 14:02
  • 1
    Question title looks like a suspense movie name. :D, `Who killed my service` – Khemraj Sharma Jul 30 '18 at 05:41
  • What is the endgoal of the service? What are you trying to accomplish by running it permanently? Like others have explained, services are not guaranteed to run permanently and you will have to look into other solutions if possible. – stefana Jul 31 '18 at 11:57
  • @Khemraj Haha you're right! Inspired by you, my answer to my question (at the bottom) includes the steps I went through to "solve the crime" and "identify the murderer"! – Ruchir Baronia Aug 01 '18 at 18:08

5 Answers5

19

Your service was killed by Doze or Standby mode of Android. That was introduced in Android 6.0 (API level 23).

Doze restrictions

The following restrictions apply to your apps while in Doze:

  • Network access is suspended.
  • The system ignores wake locks.
  • Standard AlarmManager alarms (including setExact() and setWindow()) are deferred to the next maintenance window.
  • If you need to set alarms that fire while in Doze, use setAndAllowWhileIdle() or setExactAndAllowWhileIdle().
  • Alarms set with setAlarmClock() continue to fire normally — the system exits Doze shortly before those alarms fire.
  • The system does not perform Wi-Fi scans.
  • The system does not allow sync adapters to run. The system does not allow JobScheduler to run.

So system ignored your Alarm Clocks, Scheduler, etc.

In Android Oreo release Android defined limits to background services.

To improve the user experience, Android 8.0 (API level 26) imposes limitations on what apps can do while running in the background.

Still if app need to run its service always, then we can create foreground service.

Background Service Limitations: While an app is idle, there are limits to its use of background services. This does not apply to foreground services, which are more noticeable to the user.

So create a foreground service. In which you will put a notification for user while your service is running. See this answer (There are many others)

Now what if you don't want a notification for your service. A solution is for that.

You can create some periodic task that will start your service, service will do its work and stops itself. By this your app will not be considered battery draining.

You can create periodic task with Alarm Manager, Job Scheduler, Evernote-Jobs or Work Manager.

  • Instead of telling pros & cons of each one. I just tell you best. Work manager is best solution for periodic tasks. Which was introduced with Android Architecture Component.
  • Unlike Job-Scheduler(only >21 API) it will work for all versions.
  • Also it starts work after a Doze-Standby mode.
  • Make a Android Boot Receiver for scheduling service after device boot.

I created forever running service with Work-Manager, that is working perfectly.

Community
  • 1
  • 1
Khemraj Sharma
  • 46,529
  • 18
  • 168
  • 182
11

The murder mystery has been solved, and I know what killed my service. Here's what I did:

  1. After I realized that startsticky, startforeground, alarmmanager, scheduleTaskExecutor, and even wakelock were unable to save my service, I realized the murderer couldn't be the Android system, because I had taken every measure possible to prevent the system from killing my service and it still would get killed.
  2. I realized I needed to look for another suspect, since the service wasn't dying because of the system. For that, I had to run an investigation. I ran the following command:

    adb shell dumpsys activity processes > tmp.txt

This would give me a detailed log of all the processes running and their system priorities. Essentially, tmp.txt would be the detective in this murder mystery.

  1. I looked through the file with lots of detail. It looked like my service was prioritized properly by the system:

    Proc #31: adj=prcp /FS trm= 0 2205:servicename.service/uID (fg-service)

The above line indicates the exact priority of a process running on the Android device. adj=prcp means the service is a visible foreground service.

At this point, I realized that my service must be encountering some error a couple hours after running, so I let it run and die. After it died, I produced a dumpsys again to examine the error:

  1. At this point, my service wasn't listed as a task in the tmp.txt file. Excited, I scrolled to the bottom of the dumpsys and solved the mystery!

com.curlybrace.ruchir.appName.MyService$2.onForeground(MyService.java:199) at com.rvalerio.fgchecker.AppChecker$2.run(AppChecker.java:118) at android.os.Handler.handleCallback(Handler.java:751) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6123) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)

The stack trace that caused the killing of my service was displayed right there! Essentially, a variable that would check for the foreground app being used would become null after a few hours of inactivity, which would cause an exception, and kill the service!

Key Takeaways: If your service is getting killed, and you've done everything you can to make sure that it shouldn't be killed, perform a dumpsys and examine the nitty gritty of your device's activity process. I guarantee you will find the issue that way.

I still would like to have the bounty awarded to @Khemraj since his answer could be a great solution for someone who hasn't started their service properly. However, I am accepting this answer since it is the solution that actually fixed the issue.

Ruchir Baronia
  • 6,857
  • 4
  • 45
  • 75
  • 2
    :D, Interesting story of misery movie. `Who killed my service?` – Khemraj Sharma Aug 01 '18 at 18:14
  • I still have question, how did you resolve this issue after getting this log? – Khemraj Sharma Aug 01 '18 at 18:15
  • @Khemraj Hey! After I realized that my variable, "appInForeground" (which would normally have the package name of the app that the user is running) would turn null, I simply added a flag to my if statement (when I compared packages) with `if appInForeground != null { return false }` and that solved my issue! – Ruchir Baronia Aug 01 '18 at 18:18
  • Cool, so please add some guiding code in this answer for future comers to let them know this common mistake. – Khemraj Sharma Aug 01 '18 at 18:20
2

onDestroy() is really unreliable and won't be called often that you want. Same for onLowMemory() callbacks. There is no way to take a guaranteed callback if android decides to kill your process or if user decides to Force Stop your app.

That's normal that than user device go to sleep mode, your service dies. Read about wakelocks. Try something like that in your service: In manifest:

<uses-permission android:name="android.permission.WAKE_LOCK" />

In service:

PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
    "tag");
wakeLock.acquire();

But it's rly tricky for user and totally anti-pattern in android world, cuz of battery consumption.

Another option is to trigger service something like every 10 mins. Make pending intent on WakefulBroadcastReceiver(where you can start your service) and schedule it with alarm manager with flag RTC_WAKE_UP

HeyAlex
  • 1,492
  • 1
  • 10
  • 26
  • Yes I agree I want to avoid going for a Wake Lock. What do you mean by 'Another option is to trigger your device by AlarmManager. Trigger your service every 10-30 mins or something like that.' How can I do that? – Ruchir Baronia Jul 23 '18 at 19:20
  • I already have an alarm manager, but it always dies with the service – Ruchir Baronia Jul 25 '18 at 01:52
  • @RuchirBaronia If you want service alive all the time - only wakelocks can help you. If you want to trigger service something like every 10 mins, make pending intent on WakefulBroadcastReceiver(where you can start your service) and schedule it with alarm manager with flag RTC_WAKE_UP – HeyAlex Jul 25 '18 at 13:33
1

Starting from SDK 26 a Service should have its relative "MainActivity" in foreground OR this Service should be started as in foreground using "startForegroundService()". The "startForeground()" doesn't work as expected if the target SDK is 26+ but need the other way I just explained.

After this you can use following code to Kill and restart the App from scratch (yes, even the Service is killed in this way):

Intent mStartActivity = new Intent(context, StartActivity.class);
int mPendingIntentId = 123456;
PendingIntent mPendingIntent = PendingIntent.getActivity(context, mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager mgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
System.exit(0);
emandt
  • 1,903
  • 2
  • 10
  • 15
  • My Target SDK is 22 though. So technically, this shouldn't be a problem, right? Any other solutions? – Ruchir Baronia Jul 22 '18 at 17:49
  • You cannot prevent the System to kill your Service, but you can make it "less often" by setting it as a Foreground Service ("Service.startForeground()"). – emandt Jul 22 '18 at 18:05
1

Doze mode kills services to save battery. Only valid solution for you is to create a foreground service in Oreo and above. https://developer.android.com/about/versions/oreo/background

Muhammad Muzammil
  • 864
  • 1
  • 9
  • 24