Chapters

Hide chapters

Android Debugging by Tutorials

First Edition · Android 12 · Kotlin 1.6 · Android Studio Chipmunk (2021.2.1)

Section I: Debugging Basics

Section 1: 8 chapters
Show chapters Hide chapters

8. Debugging WorkManager Jobs With Background Task Inspector
Written by Zahidur Rahman Faisal

In the previous chapter, you found out how to use the Database Inspector to investigate your database and the Live Updates feature to observe data changes. You learned how to use a query for previewing results while debugging the app and you got familiar with exporting your database to make sharing easier.

All this sounds really great, but you’re probably wondering where to find the data for populating your local database and how to download it. That could become a demanding task if the data is big and you need to sync it often. You need to think about user experience as well. Therefore, the solution is to run complex tasks in the background.

Performing long-running operations and processing data in the background is very common for mobile applications. Whether syncing your everyday tasks in the calendar or backing up your contacts and images to the cloud, they’re all background tasks, a basic and necessary part of your smartphone usage habit.

Here are the conditions one task needs to meet to become a background task:

  • None of the ongoing activities are currently visible to the user.
  • The task isn’t running any foreground services that the user explicitly started.

Common Types of Background Tasks

A background task includes additional processes like scheduling, monitoring, logging and user notification separately from the actual work it’s supposed to do. Considering the type of work it does, a background task can be any of the three types below:

  1. Immediate: Expedited tasks that must begin immediately and complete quickly, e.g. fetching data from the server.
  2. Long-Running: Tasks that run for a more extended period, usually more than 10 minutes, e.g. downloading big files.
  3. Deferrable: Scheduled tasks that start at a later point in time or run periodically, e.g. periodic sync with the server.

PodPlay relies on Android WorkManager to handle all types of background tasks. The app is smart enough to regularly check your subscribed channels and update the episode list if new episodes are published. This job runs in the background without you even realizing it’s happening!

This is great, but it can take a toll on your mobile data and CPU usage if the background operation is frequently running. In this chapter, you’ll inspect background tasks and schedule them to use optimal resources. You’ll learn how to:

  • List currently running or scheduled background tasks.
  • Find details of a background task.
  • Check the execution flow of background tasks.

Starting the Background Task Inspector

The scheduling of background tasks happens when you launch PodPlay. You can then see what’s happening by starting the Background Task Inspector.

Here are the steps:

  1. Open the Podplay starter project and run the app on an emulator or connected device using API level 26 or higher.

  2. Select View ▸ Tool Windows ▸ App Inspection from the menu bar.

  1. In the App Inspection toolbar, choose your device. Then, select com.yourcompany.podplay from the running app process in the dropdown menu.

  1. Then select the Background Task Inspector tab.

Now, you prepared the environment for inspecting PodPlay.

Viewing and Inspecting Workers

PodPlay contains three different workers that check for new episodes in your subscribed channels:

  1. LoadPodcastsWorker loads all podcast channels saved in PodcastRepo.
  2. GetNewEpisodesWorker fetches the list of episodes for each individual channel from the server, then compares it with existing data in PodcastRepo to find if there are new episodes.
  3. SaveNewEpisodesWorker appends in PodcastRepoif new episodes are found and notifies the user.

The Work Table

Background Task Inspector displays a table listing all the tasks which WorkManager assigns.

At first glance, it reveals four important pieces of information about a worker:

  1. The class name that implements the Worker interface handling the work.
  2. What state that work is in right now (e.g., Enqueued, Running, Blocked, Succeeded or Failed).
  3. The exact time that the work started execution.
  4. How many times the work has been retried if there was any problem during execution.

The table displays the list of workers in alphabetical order based on their class name by default, but you can sort them based on each field above. For example, click the Start column, and that task will be sorted by its start time, as you can see below:

Extracting Work Details

The table does a lot more than just show you a list of works; it allows you to filter each individual work by its tag and extract task details.

Your next objective is to leverage them and find details about a specific worker from the WorkManager’s queue.

Finding a Work by Tag

