Marko Topolnik
Marko Topolnik

Reputation: 200168

Android 9 always calls onStopJob right after onStartJob returns with true

I have a scheduled job implemented as follows:

class RefreshImageService : JobService() {
    override fun onStartJob(params: JobParameters): Boolean {
        info { "RefreshImage: start job" }
        appCoroScope.launch {
            fetchImageAndUpdateWidget()
            info { "RefreshImage: finished" }
            jobFinished(params, false)
        }
        return true
    }

    override fun onStopJob(params: JobParameters): Boolean {
        info { "RefreshImage: stop job" }
        return true
    }
}

In the logs I always see RefreshImage: stop job immediately after RefreshImage: start job and then, later on, RefreshImage: finished, which means Android released my wakelock right away and my service was allowed to finish only by chance. It would probably be killed if the HTTP request took a bit longer.

This is how I declare the service:

<service
    android:name=".RefreshImageService"
    android:permission="android.permission.BIND_JOB_SERVICE"
/>

And this is how I schedule it:

val jobInfo = JobInfo.Builder(refreshImageJobId, 
        ComponentName(context, RefreshImageService::class.java))
    .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
    .setMinimumLatency(latencyMillis)
    .setOverrideDeadline(HOUR_IN_MILLIS)
    .build()
context.jobScheduler.schedule(jobInfo)

Is there something I'm doing wrong, or maybe a workaround that would make Android behave properly?

Upvotes: 3

Views: 738

Answers (1)

Marko Topolnik
Marko Topolnik

Reputation: 200168

I realized the problem lies within fetchImageAndUpdateWidget() which must also deal with scheduling the next image fetch based on the Last-Modified header of the current HTTP result. If Android decides to kill my job before it reaches the scheduling code, my job will never run again.

To prevent this, I added a line that provisionally schedules the job before making the HTTP request. That, however, caused Android to try to immediately stop my current job, as per the documentation of JobScheduler.schedule():

If a job with the given ID is currently running, it will be stopped.

This leaves me without an option to robustly implement my scheduling behavior. I had to resort to using another scheduled job just to check whether my primary job lost its schedule.

Upvotes: 2

Related Questions