Reputation: 1008
I have an android app that needs to perform some long running image processing in the background (processing many chunks of it)
Versions
androidx.work:work-runtime:2.1.0
The WorkManager is setup as follows (simplified code)
@NonNull
@Override
public Result doWork() {
// 1 Create a NotificationChannel with unique ID
// 2 read params from getInputData
// 3 create as many instances of a class
// that extends Callable<String> as there are small subtasks
// 4 add all these tasks to a ThreadPoolExecutor and call
// invokeAll() which blocks the main Worker thread as required
// by the WorkManager API
// 5 Everytime a subtask finishes, update the notification progress
}
@Override
public void onStopped(){
// Call .shutDown() on the ThreadPoolExecutor
}
The work itself is submitted with the following parameters
WorkManager mWorkManager = WorkManager.getInstance(context);
Data input = new Data.Builder()
.putString(PARAM_IN_DTO, dto.toString())
.build();
OneTimeWorkRequest mRequest = new OneTimeWorkRequest
.Builder(ExtractorWorker.class)
.setInputData(input)
.addTag(dto.mapName)
.build();
mWorkManager.enqueueUniqueWork(
WORK_NAME,
ExistingWorkPolicy.KEEP,
mRequest);
As WORK_NAME
is unique with the code above I would expect there to never be two tasks running at the same time (note I tried with ExistingWorkPolicy.REPLACE
- same issue)
The job starts correctly and the progress in the notification starts updating. After a while, it seems that the task is started again with the previous one still running. I see this from the unique notification toggleing between two states (the notification age between now
and the previous age, and the progress between 0% and the previous progress). There is clearly two tasks now that update the same notification. I confirmed this by setting a random notification ID instead of a unique one, and two notifications appeared, each living their own life.
I wonder
enqueueUniqueWork
Upvotes: 2
Views: 2835
Reputation: 6476
How long can it takes the "long running image processing"?
WorkManager has a hard limit of 10 minutes for its workers (same limit of the underlying JobScheduler API). If a Worker takes more than 10 minutes, it is cancelled and rescheduled, whatever is returned by the Worker in this case is thrown away by WorkManager. The worker will be executed like if it was never been executed before.
If you fail to stop the Worker (there's also a isStopped()
function you can use in your doWork()
method to check this condition) you may end up having two instances of your worker running at the same time.
You can find more on the documentation
Upvotes: 1
Reputation: 1008
I think i do understand the behaviour now. When WorkManager
wants to interrup thr job (tonresume it later) onStopped()
is called with a very short timeout. The executor.shutDown()
call exceeds this and a TimeoutException
is thrown which seems to cause the shutdown not to perform normally. The execution continues.
After this timeout is raised, and because doWork
has not returned anything on time, WorkManager must assume the result value is either retry
or fail
and resends the job to its internal executor.
In short there is no guarantee that two jobs will not run in parallel. If a job fails to stop properly and quickly when onStopped
is called, a new job will be submitted nevertheless. Workmanager does not manage threads in its executor directly and not interrupt jobs that do not stop properly when required and might restart it.
The solution in this case was for me to set my jobs internal state to cancel so that they finish immediately when started. Once the executor has ran out it deletes a lock file. The new process (resumed) waits for the lock file to be gone before checking the state of the work and resumes from there.
Upvotes: 0