When your WorkManager is executing a whole lot of work, the easiest way to find one of them from the table is to use the Tag Filter.

Click All Tags, then select com.yourcompany.podplay.worker.GetNewEpisodesWorker from the dropdown.

Note: WorkManageradds a tag in: [Your Package Name].worker.[Worker Class Name] format to each worker if the tag isn’t explicitly set while enqueuing the work.

Anatomy of a Worker

Now that you’ve filtered GetNewEpisodesWorker from the table, click the row to reveal details about the work. The Task Details panel will open next to the row.

The panel breaks down the information into four major segments:

  1. Description - This section explains the worker class itself.

The fields describe the following:

  • Class: Displays the worker class name and the fully-qualified package name.

  • Tags: The default tag assigned by WorkManager or any additional tags specified by the developer to identify this worker while creating it.

  • UUID: An unique identifier provided by WorkManager to each worker.

  1. Execution - Presents the execution criteria of the worker.

This displays four important bits of information:

  • Enqueued By: Shows the class name which created and enqueued this worker. The class name is only shown if that worker is enqueued after opening the table; otherwise, it shows as “Unavailable”.

  • Constraints: Displays details if the worker is running under any work Constraints. Constraints are one or more rules that must be met before executing the work as pre-conditions, for example, internet connectivity. If no constraints are set, then it simply displays “None”.

  • Frequency: Indicator that represents how many times the worker’s going to execute the work. It can be a OneTimeWorkRequest or PeriodicWorkRequest that’s scheduled and repetitive.

  • State: The work’s current state, either it’s Enqueued, Running, Blocked, Succeeded or Failed.

  1. WorkContinuation - If the worker enqueued as part of any work-chain, this section would display its details.

Important fields to look at in this segment:

  • Previous: UUID of the previous worker in the work chain, if any.
  • Next: UUID of the next worker in this work chain (if available). It displays “None” if no worker is queued after this.
  • Unique work chain: This is a quick overview of your work chain. It lists the UUIDs of all the workers in the sequence that they are executed. The worker marked as “Current” is the worker you’re viewing details of right now. The tick icons beside each UUID mark that the task has been executed successfully.
  1. Results - This section is for displaying the final results of executed work.

It consists of three specific pieces of data:

  • Time started: The start time of the work execution.
  • Retries: Shows the number of times the work has been executed, which is one, in this case. If work failed for some reason, the retry count would have increased.
  • Output data: The returned result or data from doWork() in your worker class.

Navigating Through Works

The WorkContinuation section is interactive; You can easily navigate to any worker in the work chain simply by clicking on the worker’s UUID.

Click the first UUID from the Unique work chain to preview its details. The Task Details panel changes, as you can see below:

Notice the highlighted areas above. The changes are:

  1. The class changed to the LoadPodcastsWorker path.
  2. The previous work is “None”.
  3. The system marks this first UUID in Unique work chain as “Current”.

Now the Task Details panel show for your first worker LoadPodcastsWorker, which means you’re pointing at the beginning of your work chain.

Next, click on the last UUID in Unique work chain; it will navigate you to the end of the work chain:

Look at the Task Details panel again; it shows the worker class is SaveNewEpisodesWorker and outputs “Saved new episodes” in the Output data section, meaning the end of the work chain. As in the previous example, the Current mark moved next to the selected UUID.

The Flow Diagram

The table can generate a nice graph of workers for better visualization. Click any of the highlighted areas below to bring out the Graph View:

The graph presents each work from the execution flow, maintaining the exact order. So, the graphical representation of your work chain looks like this:

It’s visible from the graph that there are three workers in the work chain:

  1. LoadPodcastsWorker executed first and loaded all podcast channels.
  2. Then GetNewEpisodesWorker fetched new episodes for each channel.
  3. Finally, SaveNewEpisodesWorker saved new episode information and finished the operation.

Click on of the highlighted points below to easily switch back to the table view:

Practical Example: A Simplified Work-Chain

