I am using JobScheduler in my application. I want to upload file after every hour if user connected to WIFI, but onStartJob()method gets called before hour, mostly it gets called after 15-20 min. Following is my code :
ComponentName componentName = new ComponentName(this,UploadService.class);
JobInfo info = new JobInfo.Builder(1,componentName)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) // change this later to wifi
.setPersisted(true)
.setPeriodic(60*60*10000)
.build();
JobScheduler scheduler = (JobScheduler)getSystemService(JOB_SCHEDULER_SERVICE);
int resultCode = scheduler.schedule(info);
if (resultCode==JobScheduler.RESULT_SUCCESS) {
Log.d(TAG,"JOb Scheduled");
} else {
Log.d(TAG,"Job Scheduling fail");
}
public class UploadService extends JobService {
#Override
public boolean onStartJob(JobParameters params) {
uploadFileToServer(params);
return true;
}
#Override
public boolean onStopJob(JobParameters params) {
return true;
}
.....
.....
}
Here uploadFileToServer(params); gets called before hour. how to set time so it will call only after hour. Thanks in advance
Use this method setPeriodic (long intervalMillis, long flexMillis) (Added in API 24) on the JobInfo.Builder and supply a flex interval as the 2nd parameter:
long flexMillis = 59 * 60 * 1000; // wait 59 minutes before executing next job
JobInfo info = new JobInfo.Builder(1,componentName)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) // change this later to wifi
.setPersisted(true)
.setPeriodic(60 * 60 * 1000, flexMillis)
.build();
Importent - The job is guaranteed to run after the flex interval (which starts after the last job is finished), but it's not guaranteed to run immediately after that time, so the duration between jobs can be more than 1 hour, depending on your job requirements, system status, and more...
Docs ref: https://developer.android.com/reference/android/app/job/JobInfo.Builder.html#setPeriodic(long,%20long)
Specify that this job should recur with the provided interval and flex. The job can execute at any time in a window of flex length at the end of the period.
As already recommended in some comments, you should start working with the new WorkManager (even that it's not production level yet) which has similar features as the JobScheduler, but it's minimum SDK support is 14 which will let you delete a lot of boilerplate code :)
From the JobInfo.java class ,
You have no control over when within this interval this job will be executed
/**
* Specify that this job should recur with the provided interval, not more than once per
* period. You have no control over when within this interval this job will be executed,
* only the guarantee that it will be executed at most once within this interval.
* Setting this function on the builder with {#link #setMinimumLatency(long)} or
* {#link #setOverrideDeadline(long)} will result in an error.
* #param intervalMillis Millisecond interval for which this job will repeat.
*/
public Builder setPeriodic(long intervalMillis) {
return setPeriodic(intervalMillis, intervalMillis);
}
Related
I am trying to upload to data to a server in background when there are internet and app is not running in the front end.
So I read somewhere this can be achieved by JobService.
I created a simple job service that toasts onStartJob and in the splash screen activity. I called the below code:
mJobScheduler = (JobScheduler)
getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo.Builder builder = new JobInfo.Builder(1,
new ComponentName(getPackageName(),
Unigen_Upload_JobScheduler.class.getName()));
builder.setPeriodic(60000);
builder.setPersisted(true);
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
if (mJobScheduler.schedule(builder.build()) <= 0) {
Log.e("Value", "onCreate: Some error while scheduling the job");
}
This runs the first time properly but doesn't run again after 1 minute. I am not sure why this isn't happening?
Also, I had another question will this trigger whenever the WIFI or Mobile is switched ON / Changed?
Do I have to use a broadcast receiver to do the above?
Thanks!
Update:
public class Unigen_Upload_JobScheduler extends JobService {
public Unigen_Upload_JobScheduler() {
}
#Override
public boolean onStartJob(JobParameters params) {
Toast.makeText(this,"Executed",Toast.LENGTH_LONG).show();
Log.e("Value","+_ what should I do");
/*
* True - if your service needs to process
* the work (on a separate thread).
* False - if there's no more work to be done for this job.
*/
return false;
}
#Override
public boolean onStopJob(JobParameters params) {
return false;
}
}
The minimum interval for JobScheduler Periodic Job is 15 minutes. Check the reason behind this:
Why can't an interval be smaller than 15 minutes for periodic jobs?
JobScheduler's minimum periodic interval is 15 minutes or (15 * 60 * 1000) 900000ms. You can look into WorkManager which is a part of android jetpack for more convenient usage. As for your second question workmanager has convenient methods for scheduling tasks on various scenarios.
I want to sync data to server after certain amount of time and this time is configurable ranging from 30 minutes to 8 hours.
I am using JobScheduler to achieve above.
Is there any way using which I can update the JobInfo associated with JobScheduler?
So that I can update value set in setPeriodic(intervalMillis) method to the updated time interval.
Refer my sample code below
int PERIODIC_SYNC_JOB_ID = 0;
long interval = 1000 * 60 * 20;
JobInfo.Builder builder = new JobInfo.Builder(PERIODIC_SYNC_JOB_ID,
new ComponentName(getApplicationContext(), SampleJobService.class));
JobInfo jobInfo = builder.setPeriodic(interval).build();
JobScheduler jobScheduler = (JobScheduler) getApplicationContext().getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(jobInfo);
Log.d("JobScheduler", "Sample job is scheduled every " + interval + " ms");
SampleJobService.java
public class SampleJobService extends JobService {
#Override
public boolean onStartJob(JobParameters jobParameters) {
Log.d("SampleJobService", "onStartJob()");
// do something in another thread/handler/AsyncTask
jobFinished(jobParameters, false);
Log.d("SampleJobService", "Sample job is rescheduled");
return true;
}
#Override
public boolean onStopJob(JobParameters jobParameters) {
Log.d("SampleJobService", "onStopJob()");
return false;
}
}
Remember to add your service in Manifest
<service
android:name=".SampleJobService"
android:permission="android.permission.BIND_JOB_SERVICE" />
Note that minimum interval is 15 minutes. Attempting to declare a smaller period that this when scheduling a job will result in a job that is still periodic, but will run with this effective period.
My code is work. But I'm not sure that this will work on all devices and always.
Can I use JobScheduler like this?
Start method:
public static void schedule() {
JobScheduler scheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo job = new JobInfo.Builder(1, new ComponentName(context, MySchedulerService.class))
.setMinimumLatency(15000)
.build();
scheduler.schedule(job);
}
Service:
public static class MySchedulerService extends JobService {
public boolean onStartJob(JobParameters params) {
schedule();
/* Business code */
stopSelf();
return false;
}
public boolean onStopJob(JobParameters params) {
return false;
}
}
Can I schedule job every 15 seconds
No. Minimum duration between two schedule is around 15 minutes. You can easily query for this information using getMinPeriodMillis()
Note:
Attempting to declare a smaller period that this when scheduling a job
will result in a job that is still periodic, but will run with this
effective period.
For setMinimumLatency, based on the documentation:
Milliseconds before which this job will not be considered for
execution.
This doesn't mean the job will be executed immediately after 15 seconds and won't be reliable from Android N when first phase of doze mode kicks in.
I have a web service on my server that needs to be pinged every hour. For this, I am using an Android app to ping it every hour. I have tried using Alarm manager but it stops working after few hours and if I swipe exit it. I have tried using service but for some reason, that doesn't seem to work and my app keeps crashing. I have am thinking about using Firebase Job dispatcher. My requirement is that the app needs to ping the web service on my server every hour. This should go on for at least next 3-4 months. Is there a way to accomplish this ? Thanks in advance!
EDIT: I have tried broadcast receiver with Alarm Manager but have not been able to sustain the firing for more then 4 hours.
I second Anantha's answer but seems like job parameters are little off for your needs.
You can go over this article to learn about the subtle differences between various Job schedulers.
As a matter of fact, even Google recommends using Firebase Job Schedular if the app needs to do a network communication due to various reasons. Please watch the attached video on the Github page for more info on the same. This also gives you basic code to kickstart your application. You can just change the job parameters to suit your needs
Hopefully, this below code should suit your requirement of triggering every one hour with a tolerance of 15 minutes
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(getContext()));
final int periodicity = (int)TimeUnit.HOURS.toSeconds(1); // Every 1 hour periodicity expressed as seconds
final int toleranceInterval = (int)TimeUnit.MINUTES.toSeconds(15); // a small(ish) window of time when triggering is OK
Job myJob = dispatcher.newJobBuilder()
// the JobService that will be called
.setService(yourJobService.class)
// uniquely identifies the job
.setTag("my-unique-tag")
// recurring job
.setRecurring(true)
// persist past a device reboot
.setLifetime(Lifetime.FOREVER)
// start between 0 and 60 seconds from now
.setTrigger(Trigger.executionWindow(periodicity, toleranceInterval))
// overwrite an existing job with the same tag
.setReplaceCurrent(true)
// retry with exponential backoff
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
// constraints that need to be satisfied for the job to run
.setConstraints(
// only run on an unmetered network
Constraint.ON_ANY_NETWORK
)
.setExtras(schedulerextras)
.build();
dispatcher.mustSchedule(myJob);
Jhon you can use firebase jobdispatcher. because it will support from api level 9. you can see below how to create job dispatcher and how to call it.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
scheduleJob(this);
}
public static void scheduleJob(Context context) {
//creating new firebase job dispatcher
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context));
//creating new job and adding it with dispatcher
Job job = createJob(dispatcher);
dispatcher.mustSchedule(job);
}
public static Job createJob(FirebaseJobDispatcher dispatcher){
Job job = dispatcher.newJobBuilder()
//persist the task across boots
.setLifetime(Lifetime.FOREVER)
//.setLifetime(Lifetime.UNTIL_NEXT_BOOT)
//call this service when the criteria are met.
.setService(ScheduledJobService.class)
//unique id of the task
.setTag("UniqueTagForYourJob")
//don't overwrite an existing job with the same tag
.setReplaceCurrent(false)
// We are mentioning that the job is periodic.
.setRecurring(true)
// Run between 30 - 60 seconds from now.
.setTrigger(Trigger.executionWindow(30, 60))
// retry with exponential backoff
.setRetryStrategy(RetryStrategy.DEFAULT_LINEAR)
//.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
//Run this job only when the network is available.
.setConstraints(Constraint.ON_ANY_NETWORK, Constraint.DEVICE_CHARGING)
.build();
return job;
}
public static Job updateJob(FirebaseJobDispatcher dispatcher) {
Job newJob = dispatcher.newJobBuilder()
//update if any task with the given tag exists.
.setReplaceCurrent(true)
//Integrate the job you want to start.
.setService(ScheduledJobService.class)
.setTag("UniqueTagForYourJob")
// Run between 30 - 60 seconds from now.
.setTrigger(Trigger.executionWindow(30, 60))
.build();
return newJob;
}
public void cancelJob(Context context){
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context));
//Cancel all the jobs for this package
dispatcher.cancelAll();
// Cancel the job for this tag
dispatcher.cancel("UniqueTagForYourJob");
}}
ScheduledJobService.java
public class ScheduledJobService extends JobService {
private static final String TAG = ScheduledJobService.class.getSimpleName();
#Override
public boolean onStartJob(final JobParameters params) {
//Offloading work to a new thread.
new Thread(new Runnable() {
#Override
public void run() {
codeYouWantToRun(params);
}
}).start();
return true;
}
#Override
public boolean onStopJob(JobParameters params) {
return false;
}
public void codeYouWantToRun(final JobParameters parameters) {
try {
Log.d(TAG, "completeJob: " + "jobStarted");
//This task takes 2 seconds to complete.
Thread.sleep(2000);
Log.d(TAG, "completeJob: " + "jobFinished");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//Tell the framework that the job has completed and doesnot needs to be reschedule
jobFinished(parameters, true);
}
}}
You will need to use JobScheduler(api >21 ) and GcmNetworkManager (api<21) depending on the api level of android. Check out this library from evernote which takes care of it.
Do you try broadcast receiver? I use Broadcast Receiver with Alarm Manager to vibrate every minute and it work fine. The only problem is that when device turn off or restarted, it not vibrate till I enter my application.
My test code.
public void setAlarm() {
alarmMgr =(AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(AlarmManagerActivity.this, AlarmManagerBroadcastReceiver.class);
intent.setAction("a.b.c.d");
PendingIntent pi = PendingIntent.getBroadcast( getApplicationContext(), 0, intent, 0);
//After after 5 seconds
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 13);
calendar.set(Calendar.MINUTE, 40);
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis()
, (1000) * (60)
, pi);
}
My receiver
#Override
public void onReceive(Context context, Intent intent) {
Log.d(getClass().getSimpleName(), Intent.ACTION_BOOT_COMPLETED);
if ( intent.getAction().equals("a.b.c.d")) {
Log.d(getClass().getSimpleName(), "Custom Broadcast01");
Vibrator vibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(10000);
}
else
Log.d(getClass().getSimpleName(), "no this action for intent!");
}
Broadcast receiver to start Alarm when device restart
<receiver
android:name=".OnBootBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class OnBootBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
setAlarm();
}
}
I'm trying to implement background refreshing service with new JobScheduler(compat by tatarka).
Here is my Service
#Override
public boolean onStartJob(JobParameters params) {
Timber.i("on start job: " + params.getJobId());
return true;
}
#Override
public boolean onStopJob(JobParameters params) {
Timber.i("on stop job: " + params.getJobId());
return true;
}
Here is my JobInfo
public void scheduleJob(View v) {
JobInfo job = new JobInfo.Builder(kJobId++ /*jobid*/, new ComponentName(getActivity(), RefreshJobService.class))
.setPeriodic(2000)
.build();
mTestService.scheduleJob(job);
}
In log i see that my job always works for 1 minute
12-31 12:38:03.884 10059-10059/#/RefreshJobService﹕ on stop job: 0
12-31 12:39:03.891 10059-10059/#/RefreshJobService﹕ on start job: 0
12-31 12:40:03.911 10059-10059/#/RefreshJobService﹕ on stop job: 0
12-31 12:42:08.841 10059-10059/#/RefreshJobService﹕ on start job: 0
12-31 12:43:08.858 10059-10059/#/RefreshJobService﹕ on stop job: 0
So why? I'm set periodic to 2000ms, any value do not influence to 1 minute interval for job. Why?
Until Android 5.1.1 there was a timeout of 60 seconds for a single job. Starting from Android 6, the timeout is now of 10 minutes.
onStopJob is called when the system wants to cancel your job. The reason it is cancelling your job is because it thinks it's still running. You need to call jobFinished(params, bool) at some point in your job to tell the scheduler that it is complete, otherwise it will time out and call onStopJob to cancel it. In addition, returning 'true' from onStartJob tells the system that you are not done processing. You should return 'false' unless you are still doing extra work, eg. passing the work off to a separate thread.
#Override
public boolean onStartJob(JobParameters params) {
Timber.i("on start job: " + params.getJobId());
jobFinished(params, false);
return false;
}
From API 26 (Oreo), setting a smaller period than 15 minutes for periodic jobs will execute at least after 15 minutes.
It sounds like you are asking why is it that you get onStopJob exactly one minute after your job is started.
This 1 minute timeout is defined in code here
43matthew is correct, but I'd like to add something more to this. The 2000 ms that you're talking about is the periodic interval after which the job starts again i.e. execute this job every 2000 ms, not the interval for which the job will execute.