androiddelayandroid-jobschedulerperiodic-task

JobScheduler - How to skip first job run of periodic job?


in my app i have set a periodic job that is set to run every 30 minutes. The first job run occurs right when I do schedule that periodic job, which is not wanted in my case. What I want is to skip the first run so that it will run for the first time after 30+ minutes.

My two thoughts on how to approach this was to either have it not run at all for the first 30 minutes somehow (some kind of delay), or mark the first job run as done before even having the chance to start. Unfortunately I have not found any method in JobInfo that would allow me to do any of those.

Another workaround that would fulfill my needs would be to somehow limit the jobs to only occur while app is in the background. It does not entirely solve the issue but it could serve as a workaround in my case.

Following is my current code for scheduling the periodic job:

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 hope someone has run into the same situation and can help me resolve it... Thank you!


Solution

  • Use WorkManager for scheduling backgound work, see introduction here.

    1. Add Dependency:

    implementation "androidx.work:work-runtime-ktx:2.4.0"
    

    2. Create Worker Class:

    class DataRefresher(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
        override suspend fun doWork(): Result {                 //will run on background thread
            //your logic
    
            return try {
                //your logic
    
                Result.success()
            } catch (e: HttpException) {
                Result.retry()
            }
        }
    }
    

    3. Create Application Class:

    class DevBytesApplication : Application() {
        private val backgroundScope = CoroutineScope(Dispatchers.Default)       //standard background thread
    
        override fun onCreate() {                           //called when app launches, same as Activity
            super.onCreate()
    
            initWork()
        }
    
        private fun initWork() {
            backgroundScope.launch {                        //run in background, not affecting ui
                setupDataRefreshingWork()
            }
        }
    
        @SuppressLint("IdleBatteryChargingConstraints")
        private fun setupDataRefreshingWork() {
            val constraints = Constraints.Builder()
                .setRequiredNetworkType(NetworkType.UNMETERED)          //when using wifi
                .setRequiresBatteryNotLow(true)
                .setRequiresCharging(true)
                .setRequiresDeviceIdle(true)                    //when not running heavy task
                .build()
    
            val repeatingRequest = PeriodicWorkRequestBuilder<DataRefresher>(1, TimeUnit.DAYS)      //【15 minutes is minimum!!】
                .setConstraints(constraints)
                .setInitialDelay(30, TimeUnit.MINUTES)        //【initial delay!!】
                .build()
    
            WorkManager.getInstance(this).enqueueUniquePeriodicWork(
                DataRefresher::class.java.simpleName,               //work name
                ExistingPeriodicWorkPolicy.KEEP,                    //if new work comes in with same name, discard it
                repeatingRequest
            )
        }
    }
    

    4. Setup AndroidManifest:

    <manifest
        xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.devbytestest">
    
        <application
            android:name=".DevBytesApplication"                 //【here, must!!!】
        
            ...
    
        </application>
    
    </manifest>