[MUSIC PLAYING]
BEN WEISS: Long-running work should not block the UI,
and that's why WorkManager is built with multithreading
in mind from the ground up.
In this episode of "MAD Skills," I'll introduce you
to APIs WorkManager provides to stay off the main thread,
and then we'll show you a way to update
the UI once your work is done.
So without further delay, let's go.
I'll use the official WorkManager sample app
to guide you through the content at hand.
The app allows you to select a picture
and apply image filters to it.
It contains a library module which
holds the workers used throughout the sample.
As Karen mentioned in the video on WorkManager Basics,
workers are where you get to define what sort of tasks you
want WorkManager to perform.
We recommend creating a separate worker for each distinct task.
This way, it's easier to run work in parallel or sequential.
The most basic worker in the sample is the CleanupWorker.
So that's where we start.
The CleanupWorker takes care of removing
temporary files that get created during the filtering process.
It takes no input and produces no output.
It inherits from Worker and overrides the doWork function.
In doWork, it calls cleanupDirectory
and then returns a result. A thrown exception
result in a failure.
Otherwise, Result.success is returned.
cleanupDirectory performs a blocking file operation.
WorkManager promises that this is executed on a background
thread.
But there is no threading code in CleanupWorker,
so we'll follow the hierarchy to find
more info about how this works.
Worker also contains startWork.
It is provided by the Worker's base class, ListenableWorker,
and will eventually be called by WorkManager
to run the declared task.
And in startWork is where threading magic happens.
For the Worker, startWork requests the background
executor and executes a runnable,
where doWork is executed.
And just like that, the synchronously written code
in doWork becomes asynchronous.
You can even configure which executor to use.
In the application class, I already
have a configuration set up, and they're
calling setExecutor and passing in an executor of my choice.
I can change the threading configuration of my WorkManager
instance.
The Executor's class provides a selection
of available executors to pick from.
And by using a single thread executor,
all background calls will be run sequentially.
A fixed thread pool executor, instead,
will spin up a specific number of threads, which
then can be used in parallel.
Without making any configuration myself,
WorkManager will take care of this,
taking available processes on device into account
and creating between two and four worker threads.
And that looks good to me, so I don't
need to set my own executor.
When writing Android apps in Kotlin,
we can rely on coroutines for lightweight and effective
asynchronous operations.
WorkManager, like many other Jetpack libraries,
comes with an accompanying KTX library.
To use it, we add the ktx suffix to the WorkManager's
dependency, and then sync the project.
Now, instead of Worker, you can extend from CoroutineWorker,
and by doing this, doWork becomes a suspend function.
This means, instead of depending on the default
executor declared in the configuration,
the task now gets managed by a coroutine dispatcher.
Without any configuration, that is the default dispatcher.
In CleanupWorker, we want to delete some files.
This counts as an I/O operation.
And coroutines also has a built-in dispatcher for that.
To move it from the default dispatcher
to the I/O dispatcher, all that's necessary
is declaring in which context this should run, and then
supply the I/O dispatcher.
Now the cleanup code runs on the correct dispatcher,
managed by coroutines.
And we recommend using Kotlin in combination
with coroutines for asynchronous operations.
If you're not using coroutines but RxJava,
we've got you covered.
WorkManager offers support for RxJava 2 and 3.
To use it, add the rxjava suffix to the build.gradle file's
WorkManager dependency and sync.
And now, I can create an Rx version of the CleanupWorker.
The RxJava artifact provides RxWorker,
which is extended from for the RxCleanupWorker.
Here, we override the createWork function
and return a result wrapped in one of RxJava's sequence
types, Single.
This now slots perfectly in a given RxJava sequence.
By default, Rx workers use the thread executor
that was declared in the WorkManager configuration,
like I showed for Worker.
To change the subscribing thread,
you can override getBackgroundExecutor
and return a custom executor for each worker.
The sample app is 100% Kotlin code and uses coroutines.
This means the Rx worker has now served its demonstration
purpose, and I'm deleting it.
WorkManager is designed for long-running operations,
whether deferred or immediate.
Some jobs, like in this sample, are triggered by the user
and can be executed immediately, so it's good practice
to let the user know when the job has
completed by updating the UI.
The WorkManager sample applies filters to images,
then saves these images to the file system.
At this point, it would be nice for the user
to have a chance to review that saved image.
And the WorkManager sample also does just that.
Once the continuation is finished,
I can click on Output, and we'll be
presented with the image that was just filtered.
When the work request to save that image was created,
the save image worker got tagged with the output tag.
Using this tag, we can find and query
state for that worker later on.
But pulling state and checking for completion
is, well, not a very reactive approach.
Luckily, Jetpack libraries are designed
to work together nicely.
In this case, we're using a view model in combination
with our live data.
WorkManager provides several functions
to wrap work infos in a live data,
and we're using getWorkInfosByTagLiveData.
And since we only care for a single work info,
we can use live data KTX artifact,
which enables quick transformation of multiple work
infos to the single work info we care for.
In the FilterActivity, we can then observe that
and react to state changes.
Here, we can check whether the work is finished,
and then update the UI accordingly.
And with that last tip, back to the camera.
In this video, we've seen multiple ways
how WorkManager helps you to keep your long-running jobs
away from the UI thread.
I've also shown you how to communicate results back
to the user.
If you want to learn more about this,
check out the links below for related guides, Codelabs,
and samples.
And come back for the next episode,
when we'll dive into configuration options,
debugging, and testing.
If you liked this video, click the thumbs-up.
And for more videos like this one,
check out the Android Developers channel on YouTube.
Thanks for watching.
[MUSIC PLAYING]