Background
I wanted to use the new JobScheduler API that was presented on Lollipop, but sadly it doesn't have an official port for pre-Lollipop.
However, there is the GcmTaskService, which provides very similar functionalities.
The problem
This API is quite new, so there are very few places to look for information of how to use it (here and here, for example).
The questions
I have a few questions about this new API :
It seems that it requires Google Play Services (here) to be used (except for when using Lollipop version of Android, which will use the normal JobScheduler). What should I do in case the Google play services aren't available?
It seems that even though I've used "setPersisted(true)" for a repeated task, when I restart the device the task won't be called again. How come?
EDIT: that's because I missed a permission of RECEIVE_BOOT_COMPLETED .
What is the default behavior of a task, in case I don't use "setRequiredNetwork" ? Is it "NETWORK_STATE_ANY" ?
The docs say about what's returned from onRunTask , I can return any of the values "RESULT_FAILURE", "RESULT_RESCHEDULE", "RESULT_SUCCESS" (info here). It seems both the FAILURE and SUCCESS options will do the same thing - remove the task from the queue. Is it true? If so, what exactly is the difference between them ? Do they function differently?
Are "TaskParams" used only for the tag of the task? Can I somehow pass a bundle to the task using the API? Otherwise, I would need to set a DB for storing what should be passed to the tasks, right?
Is it possible for the app to get the queue of the tasks? I know it's possible using adb, but is it possible using the API too?
They say (here) that each task has a wakelock of up to 3 minutes. What should be done if the task needs more than that? Should it acquire another wakelock for itself? Will the API warn that the wakelock was released? Here's what the docs say:
The scheduler will hold a PowerManager.WakeLock for your service,
however after three minutes of execution if your task has not returned
it will be considered to have timed out, and the wakelock will be
released. Rescheduling your task at this point will have no effect. If
you suspect your task will run longer than this you should start your
own service explicitly or use some other mechanism; this API is
intended for relatively quick network operations.
They say (here) that all networks-tasks are removed each time the app gets upgraded/replaced, and there is a call for "onInitializeTasks" when this happens, and that you can re-schedule them again. How can I re-schedule the tasks? I don't think I can even get the list of tasks...
Is it possible to tell the task to prefer specific times during the day ? For example, between the 14:00-15:00 ?
I've noticed that if you schedule a task, and then you force-stop and/or clear data of the app, the task will still run. How can I avoid this behavior?
jacktech24 did a really good job, but i will try as well in case there are any lingering questions.
It seems that it requires Google Play Services (here) to be used (except for when using Lollipop version of Android, which will use the normal JobScheduler). What should I do in case the Google play services aren't available?*
You can't use this API if Google Play Services isn't available. Rather, the Google Play Services client library is designed to request that the user download and install Google Play Services if it detects that it's missing, but I don't believe that the GcmNetworkManager does this.
What is the default behavior of a task, in case I don't use "setRequiredNetwork" ? Is it "NETWORK_STATE_ANY" ?*
The javadoc describes which is the default.
The docs say about what's returned from onRunTask , I can return any of the values "RESULT_FAILURE", "RESULT_RESCHEDULE", "RESULT_SUCCESS" (info here). It seems both the FAILURE and SUCCESS options will do the same thing - remove the task from the queue. Is it true? If so, what exactly is the difference between them ? Do they function differently?*
The only difference between these 2 is that in the adb shell dumpsys it will display what you returned, so you can use this to troubleshoot issues.
The other reason is that if the task fails, it is strange to require the client return a "success."
Are "TaskParams" used only for the tag of the task? Can I somehow pass a bundle to the task using the API? Otherwise, I would need to set a DB for storing what should be passed to the tasks, right?*
In the next version of GmsCore the ability to add a bundle to the task should be supported.
Is it possible for the app to get the queue of the tasks? I know it's possible using adb, but is it possible using the API too?
No it's not possible. Instead you should perform the cancel when you want it and if the task is not there it will be a no-op. Similarly you should schedule the task at the point in your code where you would have queried for the list of tasks. use setUpdateCurrent=false to ensure that it doesn't update the pre-existing task. The AlarmManager works in a similar way in that you would set the alarm regardless of whether the alarm was already set - the api was designed to follow this.
They say (here) that each task has a wakelock of up to 3 minutes. What should be done if the task needs more than that? Should it acquire another wakelock for itself? Will the API warn that the wakelock was released? Here's what the docs say:*
Yes, the app should acquire its own wakelock and everything will be fine. The reason the scheduler releases the wakelock after 3 mins is because in practice having an unlimited wakelock timeout only leads to really hard to track down battery drain bugs. If you need longer than 3 mins you have a sophisticated enough use-case that you can dig into how the PowerManager APIs work and call the acquire()/release() yourself (it's really quite simple, the fact that the network manager does it for you is more of a politeness than anything else).
They say (here) that all networks-tasks are removed each time the app gets upgraded/replaced, and there is a call for "onInitializeTasks" when this happens, and that you can re-schedule them again. How can I re-schedule the tasks? I don't think I can even get the list of tasks...*
You reschedule the tasks the same way you scheduled them in the first place. Whatever function you used to schedule them, call that function from GcmTaskService#onInitializeTasks. This was done to avoid lingering tasks across app logic changes. Consider the situation where a developer changes their task timetable, and starts using a different tag. They would be required to call cancel(old_tag) after they'd detected the upgrade (which they'd have to add more code to do), which would mean they'd need a reference to the old (unused) tag even in their new code. This would imply that a tag is a stable identifier that shouldn't change across app upgrades - which shouldn't be a requirement for this api.
Is it possible to tell the task to prefer specific times during the day ? For example, between the 14:00-15:00 ?*
No, this type of background scheduling causes all sorts of problems with herding across large populations of devices. I.e. if 1 device runs a job at 15:00 that is probably fine. But if 1x10e6 do suddenly your server is in serious problems.
I've noticed that if you schedule a task, and then you force-stop and/or clear data of the app, the task will still run. How can I avoid this behavior?*
Unfortunately you can't, but this is not intentional and should be changed - there should be no way for an app to run after the user has explicitly stopped it.
you can find answers to most of your questions it here.
https://github.com/jacktech24/gcmnetworkmanager-android-example/blob/master/README.md
To answers that are not answered there
7: You won't get notified when wakelock is removed, and as they say in documentation, this API is only for short tasks, if you have longer, write your own implementation
9: No you can't as of now, the API doesn't allow it
10: That's because Google Play services are taking care of it, and there is no way around it. You have to detect in the service whether the app is setup (I expect that's your problem) eg. configuration is created etc. and eventually cancel all tasks when one of them is called.
Related
Background
Ever since Android O came out I had trouble with what classes and methods I should use when I want to schedule background sync tasks and notifications, more specifically, I want to do these two separate things:
Background sync task to download data from the server, and notify the user about important information (that was just fetched from the server). These syncs should occur at an almost precise time of the day (e.g. 21:30 ±10min).
Notify the user about an upcoming event, for example, one week before an exam.
Because Android O has placed some restrictions on AlarmManager, I cannot set a background service that runs at a specific time of the day, unless I use getForegroundService(), which, as the docs say, should only be used for services that are noticeable to the user.
What I tried / considered
I have been using JobService that runs periodically every so and so hours, but I would prefer for it to run at a more specific time of the day.
I have looked into CalendarProvider, and also considered a push notification service, but it seems to me like an overkill for simple tasks like these.
Question
My final question is what methods I could, or I should use to implement the above features?
Use WorkManager this is the best way to Run Task in Background in Android Oreo and Pie versions and it also works in older versions of Android.
See the Documentation
https://developer.android.com/topic/libraries/architecture/workmanager
https://developer.android.com/topic/libraries/architecture/workmanager/basics
https://developer.android.com/topic/libraries/architecture/workmanager/advanced
GitHub Example
https://github.com/krunalpatel3/WorkManager-Example-Andorid
Reference
http://thetechnocafe.com/how-to-use-workmanager-in-android/
https://www.youtube.com/watch?v=fAQKvBHeg_w
https://www.youtube.com/watch?v=1VVir3-4hII
https://www.youtube.com/watch?v=0jgkQYebYvQ
https://www.youtube.com/watch?v=ooP8kkhvRQI
I am still new to android and working on an application that works on Android (API >= 21). This application displays data that is previously downloaded from a server on the local network so I need to implement a service to download the content, on-demand & Periodically, on the device.
SyncAdapter
I've implemented this "Downloader" using SyncAdapter and it was working fine but at the end the code was really verbose:
The application does not have ContentProvider. The content is downloaded as files.
It runs on a local closed network so there is no need for authentication.
The application had 3/4 extra classes that not doing any real job.
JobScheduler
After some reading and searching, I decided to go with JobScheduler.
"JobScheduler is guaranteed to get your job done." medium article
It looks easy and has clear API, I said, so I re-implemented my "Downloader" with JobScheduler and the result was really good. The application was able to download the content, writing a good log to trace errors and operations. The Job runs when the device is Idle and kicks off/stopped, on demand, as expected.
I used alarm manager to implement the periodical calls of the job and turning wifi on. Oh, I forgot to mention that the application is the responsible of turning on the wifi because it is inside a case and works as Kiosk
The problem is that there is a catch. This is not mentioned in the documentation, or I was blind not to see it, at all. The Job is limited to 1 minute only (1 minute on lolipop and more on Android >= 0) then the onStopJob() will be called. So my is cancelled before completing the download when the data is a little big. Of course I can keep my thread going and continue download in the background but in this case I can't benefit from the API to maintain a good log, reschedule my job and manage Wifi status.
I need another implementation
I need something similar to SyncAdapter or JobScheduler that runs when the Wifi is on and when the device is Idle.
I am not sure whether triggering a service from JobScheduler is the solution I am left with. I need a little certain information before implementing the same thing for the third time.
Any idea?
The new limitations on running background services is explained here and here.
This new behaviour is apparently to stop apps doing lots of stuff in the background without the user even being aware, which is fair enough.
However, one of the suggested workarounds is to use a scheduled job instead. But doesn't that also potentially result in the app "doing lots of stuff in the background without the user even being aware"? The only difference being that Android decides exactly when this stuff is done, rather than the app.
So, what exactly is the point of the new limitations? Or maybe I've missed something fundamental.
EDIT: This is not a duplicate of this question... that one is about using startServiceInForeground() as an alternative (or about documentation relating to that) whereas this question is about using a scheduled job as an alternative (and whether that defeat the purpose of the new limitations completely). These are completely different alternatives.
I see your point.
Seems to me (after reading through the docs) this is the only pro that we get using the new JobScheduler.
However if you look through Job Scheduler Improvements in O
Scheduled jobs now support several new constraints:
JobInfo.isRequireStorageNotLow()
Job does not run if the device's available storage is low.
JobInfo.isRequireBatteryNotLow()
Job does not run if the battery level is at or below the criticial threshold; this is the level at which the device shows the Low battery warning system dialog.
NETWORK_TYPE_METERED
Job requires a metered network connection, like most cellular data plans.
You would notice that for each JobInfo you get to specify which constraints need to hold for the job to start.
Once the job has been started, it is then free to use as many resources as it wants for however long it wants.
That seems to be true, since there is no indication from the docs on what would happen if/when the resources are required again. However you can break down your big task into smaller tasks and then use the [JobScheduler.enque()](https://developer.android.com/reference/android/app/job/JobScheduler.html#enqueue(android.app.job.JobInfo, android.app.job.JobWorkItem)) method to ensure that the JobScheduler stops before starting next job in queue if the System otherwise requires resources. However this may not be necessary if JobScheduler provides a method or a callback to the Job to pause/stop (doubtful since the docs don't get into it) I've never tried it myself.
Conclusion :
So in conclusion while the new API does not completely restrict the Background Services to run while required resources are available, it does however provide a platform for the coder to restrict them to start only when above mentioned conditions are met while also restricting min lines of code required. Which should in most cases minimize battery usage and provide a smoother experience to user.
Background
Google has multiple solutions for job/task scheduling, such as JobScheduler and GcmTaskService. Each has its own advantages and disadvantages.
Recently, Google presented a new library called "Firebase JobDispatcher".
The problem
Sadly, there is very little to read about this new API. In fact, it's really hard to find anything about it.
Only thing I've found is their announcement video and a sample. But even their, there is not much to know about this API.
The questions
Looking at previous questions, investigations and comparisons I had with the other APIs (here, for example), I'd like to ask how the new API works and know what to take into consideration when using it:
Can a job have parameters that stay with it and can even be modified when needed? They say in the sample "An optional Bundle of user-supplied extras. The default is an empty Bundle." Is this it? Can it be modified by the job upon execution of it?
Can jobs be re-scheduled easily? It is said "A boolean indicating whether the Job should repeat" . How can it be chosen when to re-schedule? I've tried the sample, and chose "Recurring", but it doesn't seem to run again, only once.
Can it be protected vs library's jobs (because of unique ids)?
Does it needs extra care when updating the app (as previous APIs did)? Can jobs still be scheduled after an update of the app? Testing on the sample, it seems the jobs are completely gone after an update of the app. Can it be avoided?
Does it need RECEIVE_BOOT_COMPLETED in case I want the job to still be scheduled even when the device is restarted? The sample seems to have it.
Is it possible to get a list of all scheduled jobs and their information (including parameters), and be able to cancel specific/all of them and even modify them ?
Will a job be removed upon clear-data operation of the app?
Is it possible to tell the job that it's best that it will run in a range of time (example : between 7:00 and 8:00 in the morning)? It is mentioned "ExecutionWindowTrigger-which specifies a time window in which the Job should be executed". Is that it? What happens when it misses this window?
The method onStartJob in JobService class return a boolean and the description for it is "whether there is more work remaining." What does it mean? What does the needsReschedule parameter of jobFinished method mean? Are they related to each other?
Are there any restrictions I should know about? For example minimal & maximal values for each of the functions?
Actually Firebase Android JobDispatcher is a layer of abstraction around job scheduling engines on Android.
And for now they only have one driver implementation for GCM Network Manager.
That means currently it behaves the same way as GCM Network Manager behaves. Hopefully in the future more Drivers will be implemented.
1. Can a job have parameters that stay with it and can even be modified when needed? They say in the sample "An optional Bundle of user-supplied extras. The default is an empty Bundle." . Is this it? Can it be modified by the job upon execution of it?
Yes, Job.Builder has method setExtras with arbitrary bundle which later may be accessed via jobParameters.getExtras().
You cannot modify the bundle (jobParameters contains only getters). You could reschedule your job with flag setReplaceCurrent(true) and specify a new bundle.
2. Can jobs be re-scheduled easily ? It is said "A boolean indicating whether the Job should repeat" . How can it be chosen when to re-schedule? I've tried the sample, and chose "Recurring", but it doesn't seem to run again, only once.
To re-schedule job you need specify setRecurring(true), setTrigger(Trigger.executionWindow(10, 20))
This becomes triggered as soon as the window start deadline is
reached, and drivers are encouraged to run the Job before the window
end if possible.
3. Can it be protected vs library's jobs (because of unique ids) ?
Job tags must be unique in your application. Other apps on the phone have their own 'endpoints' (package name/service name).
To see all scheduled/finished jobs for GooglePlayDriver please use
adb shell dumpsys activity service GcmService
4. Does it needs extra care when updating the app (as previous APIs did) ? Can jobs still be scheduled after an update of the app ? Testing on the sample, it seems the jobs are completely gone after an update of the app. Can it be avoided?
As for GCM Network Manager GooglePlayDriver doesn't reschedule Jobs after Google Play Services or the app is updated.
Here is an open issue for this. So for now this is your responsibility.
5. Does it need RECEIVE_BOOT_COMPLETED in case I want the job to still be scheduled even when the device is restarted? The sample seems to have it.
Yes you need such permission.
Builder has a parameter to control the behavior: setLifetime(Lifetime.FOREVER | UNTIL_NEXT_BOOT)
Of course if you're going to create your own driver you'll have to take care of the lifetime yourself.
6. Is it possible to get a list of all scheduled jobs and their information(including parameters), and be able to cancel specific/all of them and even modify them ?
No, the same as for GCM Network Manager.
But you could track all jobs yourself somehow while scheduling them to play services.
7. Will a job be removed upon clear-data operation of the app?
Yes, the job will be removed. Probably in the previous versions of google play services it behaved differently.
8. Is it possible to tell the job that it's best that it will run in a range of time (example : between 7:00 and 8:00 in the morning) ? It is mentioned "ExecutionWindowTrigger-which specifies a time window in which the Job should be executed" . Is that it? What happens when it misses this window?
Well, you could setup an alarm to be fired at 7:00 and schedule a non-recurring job with executionWindow(0, 60*60). The job will run between 7:00 - 8:00.
You cannot use recurring job because
windowStart - The earliest time (in seconds) the job should be
considered eligible to run. Calculated from when the job was scheduled
(for new jobs) or last run (for recurring jobs).
Also, ExecutionWindowTrigger specifies approximate time. It's not guaranteed it would run at the given window.
If it misses the window the job will run any time later.
9. The method "onStartJob" in "JobService" class return a boolean and the description for it is "whether there is more work remaining." . What does it mean? What does the "needsReschedule" parameter of "jobFinished" method mean? Are they related to each other?
if onStartJob returns false that means you completed your work. No Need to invoke jobFinished. The RESULT_SUCCESS is sent automatically.
if onStartJob returns true that means you started a thread and waiting for results. As soos as you're done you must invoke jobFinished to inform google play services whether the job should be rescheduled or not. If yes the job will be rescheduled depending on RetryStrategy.
10. Are there any restrictions I should know about? For example minimal&maximal values for each of the functions?
onStartJob should offload work to another thread of execution as soon as possible. They provide a SimpleJobService as an example of what is expected from you.
There is no Driver implementation for Lollipop's JobScheduler. Also need to handle the situation when google play services are not available, we should probably implement Driver based on AlarmManager.
I have been developing for Android for little less then 2 years, and I am still puzzled by this seemingly simple question.
When should one implement a service?
From my experience there are some rare cases but I am questioning this because on every phone there are quite a lot of them running and I doubt it's just a poor application design.
This is essentially core of my question but following are some of my experiences and thoughts about the subject which can explain my question in more detail.
In all apps that I have developed only one really required a service. It was a background sound recorder and I was using it as Foreground service with notification since I wanted buttons to be able to control it (like music players do for example).
Except this I never really saw a requirement for the constantly running service because:
A) Intent listeners (Manifest registered BroadcastReceivers) are quite a useful feature and using them as you know is usually enough for many use-cases (for example showing notifications).
B) If scheduled execution is a must one can subscribe to alarm events.
C) I know that service in Android is quite different then for example in Windows since in Android services are just a "package" to organize your code in and have a the system manage the lifetime of the object. Services use the Main Thread but it's customary to spawn new threads in them.
D) In the development documentation services are suggested for network communication and background calculations but I don't get why you should not just use AsyncTasks for that. I am a big fan of these and use them extensively for lot of things from downloading data from the internet to doing FFT calculations under time critical conditions.
E) I get the usefulness of Foreground services but why are people using background services so much (excluding the system apps).
Those are my thoughts about the SERVICE and I hope someone with more experience will be able to explain these PROS and CONS (along with others that I probably missed).
When should one implement a service?
When you have work -- delivering value to the user -- that:
Needs some time to complete, perhaps longer than you have time for in the component wishing the work to be done, or
Is delivering that value under user control (e.g., music player, controlled by play/pause buttons in a UI), or
In rare cases, needs to be running continuously, as it delivers value continuously
there are quite a lot of them running and I doubt it's just a poor application design
Some are likely to be poor implementations, either due to technical misunderstandings, or other concerns (e.g., making marketing happy) trumping making users happy.
It was a background sound recorder and I was using it as Foreground service with notification since I wanted buttons to be able to control it (like music players do for example)
That is a reasonable use for a service, IMHO.
Intent listeners are quite a useful feature and using them as you know is usually enough for many use-cases (for example showing notifications)
I assume that by "Intent listeners" you mean manifest-registered BroadcastReceivers. In that case, if the work to be done by the BroadcastReceiver will take more than a millisecond, that work should be delegated to an IntentService for completion. onReceive() is called on the main application thread, and it is not safe for a manifest-registered BroadcastReceiver to fork a bare thread, as the process could go away shortly after onReceive() returns. However, in these cases, the service is usually short-lived (e.g., do some network I/O and disk I/O, then go away).
In the development documentation services are suggested for network communication and background calculations but I don't get why you should not just use AsyncTasks for that
An AsyncTask is a fine solution for background work that is:
Requested by the UI (activity or fragment), and
Will take less than a second or so, and
Is non-critical
For example, if you are downloading avatars to show in a ListView, AsyncTask is probably a fine choice, whether you use them directly or use some image-fetching library that uses them internally.
Conversely, if the user buys an MP3 through your app, and you need to download that MP3 file, an AsyncTask is not a good solution. That could easily take over a second. While the download is going on, the user could switch away from the app (e.g., press HOME). At that point, your process is eligible to be terminated... perhaps before your download is complete. Using an IntentService to manage the download is a signal to the OS that you are really doing work here, adding value to the user, and so the process will be left alone for a little while.
Note that if the background work might take 15+ seconds, WakefulBroadcastReceiver or my WakefulIntentService is probably a good idea, so the device does not fall asleep while you are trying to wrap up this bit of work.
I can name some of the Service uses from my experience:
to implement
location listener,
sound module, generating various voices
in app content updates,
API, provide services to other apps
in app billing
Communication with webservices (if requests frequency is high)
actually (excluding 5.) they all are working for the whole app duration, they are using some of the other android services, also they manage their state. I suppose one of the important thing here is state management during application life cycle changes.
I prefer to look at AsyncTasks in a same way as Executors (ExecutorService), they should be executed sequentially and for small tasks.
In the android website, you can find a table when to use Service, Thread, or WorkManager (the new API for scheduling jobs, currently in alpha as of this comment posted). https://developer.android.com/guide/background/#table-choose
The website also state that you need to use started service only as last resort. The Android platform may not support started services in the future. Refer to this link https://developer.android.com/topic/performance/scheduling#services
You should avoid using started services that run perpetually or perform periodic work, since they continue to use device resources even when they aren't performing useful tasks. Instead, you should use other solutions that this page describes, and that provide native lifecycle management. Use started services only as a last resort. The Android platform may not support started services in the future.
If you consider UI and bound services, u would think that both can exist and not be doing anything for certian periods. In such scenarios, your UI can be recreated a lot of times however service does not. And this is where service is important. Lets say you are processing images and then rotate device you want processing to continue while UI is being recreated. You recording a voice and then rotate device. These are one of the places where I find service very important. (Having lot of heavy data processing, interaction with web, that could be few seconds)