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

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?

Related

Android's WorkManager not running when scheduled from a service

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.

Still getting "Excessive network usage (background)" warning after running network code in Foreground service

By referring to Proper way to tackle and resolve "Excessive network usage (background)"
After few months of debugging, we are now able to run all network related code in Foreground service.
However, we are still getting "Excessive network usage (background)" warning in Android Vital.
When foreground service code is executed, a notification UI will always shown in status bar area.
When we "quit" our app, we launch the foreground service, using WorkManager. The WorkManager will return immediately, after foreground service is launched.
public class SyncWorker extends Worker {
#NonNull
#Override
public Result doWork() {
final Intent intent = new Intent(WeNoteApplication.instance(), SyncForegroundIntentService.class);
ContextCompat.startForegroundService(
WeNoteApplication.instance(),
intent
);
return Result.success();
}
}
public class SyncForegroundIntentService extends IntentService {
private static final String TAG = "com.yocto.wenote.sync.SyncIntentService";
public SyncForegroundIntentService() {
super(TAG);
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
final Context context = WeNoteApplication.instance();
NotificationCompat.Builder builder = new NotificationCompat.Builder(...
startForeground(SYNC_FOREGROUND_INTENT_SERVICE_ID, builder.build());
// Perform networking operation within foreground service.
stopForeground(true);
Side note
We don't think we are sending a lot of data. As you can see our latest release, we fall in the lowest range (0 - 5 MB per hour)
Any idea why we're still getting "Excessive network usage (background)"? Apparently, we no longer perform any networking call in background.
We utilize https://developer.android.com/reference/android/app/Service.html#startForeground(int,%20android.app.Notification) and https://developer.android.com/reference/android/content/Context.html#startForegroundService(android.content.Intent)
You are using a Worker to invoke the ForegroundService. From the Worker's documentation:
Worker classes are instantiated at runtime by WorkManager and the
doWork() method is called on a pre-specified background thread (see
Configuration.getExecutor()). This method is for synchronous
processing of your work, meaning that once you return from that
method, the Worker is considered to be finished and will be destroyed. (...) In case the work is preempted for any reason, the same instance of Worker is not reused. This means that doWork() is called exactly once per Worker instance. A new Worker is created if a unit of work needs to be rerun.
A ForegroundService is a Service that you put in the foreground state, that means, the system will not kill the process if it needs CPU or if your app is closed. This and only that.
I wasn't able to find the documentation of Android Vital proving this, so this is only my suspicion, but I'm quite positive this is the case: this means that regardless of the fact that you use ForegroundService or not, Android Vital still counts this as background work.
A proper way to move your app’s mobile-network usage to the foreground is to call DownloadManager with proper visibility setting set (as stated in the link you have provided). Please let me know if that helps - if not we'll try something different. Btw, were you able to narrow down the statistics to a specific API version? (there were some background threading changes in 9.0 and 8.0 so this can also be a clue)
If you're doing this:
When we "quit" our app, we launch the foreground service, using WorkManager. The WorkManager will return immediately, after foreground service is launched.
then you're technically scheduling a worker probably with network constraints every time the user shuts the app.
From the documentation for "Excessive background network usage" link
When an app connects to the mobile network in the background, the app wakes up the CPU and turns on the radio. Doing so repeatedly can run down a device's battery
Thus, even though you're not sending the threshold data of 50MB/ 0.10% of Battery session you're getting this warning cause your app is technically waking up the CPU a lot in the background (for network pings).
Though i'm not sure if this is the problem or not, what you can do is, since even the worker documentation guide says:
WorkManager is intended for tasks that are deferrable—that is, not required to run immediately—and required to run reliably even if the app exits or the device restarts
you can try these:
Scheduling a foreground service as soon as the user shuts the app, instead of scheduling a worker, checking inside the onHandleIntent whether the user is online or not (having a connection and for devices above 7, a flowing internet connection as well).
You can try scheduling a worker to run periodically, lets say every few hours based on your business-side requirements, this could be problem if that's not how you wish to back-up the data, but it serves the real purpose of the worker being a deferred task and not something to be executed immediately.
Not sure about this, never tried, but theoretically looks valid, you can use a Unique Work with a mode called REPLACE to replace the worker and have an initial delay of lets say 30mins, this is a hack, but this will delay running of your worker for 30mins, and in the meanwhile if the user opens and shuts the app again, it will replace the old worker with a new one. This solution has its own drawbacks as well, like sometimes the task wont be scheduled if the user is using the app constantly. But will reduce the total number of times the worker had ran.
Finally, the architecture you're using is valid, the whole thing about using a FG service and a worker to schedule it, just that you're doing it quite often. Source

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).

Android - JobScheduler or BroadcastReceiver?

In my Android app I need to do some work every time the user plugs their device. For this purpose right now I use a BroadcastReceiver, which starts my IntentService to do the work when the user plugs the device and stops it when the device becomes unplugged.
Right now I'm thinking of using JobScheduler for Android 5.0+, but what I'm seeing is that with JobScheduler, I would have to schedule my job within the app, by calling
JobScheduler.schedule(JobInfo);
But this is a problem to me, because I want my job to run every time the user connects their device to the charger, even without the user having to open my app.
For this reason, I think one way would be to schedule it the first time the user opens the app, and then always force reschedule, since I cannot trust on the user opening my app every day (which, due to the nature of my app, certainly won't happen).
So, should I stick with BroadcastReceiver or use JobScheduler for Android 5.0+?
And in the case of using JobScheduler, should I schedule my job only once and then always return true in order to force rescheduling?
Thank you.
So, should I stick with BroadcastReceiver or use JobScheduler for Android 5.0+?
Use JobScheduler, this can improve your app’s performance, along with aspects of system health such as battery life. Also, JobScheduler persists through device reboots and supports batch scheduling by which the android system can combine pending jobs thus reducing battery usage. Moreover, you can do distinguish between android versions thus using JobScheduler on Lollipop and up, and AlarmManager on older versions.
And in the case of using JobScheduler, should I schedule my job only once and then always return true in order to force rescheduling?
Now, there are 2 ways to do this :
As you guessed, scheduling your job only once and always returning true in jobFinished() - this should do the trick.
Upon completing a job (originally scheduled by you by calling JobScheduler.schedule(JobInfo)), you schedule another job by calling the same. This will schedule consequent jobs once each job is about to be completed.
Jobscheduler runs in the background and persists through reboots so you should be fine.

Jobscheduler API android L

I am making an application that makes use of the jobscheduler API.
I want to run a service periodically and when the device is charged. This is the code.
JobInfo.Builder builder = new JobInfo.Builder(kJobId++, mServiceComponent);
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
builder.setPeriodic(3000);
builder.setRequiresCharging(true);
mTestService.scheduleJob(builder.build());
Now when I run this and I unplug the device, the service still runs after 3 secs. There is no effect of setting the setRequiresCharging.
When i comment out builder.setPeriodic(3000), it works perfectly fine. I am not sure as to where I am going wrong.
To identify each job internally, the framework creates a new JobStatus when a new job lands on the scheduler. One of the first things JobStatus does is to check and see if your job has a periodic update interval. If it does, it uses the periodic update interval to determine the latest point in the future that a job must be run, or in other words, if it has a deadline constraint.
One of the criteria for a job to be considered ready to be executed, it that all of its constraints have been satisfied or the deadline of a job has expired. See JobStatus.isReady for more information.
The JobSchedulerService adds several StateControllers used to track when jobs should run and when they must be stopped. One of these controllers is a TimeController, which:
sets an alarm for the next expiring job, and determines whether a
job's minimum delay has been satisfied
To determine if a new alarm should be scheduled, TimeController checks if your job has any time delay or deadline constraints, which all jobs seem to be given if you set a periodic update interval.
I hope that helps you at least understand why your job continues being scheduled despite your battery constraint. But I don't have a solution that can offer a straightforward fix for this at the moment.

Categories

Resources