In a real-world app, you might need to combine all the tasks of loading podcasts, fetching episodes, and saving updates in a single worker. The worker should run periodically in the background and only notify the user if new episodes have been added.

EpisodeUpdateWorker in PodPlay does all the heavy lifting mentioned above.

Open EpisodeUpdateWorker.kt and look into doWork():

override suspend fun doWork(): Result = coroutineScope {
  val job = async {
    // 1
    val db = PodPlayDatabase.getInstance(applicationContext, this)
    val repo = PodcastRepo(RssFeedService.instance, db.podcastDao())
    // 2
    val podcastUpdates = repo.updatePodcastEpisodes()
    // 3
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      createNotificationChannel()
    }
    for (podcastUpdate in podcastUpdates) {
      displayNotification(podcastUpdate)
    }
  }
  job.await()
  Result.success()
}

What the job doing here is:

  1. Loads PodcastRepo, the repository where all your bookmarked podcast channels are stored.
  2. Calls updatePodcastEpisodes() that fetches the latest episode list from the channels and returns updates as a list in podcastUpdates.
  3. It displays a notification to the user for each item in podcastUpdates.

Your next step is to schedule this job using WorkManager. To do so, open PodcastActivity.kt and replace scheduleJobs() as follows:

private fun scheduleJobs() {
  // 1
  val constraints: Constraints = Constraints.Builder().apply {
    setRequiredNetworkType(NetworkType.CONNECTED)
    setRequiresBatteryNotLow(true)
  }.build()
  // 2
  val request = PeriodicWorkRequestBuilder<EpisodeUpdateWorker>(
      repeatInterval = 1,
      repeatIntervalTimeUnit = TimeUnit.HOURS)
      .setConstraints(constraints)
      .build()
  // 3
  WorkManager.getInstance(this).enqueueUniquePeriodicWork(TAG_EPISODE_UPDATE_JOB,
      ExistingPeriodicWorkPolicy.REPLACE, request)
}

The above code:

  1. Defines a work constraint to run the job only when the device is connected to the internet and the battery isn’t low. This ensures optimum resource consumption when the worker is running or prevents the worker from running if the conditions aren’t met.

  2. Creates a PeriodicWorkRequest that’s scheduled to execute once every hour if the work constraints are satisfied.

  3. Enqueues the periodic request to WorkManager with a tag. ExistingPeriodicWorkPolicy.REPLACE makes sure if there’s an existing work request with the same tag, this will replace the existing one.

Now, build and run the app and observe the work from Background Task Inspector; you’ll see some interesting developments!

The changes you see on the highlighted points are listed below:

  1. EpisodeUpdateWorker is now enqueued in the table of workers.
  2. The Execution section under the Task Details panel now shows your defined constraints for the worker. It also displays that the work will execute at a periodic frequency, not one at a time.
  3. The Unique work chain indicates that EpisodeUpdateWorker is the only item in the work queue displaying a single UUID.
  4. The Results section is showing “Awaiting data…” for output because the work is scheduled to be executed in the future.

Switch to the Graph View that’ll display a single item now:

Canceling a Worker

Sometimes you may want to cancel a worker which is scheduled to run in future. It’s easy to stop a currently running or enqueued worker using Background Task Inspector. Select EpisodeUpdateWorker and click the highlighted icon from the toolbar:

This will suspend any pending or scheduled work; you’ll see the change immediately in the Status column.

Next, you’ll learn how to monitor network activity with Network Profiler while you add new episodes in PodPlay. Good luck on your next quest!

Key Points

  • You can easily inspect WorkManager workers using Background Task Inspector.
  • Background Task Inspector contains a table with all workers.
  • You can find a specific worker using tags.
  • The Task Details panel reveals the current state, execution flow, work continuation and results of background work.
  • You can see a detailed work chain graph using Graph View.

Where to Go From Here?

You’ve now mastered inspecting background tasks in Android! To learn how to utilize Background Task Inspector more, check out View and inspect Jobs, Alarms, and Wakelocks.

But don’t stop there; take a look at our tutorials related to the background processing:

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.