Basically what I want to do is, when there is an internet connection then collect values from sensors and schedule the next time data is collected from sensors, let's say 5 minutes (this interval depends on the current activity: walking, running...). After 5 minutes, the system checks if there is internet connection:
-if there is then the cycle is repeated (collection, sending to server and scheduling the next data collection)
*if there isn't then the next time the data is collected is scheduled for the next time there is an available internet connection
What I tried using:
1) I wanted to use a broadcast receiver for api<21 and job scheduler for api>=21.
For job scheduler, I can't set requirement available network and make the job periodic at the same time because this way the job runs after the period is over whether the requirement is met or not.
2)I tried to make the job service set the next job after the current one is over but the app ran as if that part of the code didn't exist (it only ran once)
3) I tried to somehow combine job scheduler with alarm manager to make the job scheduler manage the network requirement and the alarm manager manage the periodic aspect of the job. But I failed to implement this and I feel like this isn't the right way to do it.
If someone could guide me to a better way to handle this, I'd really appreciate it because I've spent a long time trying to solve this.
I would recommend a library from Evernote called android-job. It handles all the complexity of choosing JobScheduler on 21+, Firebase/GcmNetworkManager, or AlarmManager and all kinds of other things. It has a pretty robust set of features that should fit your use case.
For Example:
int jobId = new JobRequest.Builder(DemoSyncJob.TAG)
.setExecutionWindow(30_000L, 40_000L)
.setBackoffCriteria(5_000L, JobRequest.BackoffPolicy.EXPONENTIAL)
.setRequiresCharging(true)
.setRequiresDeviceIdle(false)
.setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
.setExtras(extras)
.setRequirementsEnforced(true)
.setPersisted(true)
.setUpdateCurrent(true)
.build()
.schedule();
Related
This is the use case : the user set a daily notification with a specific time. At the specified time, a network request is made to fetch some data and then a notification is displayed using the retrieved data. I am not sure whether I should use AlarmManager or WorkManager to implement this use case.
As I understand, AlarmManager is best for scheduling at a precise time. But without network, the job will fail and I prefer the job to be deferred to respect the network constraint than failing at execution. For this type of constrained work, with a guarantee of final execution, WorkManager looks like the best solution, using a OneTimeWorkRequest with an initial delay so that it is executed at the right time.
Comparing AlarmManager and WorkManager, WorkManager wins for some reasons:
1) AlarmManager starting from Kitkat, the alarms may be shifted by OS to reduce the wakeup of the device to reduce battery usage.
Check official documentation for more details.
2) Since you aren't going to define a specific timing for the notification, I mean here you're not going to use Calender for a specific time maybe 3:00 PM, use WorkManager because you have PeriodicWorkRequest in WorkManager.
Note that you aren't permitted to make PeriodicWorkRequest less than 15 mins.
Check PeriodicWorkRequest in official documentation
3) WorkManager now replaces all APIs for background jobs, JobScheduler, Firebase JobDispatcher.
4) WorkManager works perfectly with Coroutines
This is my humble opinion if you have any concerns kindly reply.
Happy coding 🤓
I'd like to detect when there is a network change (connection\disconnection) when my app is not in the foreground, so I can sync the user's data.
I have tried to use a broadcast receiver (like in this answer) but the broadcasts are no longer received when the app is closed (after Android N). It only works when the app is in the foreground.
In order to detect connectivity change in the background, I have tried to use the Job Scheduler (like in this answer) with the .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) parameter, but my problem is that the job is done repetitively (every 15 minutes), even when the user is still connected to the internet.
My goal is to make the job once when the internet connection is regained, and then stop the job until the user disconnects and connects again. The job should be done once for every connection to the internet, and then wait until a reconnection (without repeating it).
Is it possible to achieve this with the Job Scheduler (or in any other way)?
Is it possible to achieve this with the Job Scheduler?
Answer - I don't know BUT
You can use WorkManager. That is part of Android Jetpack.
There are 2 options.
1) OneTimeWorkRequest (that what you need).
2) PeriodicWorkRequest (executes periodically, min time is 15 minutes, same as jobScheduler).
WorkManager executes only 10 seconds, as jobscheduler.
Why is WorkManager good. Image in below will give answer.
Documentation - https://developer.android.com/topic/libraries/architecture/workmanager
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).
According to https://github.com/ReactiveX/RxAndroid/issues/257#issuecomment-164263215 . interval is just for active code, and if app is not wake up, it will not work. So how to use interval for background scheduling tasks?
Please DO NOT use this solution:
To use interval from RxJava you'll have to make sure your app's process stays alive. One way to do it is to put use the Observable in a foreground service. This is a bad idea because the service is NOT actively delivering value to the user. Waiting for time to pass is not delivering value for the user. Again please DO NOT use this.
AlarmManager and JobScheduler (or it's backport GcmNetworkManager) are far better choices for repeating background activities. If you use AlarmManager.setInexactRepeating() the system can batch jobs from multiple apps together to save battery. Using JobScheduler enables you to execute your background jobs in specific conditions, eg. when the device is connected to the internet or when battery is more than 20%. (Internet is required to check the weather).
interval from RxJava does have it's usage on Android. It's an excellent replacement for Runnable.postDelayed for relatively short durations. It makes the code shorter and more readable.
If you need to schedule a task that should be run even if app is not active anymore then use AlarmManager.
If you need to schedule a task that should be run only when app is active then you can use Observable.interval() and react on emission to execute some code and please don't forget to unsubscribe from the Observable when appropriate (when Activity is paused, etc) so app won't burn the battery!
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.