Android's WorkManager not running when scheduled from a service - android

My works scheduled in the WorkManager are not always being executing when the app in the background. This is how I schedule them:
Constraints myConstraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build();
OneTimeWorkRequest batchingWork = new OneTimeWorkRequest.Builder(BatchingJobWorker.class)
.setConstraints(myConstraints)
.setInitialDelay(minLatencyJobs, TimeUnit.MILLISECONDS)
.setBackoffCriteria(BackoffPolicy.LINEAR, 10 * 1000, TimeUnit.MILLISECONDS)
.addTag(BATCHING_JOB_WORK)
.build();
WorkManager.getInstance(context)
.enqueueUniqueWork(BATCHING_JOB_WORK, ExistingWorkPolicy.APPEND, batchingWork);
I read from this other thread that the problem could be that the Android operating system could be killing / force stopping the app so that the services, schedulers, alarms, broadcast receivers, etc., are no longer working.
Android. Is WorkManager running when app is closed?
However, in my case I'm scheduling the job from a background service and the delays I'm setting are small (1 min or less), so I know that the app is running at that moment. Also, I'm testing this with my testing devices, so I know no one is killing the app by swipping it from the recents menu. This is the exact procedure I'm following:
An MQTT message is received in the background service
I request a PARTIAL_WAKE_LOCK
Some code is executed (I know this code is being called, so I suppose the MQTT and background service part is working fine).
I schedule the work in the WorkManager.
As I said, in some cases the doWork() of the worker is not executed. These are some tests I did:
When the work is scheduled in a running activity with a button click and the app is in foreground, it works as expected.
Disabling the battery saver mode in some phones seems to help, but It doesn't fix the problem in 100% of the cases.
Some smartphones or ROMs seem to have higher probability of executing the scheduled work.
When the worker doesn't executes I don't see any exception in the log.
The failure probability seems to increase with the amount of previous jobs executed in the background. In fact, when no more works are being executing, usually reinstalling the app fixes the problem.
With the device connected to power seems to work better.
Could anyone with me a clue about what is happening here? Am I doing something wrong?
Thanking you in advance.

Related

Android 12 expedited job notification

