I want to replace the job scheduling aspect of my existing data syncing system with the new JetPack WorkManager (link to codelabs) component (in a sandbox branch of the app). My existing system works well but some of the new features in WorkManager
would come in handy (e.g. chaining).
My current system uses a shared LiveData
to communicate the progress from a job in progress to any UI element (RecyclerView
in my case) observing on it (I'm actually SwitchMapping in the ViewModel
into a list of SyncItem
s)
data class SyncItem(
val title: String,
private var _progress: Int,
var total: Int) : BaseObservable() {
var progress: Int
@Bindable get() = _progress
set(value) {
_progress = value
notifyPropertyChanged(BR.progress)
}
}
The new WorkManager
component has several methods (getStatusById
, getStatusesByTag
, etc.) that can be used to retrieve a LiveData with one or more WorkStatus
es, but these only report a course-grained status (running, success, failed, cancelled).
What is the recommended way of communicating progress (e.g. '546/1234 items downloaded') to the UI? The setOutputData
/getOutputData
pair seems to be used more to communicate between Worker
s (which I need when chaining) than with the UI.
Attached is a screenshot of what it looks like (in a [test] version of my app using my old method) when a user opens the sync status page (2 items completed, rest in progress).
In the final product the user will be able to cancel any jobs in progress and re-issue once-off work requests. Normally the jobs will be fired off by PeriodicWorkRequest
.
Natively Supported
implementation 'androidx.work:work-runtime:2.5.0'
Report progress on Worker:
public class FooWorker extends Worker {
public FooWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
try {
setProgressAsync(new Data.Builder().putInt("progress", 0).build());
Thread.sleep(1000);
setProgressAsync(new Data.Builder().putInt("progress", 50).build());
Thread.sleep(1000);
setProgressAsync(new Data.Builder().putInt("progress", 100).build());
return Result.success();
} catch (InterruptedException e) {
e.printStackTrace();
return Result.failure();
}
}
}
Observe progress of Worker:
WorkManager.getInstance(context).getWorkInfosForUniqueWorkLiveData("test").observe(lifecycleOwner, new Observer<List<WorkInfo>>() {
@Override
public void onChanged(List<WorkInfo> workInfos) {
if (workInfos.size() > 0) {
WorkInfo info = workInfos.get(0);
int progress = info.getProgress().getInt("progress", -1);
//Do something with progress variable
}
}
});
ListenableWorker now supports the setProgressAsync() API, which allows it to persist intermediate progress. These APIs allow developers to set intermediate progress that can be observed by the UI. Progress is represented by the Data type, which is a serializable container of properties (similar to input and output, and subject to the same restrictions).
See Android Documentation
The best way to do it is to write intermediate progress to your own data store and expose a LiveData<>
. We are considering adding this feature in a future alpha.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With