Reputation: 1162
I have a service listening for incoming FCM notifications. When such a notification is received, I run a job with WorkManager.
When multiple notifications are received at the same time, how can I ensure that only one job is executed at the time? The jobs should be executed sequentially. In this case, I want to send an sms and these cannot not be sent simultaneously. (Note that I do other stuff like http requests in this job before and after sending sms, that's why I decided to create a job for it instead of sending an sms straight from the service.)
public class MyFirebaseMessagingService extends FirebaseMessagingService {
public void onMessageReceived(RemoteMessage remoteMessage) {
//...
OneTimeWorkRequest sendSmsWorker = new OneTimeWorkRequest.Builder(SendSmsWorker.class).build();
WorkManager.getInstance().enqueue(sendSmsWorker);
//...
}
}
I've checked the advanced section in the WorkManager docs; it mentions Chained sequences, however these must be chained to each other explicitly (beginWith/then/then/...).
Upvotes: 16
Views: 3713
Reputation: 1690
Another way to execute Workers sequentially without the limitation of breaking the chain if you return a result other than Result.SUCCESS
, is to extend ListenableWorker
instead of Worker
.
If you see the Worker
implementation, they are using WorkManager's background executor to run all Worker instances. You can define your own executor that uses a single thread:
// Define your executor here
private static final Executor sExecutor = Executors.newSingleThreadExecutor();
@Override
public final @NonNull ListenableFuture<Result> startWork() {
mFuture = SettableFuture.create();
sSerialExecutor.execute(new Runnable() {
@Override
public void run() {
try {
Result result = doWork();
mFuture.set(result);
} catch (Throwable throwable) {
mFuture.setException(throwable);
}
}
});
return mFuture;
}
Note that you'll have to use Guava's SettableFuture
.
Upvotes: 0
Reputation: 29
There is a concept in Work Manager, which is chaining though, which we can arrange workers as many orders as we want to.
Suppose there are some sequential tasks in which we have to
Now, for achieving this we can chain them in this order so they run sequentially
WorkManager.getInstance(myContext)
.beginWith(createDataWork) //This is going enqueue first
.then(cacheWork) // enqueue after returning success from create worker
.then(uploadWork)// enqueue after returning success from cache worker
.enqueue()
Every then
will returns a new WorkerContinuation()
instance
Upvotes: 0
Reputation: 17380
You need to create the jobs as a OneTimeWorkRequest with the same group name, and enqueue them as a unique work with APPEND. This will make all the jobs to be appended to the same chain and execute sequentially.
Beware that you should always return Result.SUCCESS, or Result.RETRY at most. Returning Result.FAILURE will cancel all the enqueued jobs. If you need to know if a job has failed, then you could set a custom flag in the worker setOutputData to handle such scenario as you may require.
final String JOB_GROUP_NAME = "your_jobs_group_name";
......
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(YOUR_WORK.class)
.setInputData(YOUR_WORK_DATA_BUILDER.build())
.build();
final WorkManager workManager = WorkManager.getInstance();
WorkContinuation work = workManager.beginUniqueWork(JOB_GROUP_NAME, ExistingWorkPolicy.APPEND, request);
work.enqueue();
Upvotes: 26