After upgrading my phone to Android 12 I started to some unexpected behavior with the expedited work job. That is the code I had:
val workRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.build()
WorkManager.getInstance(context).enqueueUniqueWork(UNIQUE_WORK_NAME, ExistingWorkPolicy.REPLACE, workRequest)
And also I had the getForegroundInfo() overrided to return ForegroundInfo instance.
On Android < 12 my work job worked fine: it is long-running and when it got started, the notification was shown in notification trey, and the job was done.
Now, with Android 12 I don't see any notification icon and if I'll turn the screen off, the job is cancelled exactly in one minute. And immediately gets relaunched.
If I explicitly call setForeground() in doWork() method (I don't know what is the difference), it slightly change the behavior: the notification starts to show (not at once for some reason) but it goes away after some time, when the job is still running. The job itself doesn't get cancelled.
What does it all mean? Do I do something wrong? All I want is to launch long-running expedited job with a notification.
Upd: I've noticed that the same thing happens when I just create a foreground service. It gets stopped in exactly one minute if I navigate away from the app (so the app is in background). So, it seems that the problem is in a foreground service itself, since android uses it implicitly when an expedited work is started.
Did you override getForegroundInfo() in your Worker class? According to the docs:
Any ListenableWorker must implement the getForegroundInfo method if
you would like to request that the task run as an expedited job
If I explicitly call setForeground() in doWork() method (I don't know
what is the difference), it slightly change the behavior:
https://developer.android.com/topic/libraries/architecture/workmanager/advanced/long-running
ListenableWorker now supports the setForegroundAsync() API, and CoroutineWorker supports a suspending setForeground() API. These APIs allow developers to specify that this WorkRequest is important (from a user perspective) or long-running.
On Android < 12 my work job worked fine: it is long-running and when
it got started, the notification was shown in notification trey, and
the job was done.
To maintain backwards compatibility for expedited jobs, WorkManager might run a foreground service on platform versions older than Android 12. Foreground services can display a notification to the user.
When targeting Android 12 or higher, foreground services remain available to you through the corresponding setForeground method.
https://developer.android.com/topic/libraries/architecture/workmanager/how-to/define-work#worker
https://developer.android.com/static/images/guide/background/workmanager_main.svg
Workers don't know whether the work they're doing is expedited or not. But workers can display a notification on some versions of Android when a WorkRequest has been expedited.
To enable this, WorkManager provides the getForegroundInfoAsync() method, which you must implement so WorkManager can display a notification to start a ForegroundService for you where necessary.
Long story short - there is a lot of might, can, etc. What you need to do is debug the JobScheduler and figure out what exactly is happening and then try to figure out why.
https://developer.android.com/topic/libraries/architecture/workmanager/how-to/debugging#use-alb-shell0dumpsys-jobscheduler
Check what exactly Constraint is satisfied or not.
What is your Quota and Internet usage(you have an amount in
milliseconds)
What is your PowerBucket(you might ask the user to exempt you from
battery optimization)
History of executed jobs

Scheduling task with JobScheduler in Android causes UI freeze on app launch

While trying to find out why my android app (java) was not responding for the first 2-3 seconds after launching (UI elements were loaded, apart from menu options, but I could not click anything), I located the delay being caused by the command that schedules a task with JobScheduler.
Basically I have an App class that extends Application, and there I do set a task with JobScheduler that would run every 30 minutes and perform some action (update some information and provide notification if their value was changed).
After creating the Builder containing all details for that task, i run the command scheduler.schedule(info) and by commenting it out I found out that this is the reason my app is unresponsive for 2-3 seconds after launching.
private void scheduleJob() {
ComponentName componentName = new ComponentName(this, myRecurringTask.class);
JobInfo info = new JobInfo.Builder(JOB_ID, componentName)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPersisted(true)
.setPeriodic(1800000)
.build();
JobScheduler scheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
scheduler.schedule(info);
}
I have tried running it in a Thread hoping that it'll run in background and not freeze the UI while running, among other similar practices, but I have yet to manage to run it in background and not have it affect the UI's responsiveness.
I suspect that the lifecycle of the App class might be the reason of that issue but I'm not experienced enough on being sure about it, or knowing how to overcome it.
Have anyone had any similar issue? And if so, how did you get it resolved?
Thank you
UPDATE 2020/02/18: As a workaround I am now checking first if job is already scheduled and only if it's not I go ahead and schedule again.
When I wrote the scheduling part, I saw in the documentation that it will overwrite the job if it already exists, so I did not worry about it, but since it's affecting app's performance, it makes sense to not do it every single time.
With that being said, I'm still looking for a better solution, that's just a workaround to limit the times it gets frozen, the UI still freezes every time job gets scheduled.
After a lot of frustration trying to overcome the issue I was experiencing, I did a more in-depth troubleshoot where I found out that the reason app freezes is that the first job run is running right when I schedule it.
It makes sense but I never thought of it being the issue.
I have made a new question on my quest to overcome that new issue, being able to skip the first job run...
JobScheduler - How to skip first job run of periodic job?

JobScheduler Android Oreo Issue

In my current application for a company Im having a ForegroundService that is running while the workers are on duty (logged in to the app).
It is important to keep that foreground service running while users are logged in. To recover from cases where this foreground service gets killed somehow (User task swipe accidently or System kill) ive implemented a periodic JOB with Jobscheduler which reactivates the ForegroundService in case it gets shut down. This technique works well pre Android 8 OREO. START_STICKY and other techniques alone did not do the trick for me.
In Android 8, as soon as the foreground service gets killed, the periodic job gets killed as well. Im getting the notification in logcat that the job is not allowed to run. To my understanding, Jobs schould be able to run even when app is in background or killed. And its working on pre OREO devices the way it should.
To my knowledge, I can fix that by enable the option "autostart" in app settings. But since there is no way to know if employees tunred that on, its not a reliable thing as well.
So my questions:
- Why does the Job scheduler stops working as it should in Android 8?
- Are there any other reliable techniques I could use to let my ForegroundService recover from shutdowns in ANDROID OREO?
Ive read https://medium.com/exploring-android/exploring-background-execution-limits-on-android-oreo-ab384762a66c but that did not answer my questions
Thank you very much
did you try to put
.setPersisted(true)
Note: Requires the RECEIVE_BOOT_COMPLETED permission.
you can also see this question.
Job Scheduler not running on Android N
reading some references:
1- The services in andreo oreo if posses a buggle oreo will kill it for energy loss in 7 is a warning but in 8 applies more strict
2 - The jobs scheduler has a time of 15 minutes and avaces is not executed in the precise time debit that they look for the precise moment to execute the event without that the battery of the cellular one is descaste
I recommend that you use this library that provides evernote that is very good and very specific:
https://github.com/evernote/android-job

Android. Is WorkManager running when app is closed?

I want to schedule nightly database updates. So I use new Android WorkManager. My understanding is that once scheduled it will always run in the background independently from the app's lifecycle.
Is that right? My first tests show that Work is only being performed when the app is running.
val locationWork = PeriodicWorkRequest.Builder(UpdateDatabaseWorker::class.java, 24, TimeUnit.HOURS)
.addTag("DATABASE_UPDATE_SERVICE")
.build()
WorkManager.getInstance().enqueue(locationWork)
Based on various issues reported on the WorkManager bugtracker, their documentation is not completely precise about the exact behavior of the WorkManager in such edge cases.
On certain devices, apps are force stopped when the app is cleared from task manager, so that part is expected. ... source
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. source
The only issue that we have come across is the case where some Chinese OEMs treat swipe to dismiss from Recents as a force stop. When that happens, WorkManager will reschedule all pending jobs, next time the app starts up. Given that this is a CDD violation, there is not much more that WorkManager can do given its a client library. source
To add to this, if a device manufacturer has decided to modify stock Android to force-stop the app, WorkManager will stop working (as will JobScheduler, alarms, broadcast receivers, etc.). There is no way to work around this. Some device manufacturers do this, unfortunately, so in those cases WorkManager will stop working until the next time the app is launched. source
With intense testing of a OneTimeWorkRequest (without constraints) on a Pixel 2 XL with stock android the behavior is the following:
Task manager close:
Work continues (after a bit)
Reboot device (work running):
Work continues after reboot done
App info "Force stop":
Work stops, will only continue when app is started again
Reboot device (work was "Force Stopped"):
Work does not continue until the app is started again
You can find a complete list of different OEM behaviors on dontkillmyapp.com. It seems the Android team also acknowledges this issue and added a test for this into their CTS test for Android Q. source
My understanding is that once scheduled it will always run in the
background independently from the app's lifecycle. Is that right?
Yes. Based on the documentation
The task is still guaranteed to run, even if your app is force-quit or
the device is rebooted.
WorkManager chooses the appropriate way to run your task based on factors such as the device API level and the app state. If WorkManager executes one of your tasks while the app is running, WorkManager can run your task in a new thread in your app's process. If your app is not running, WorkManager chooses an appropriate way to schedule a background task--depending on the device API level.
WorkManager might use JobScheduler, Firebase JobDispatcher, or AlarmManager depending on the API level. It will repect the Doze and conaider all other constraints before executing the Work. You can expect some delay in Doze mode since it could wait for maintenance window.
Note:
WorkManager is intended for tasks that require a guarantee that the system will run them even if the app exits, like uploading app data to a server. It is not intended for in-process background work that can safely be terminated if the app process goes away; for situations like that, we recommend using ThreadPools.
This is what documentation is saying:
Note: WorkManager is intended for tasks that require a guarantee that the system will run them even if the app exits, like uploading app data to a server. It is not intended for in-process background work that can safely be terminated if the app process goes away; for situations like that, we recommend using ThreadPools.
But there must be some condition. if that condition meet then WorkManager will run the task (this is important). Conditions like "only while device is charging and online"
Read this carefully, The WorkManager attempts to run your task at the interval you request, subject to the constraints you impose and its other requirements.
Here I found a good tutorial about how to use WorkManager for scheduling tasks : https://android.jlelse.eu/how-scheduling-work-with-new-android-jetpack-component-workmanager-852163f4825b

JobScheduler run job at most once every time the conditions are met

NOTE: THIS IS NOT A DUPLICATE, PLEASE READ THE QUESTION
I want to run a job every time the device is Charging & ON WIFI. This job has to run at most once every time these conditions are met.
This means that if I leave the phone charging overnight with wifi ON the job should not repeat itself.
Only when I unplug and replug the job is allowed to execute again. Same goes for when I turn wifi off and on.
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setRequiresCharging(true)
//.setPeriodic(TimeUnit.SECONDS.toMillis(10))
.setPersisted(true)
Job scheduler provides methods like setPeriodic but that will run my job every X amount of time. Not really what I want.
The job is not critical, I don't need it to be executed right away after the conditions are met, and I'm also OK with it not executing at all sometimes (meaning it's ok for it not to be run when conditions are met for a short period of time)
Is it possible to achieve this using job scheduler? The documentation on this is pretty scare.
If you're not using setPeriodic, then your job would only run once when your other constraints are set. However, your requirements mean you need to schedule a new job when you leave those conditions - JobScheduler does not offer that API, nor does Android offer any API that does that that also works with Android 8.0's Background Execution Limits (with the exception of continuously running a foreground service).

Categories

Resources