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?
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.
The murder mystery has been solved, and I know what killed my service. Here's what I did:
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.
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.
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:
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.
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
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);
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
Related
There are few questions similar to this on Stack Overflow but none of the solutions are working for me
The problem is with only few devices like OnePlus and MI, The service is getting killed as soon as the user swipes away app from recent app.
I read that these OEM'S use some aggressive strategies to kill services. I just want to know is there any way to keep service running or start it as soon as it gets killed.
I need to run a service which will give location continuously (24/7) in background (This app is only for specific people so no worries about battery life).
I've tried:
running foreground service.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent)
} else {
startService(intent)
}
Also in service onCreate method started with notification
#Override
public void onCreate() {
Log.i("Service", "onCreate");
startForeground(NOTIFICATION_ID, getnotification());
}
returning START_STICKY in onStartCommand
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
initLocationClient();
initLocationSyncThread();
return START_STICKY;
}
re-initiating service in onDestroy and onTaskRemoved but they are not getting called.
binding a service
scheduling alarm manager and start service frequently but play store will give warning that our app is using alarm manager too frequently and its a bad practice. And there is now way using workmanager to schedule for less than 15 min and its still not guaranteed to start after 15 min.
So is there any way to keep running a service other than above options?
If you go through THE LINK, you will find:
Unfortunately, some devices implement killing the app from the recents menu as a force stop. Stock Android does not do this. When an app is force stopped, it cannot execute jobs, receive alarms or broadcasts, etc. So unfortunately, it's infeasible for us to address it - the problem lies in the OS and there is no workaround.
It is a known issue. To save battery, many manufacturers force close the app, thus cancelling all the period tasks, alarms, and broadcast receivers etc. Major manufacturers being OnePlus (you have option to toogle), Redmi, Vivo, Oppo, Huwaei.
Each of these devices have AutoStartManagers/AutoLaunch/StartManager type of optimization managers. Which prevent the background activities to start again. You will have to manually ask the user to whitelist your application, so that app can auto start its background processess. Follow THIS and THIS link, for more info.
The methods to add to whitelist for different manufactures are give in this Stack Overflow answer. Even after adding to whitelist, your app might not work because of DOZE Mode, for that you will have to ignore battery otimizations
Also in case you might be wondering, apps like Gmail/Hangout/WhatsApp/Slack/LinkedIn etc. are already whitelisted by these AutoStart Managers. Hence, there is no effect on their background processes. You always receive timely updates & notifications.
Here are few things which helped little .
In AndroidManifest.xml add line android:enabled="true"
<service
android:name=".service.TrackingService"
android:exported="false"
android:enabled="true"
/>
Inside service Add alarm to wake up again after 2 seconds .
#Override
public void onTaskRemoved(Intent rootIntent) {
initAlarm();
super.onTaskRemoved(rootIntent);
}
private void initAlarm() {
AlarmManager alarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, StartServiceReceiver.class);
PendingIntent alarmIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() +
2000, alarmIntent);
}
Create a receiver StartServiceReceiver and in it just start service again .
For Mi devices we need to allow a permission inside setting to allows service to start in background
The right way to listen to location on the background of to use a fused location API from play services.
Look at the samples here.
https://github.com/googlesamples/android-play-location?files=1
These API's are power efficient, and I would recommend it too.
My IntentService gets fired from 2 places, either by an Alarm or by the Activity and since the duration is related to the amount of data it needs do fetch from the web, from what I understood, I need to keep a partial wakelock.
Here's my implementation:
#Override
protected void onHandleIntent(Intent intent) {
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
WakeLock wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WakeLock");
try {
wakeLock.setReferenceCounted(false);
wakeLock.acquire(3600000);
///other code here
}
catch{
}
finally{
if (wakeLock.isHeld()) {
wakeLock.release();
}
}
My question is: will this work good enough? Will the finally make sure that the wakelock is released in any circumstances? From what I know onHandleIntent handles intent one after another, so there is no risk in having 2 intents/2 wakelocks in the same time.
Later edit:
The IntentService is called in 2 ways:
from my Activity, like
startService(new Intent(context, MyService.class).putExtra()..);
2 from a triggered Alarm using a PendingIntent
PendingIntent pendingIntent = PendingIntent.getService(context, someId, myServiceIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Will the service have enough time to aquire wakelock when ran from the Alarm?
Whether you need to keep wake-lock or not should not be related to the amount of work your Service does - theoretically, device can go to sleep even if the amount of work is small.
Wake-locks should be considered only if you absolutely must ensure that device can't sleep while the Service is running. Cases like this are very rare. Some examples:
Alarm clock applications (need to wake you up even if the device is sleeping)
Real time messaging applications (need to notify you about new messages even if the device is sleeping)
Most applications don't have such a strict timing requirements. For example, the following are NOT good reasons to use wake locks:
Periodic synchronization of data with the server (should be delayed until device awakes)
Displaying current user's location on map (can be obtained when device awakens; but wake-lock will be needed for applications that monitor user's entire route)
If you really need to ensure that the device doesn't sleep during Service execution, then you need to acquire a wake-lock (one of the several types). Let's assume that this is the case here.
You want to be able to start the "wakeful" Service from application's UI (Activity), and using AlarmManager.
Starting from UI
Since the device should be completely awake in order for the user to interact with UI, you can safely assume that if you start the Service in response to UI interaction it will have a chance to acquire a wake-lock (but do it as soon as the Service is started).
Your solution covers this case.
Starting from AlarmManager
Unfortunately, there is no guarantee (at least no documented guarantee) that when AlarmManager starts the Service it will hold a wake lock and allow the Service to acquire its own wake-lock. This means that the device can go to sleep after alarm fired, but before your Service had a chance to acquire the wake-lock.
This means that your solution will "break" in this case.
The only documented scheme in which AlarmManager will help you with wake-locks involves broadcasts:
The Alarm Manager holds a CPU wake lock as long as the alarm
receiver's onReceive() method is executing. This guarantees that the
phone will not sleep until you have finished handling the broadcast.
Once onReceive() returns, the Alarm Manager releases this wake lock.
This means that the phone will in some cases sleep as soon as your
onReceive() method completes. If your alarm receiver called
Context.startService(), it is possible that the phone will sleep
before the requested service is launched. To prevent this, your
BroadcastReceiver and Service will need to implement a separate wake
lock policy to ensure that the phone continues running until the
service becomes available.
This is where WakefulBroadcastReceiver comes in very handy.
Note that if you use this scheme, then there is no need to support a different one for "UI initiated" case - use the same approach in both cases.
You might also want to take a look at this library developed by #CommonsWare (I didn't use it myself though).
I've got a pretty simple app that runs two services in the background, using IntentService with AlarmManager. One is a "messaging" service that sends a JSON request and parses the response, the other polls for location from a LocationManager. The requirement here is that they run on an interval, indefinitely, until manually stopped by the user, with a button - even if the device's screen hasn't been turned on for days. The services must never stop. Battery life is not a concern.
My minimum-supported API is 4.1 and I'm testing on 4.1, 4.2, and 4.4 devices. On my Nexus 7 and GPad, 4.4.4 and 4.4.2, respectively, the services will run indefinitely and work as expected. On a Galaxy Tab 3 running 4.2, the device seemed to go to sleep after 8 hrs. or so of inactivity, and would then quit reporting location and polling. The 4.1 devices seem to do the same.
With that hunch, I added this to the messaging service to manually wake the CPU. This one runs every 60 sec. so I had hoped it would prevent the devices from ever going to sleep.
#Override
protected void onHandleIntent(Intent intent)
{
PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
"com.service.MESSAGE_SERVICE_WAKE_LOCK");
wl.acquire();
//poll for messages via JSON
wl.release();
}
Setting the AlarmManager like so (pi = PendingIntent...passing in the IntentService class):
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), pollInterval, pi);
This appeared to fix things for my own testing, and the devices are consistently reporting location for me on 4.4 and 4.2. However, the 4.1 testers have reported that it still goes to sleep on them after about 8 hrs. I also tested it on a friend's Galaxy S5 (4.4.2) and it quit reporting location after a few hours.
This is my first app and this part has been especially frustrating. Am I doing something wrong? Any idea why these services might stop running? Happy to provide more code if that helps...thought I'd start with this, at least.
Alarms which use PendingIntent for a Service are not guaranteed to keep the device awake long enough for the Service to be started or resumed. In the case of an IntentService, the service automatically dies once it has processed all queued Intent objects. You'll need to use a PendingIntent for a BroadcastReceiver which takes a WakeLock and starts your Service. Your Service can then release the wakelock when it is done with its work, allowing the device to go back to sleep.
If you wish for a service to "always" run, you shouldn't make is a background service.
Make it a foreground service instead:
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.
In order to try out how long your service can run when the OS has low RAM, you can try this sample I've made (which I've posted here, and uses this library I've made), which uses more and more RAM (real RAM, not of the heap). It causes the OS to kill other processes and eventually kill the process of the sample itself.
If you just want your services to run every X hours/minutes, you don't need this at all, and what you've found seems ok.
I am making an app that needs to execute a function each hour even the app is closed.
First of all, I thought to create a service, but during my tests, I realise that android sometimes kills my service. So I was looking for another solution and I found AlarmManager. I have implemented it and it seems to work but I have the doubt if it will happen the same the service or it will run forever? (Until reboot of the mobile...)
Another question, it is necessary to create a new thread to execute the process in alarm manager or it runs directly in other thread?
I have implemented it and it seems to work but I have the doubt if it will happen the same the service or it will run forever? (Until reboot of the mobile...)
It will run until:
the device is rebooted, as you noted, or
the user uninstalls your app, or
you cancel the events yourself, or
the user goes into Settings, finds your app in the list of installed apps, taps on that entry, and clicks the Force Stop button
It's possible that alarms will need to be scheduled again after your app is upgraded (I forget...).
it is necessary to create a new thread to execute the process in alarm manager or it runs directly in other thread??
Unless the work you are going to do will take only a couple of milliseconds, you will want a background thread for it. That leads to two possible patterns:
If you are not using a _WAKEUP-style alarm, use a getService() PendingIntent to send control to an IntentService every hour
If you are using a _WAKEUP-style alarm, you will need to use a getBroadcast() PendingIntent, and have it either invoke your subclass of my WakefulIntentService, or you will need to manage a WakeLock yourself to keep the device awake while you do your bit of work
No, Android won't kill scheduled alarms and they got executed as planned unless app is replaced or device is rebooted. Use broadcast receivers for these events to reschedule Alarms. There's no way to prevent Force Stop as it kills all of your app components and threads totally.
That depends on what Alarm Manager do. If it sends a broadcast, the receiver limit is 10 second.
If it starts an Activity, Service or Intent Service, there is no limit. For Activity and Services you must finish or stop it and for Intent Services until the process is finished. Be aware that you can't have another thread inside Intent Service and you'r limited to code inside the OnHandleIntent.
Also you must consider device state. If it's sleep and you are using Wake Up flag receivers won't need a wake lock, but others do. It won't take long for device to go back to sleep.
Don't waste system resources with a service because Alarm Manager do what you want.
I would appreciate some guidance on how to deal with OS killing a long run service.
Business scenario:
Application records a BTT track which may last for several hours. It can also show the track on map together with relevant statistics.
The application user interface enables the user to start/stop track recording and view the real time track on a map.
After start track recording user can exit the application and turn screen off (to save power), and only a service will remain running to keep the recording update to database (notification shown), until the user starts again the activity and ask for stop recording, which results in service termination.
Issue:
After a variable time, which runs from 40 minutes to 1 hour and a half, the recording service gets killed without any warning. As BTT outings may take several hours, this result in track recording incomplete.
Some additional information:
Service is started with START_STICKY and acquires a PARTIAL_WAKE_LOCK, and runs in the same process as the main activity.
New locations are acquired (and recorded) at user defined rate from 1 second to several minutes.
I know from the Android documentation that this is the expected OS behavior for long running services.
Question:
What is the best architecture design approach to have a well behaved application that could satisfy the business scenario requirements?
I can think of a couple of options (and I don’t like any of them), but I would like guidance from someone how have already faced and solved similar issue:
Use broadcast receiver (ideally connected to Location Manager if that’s possible) to have the service only running when a new location
is acquired?
Do not enable the user to leave the main activity (resulting in pour user experience)?
Have an alarm broadcast receiver restarting the service if needed?
Thanks to all who could share some wisdom on this subject.
I have an app that does a very similar thing. I make sure the service keeps running by making it a foreground task. When I am ready to start running, I call this function, which also sets up a notification:
void fg() {
Notification notification = new Notification(R.drawable.logstatus,
"Logging On", System.currentTimeMillis());
Intent notificationIntent = new Intent(this, LoggerActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
notification.setLatestEventInfo(this, "Logger","Logger Running",
pendingIntent);
startForeground(1, notification);
}
and then to leave foreground mode when logging is finished:
stopForeground(true);