Get immediate access to this and over 1,500+ other videos and books.

Boost your skills with a Beginner subscription. With over 60+ video courses and our core foundational programming books bundled in one subscription, it’s simply the best investment you can make in your development career.

Chain Work

The student materials have been reviewed and are updated as of SEPTEMBER 2022.

Implement another worker, to apply a filter to the image, practicing what you’ve learned so far.


Heads up... You've reached locked video content where the transcript will be shown as obfuscated text.

You can unlock the rest of this video course, and our entire catalogue of books and videos, with a Professional subscription.

After building a worker, observing its status, and returning a result, you’re ready to build more workers, and chain them together! :]

class UploadResponse(val message: String = "", val url: String = "")
  suspend fun uploadImage(
      @Part imageFile: MultipartBody.Part
  ): UploadResponse
suspend fun uploadImage(file: File): UploadResponse {
  val part: MultipartBody.Part = MultipartBody.Part.createFormData("file",, file.asRequestBody())

  return apiService.uploadImage(part)
class UploadImageWorker(context: Context, workerParameters: WorkerParameters) :
    CoroutineWorker(context, workerParameters) {

  override suspend fun doWork(): Result {

class UploadImageWorker(context: Context, workerParameters: WorkerParameters) :
    CoroutineWorker(context, workerParameters) {

  private val remoteApi by lazy { App.remoteApi }

  override suspend fun doWork(): Result {
    val imagePath = inputData.getString("image_path") ?: return Result.failure()

    val result = remoteApi.uploadImage(File(imagePath))

    return if (result.message == "Success!") {
    } else {
private const val NO_IMAGE = "noImage"

class LocalImageCheckWorker(context: Context, workerParameters: WorkerParameters) :
    Worker(context, workerParameters) {

  override fun doWork(): Result {
    val imagePath = inputData.getString("image_path") ?: NO_IMAGE
    val parts = imagePath.split("/")

    if (imagePath.isBlank() || imagePath == NO_IMAGE) {
      val outputData = workDataOf("is_downloaded" to false)
      return Result.success(outputData)

    val rootFile = applicationContext.externalMediaDirs.first()

    val lastSegment = parts.last()

    return try {
      val isDownloaded = rootFile.list()?.any { filePath -> lastSegment in filePath }
      val outputData = workDataOf("is_downloaded" to (isDownloaded ?: false))

    } catch (error: Throwable) {
val isAlreadyDownloaded = inputData.getBoolean("is_downloaded", false)
val imageDownloadPath = inputData.getString("image_path") ?: return Result.failure()
val parts = imageDownloadPath.split("/")

if (isAlreadyDownloaded) {
  val imageFile = File(applicationContext.externalMediaDirs.first(), parts.last())
  return Result.success(workDataOf("image_path" to imageFile.absolutePath))
val imageUrl = URL("$BASE_URL/files/$imageDownloadPath")


val file = File(applicationContext.externalMediaDirs.first(), parts.last())
val localImagePath = BASE_URL + "/files/${image.imagePath}"

val localImageCheckWorker = OneTimeWorkRequestBuilder<LocalImageCheckWorker>()
    .setInputData(workDataOf("image_path" to imageUrl))
val workManager = WorkManager.getInstance(requireActivity())
val context = activity as? Context ?: return
val selectedImage = data?.data ?: return
val fileUri = FileUtils
    .getImagePathFromInputStreamUri(selectedImage, context.contentResolver, context)

val worker = OneTimeWorkRequestBuilder<UploadImageWorker>()
    .setInputData(workDataOf("image_path" to fileUri))