Topic: 17 Understanding WorkManger in Android

This is our seventeenth topic from learn android from basic to advance series

Β·

7 min read

Topic: 17 Understanding WorkManger in Android

Hello devs, You already know about services in Android right? So In Android development, both WorkManager and Service are used for background processing, but they serve different purposes and have distinct characteristics.

WorkManager

WorkManager is an Android Jetpack library that simplifies the process of performing background tasks, making it easier to execute deferrable, asynchronous tasks even when the app is not running. This API allows you to schedule jobs (one-off or repeating) and chain and combine jobs. You can also apply execution constraints to them such as triggering when the device is idle or charging, or executing when a content provider changes.

Here's a brief overview of how WorkManager works along with a short example in Kotlin:

  1. Defining a Worker: A Worker is an abstract class where you define the actual work to be done in the background. You override the doWork() method and put your background task logic inside it.
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters

class MyWorker(context: Context, params: WorkerParameters) : Worker(context, params) {

    override fun doWork(): Result {
        // Do your background work here
        // This is executed on a background thread managed by WorkManager
        return Result.success()
    }
}
  1. Setting Constraints: You can set constraints on when your background task should run using Constraints. For example, you might want your task to run only when the device is charging and connected to Wi-Fi.
import androidx.work.Constraints
import androidx.work.NetworkType

val constraints = Constraints.Builder()
    .setRequiresCharging(true)
    .setRequiredNetworkType(NetworkType.UNMETERED)
    .build()
  1. Creating and Enqueuing Work: You create an instance of OneTimeWorkRequest or PeriodicWorkRequest by passing your Worker class and constraints. Then, you enqueue the work using WorkManager.
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager

val myWorkRequest = OneTimeWorkRequest.Builder(MyWorker::class.java)
    .setConstraints(constraints)
    .build()

WorkManager.getInstance(context).enqueue(myWorkRequest)
  1. Observing Work Status (Optional): If you need to observe the status of your background task, you can use WorkManager to get LiveData of WorkInfo and observe it for changes.
WorkManager.getInstance(context).getWorkInfoByIdLiveData(myWorkRequest.id)
    .observe(owner, { workInfo ->
        // Handle work status changes here
    })

That's a basic example of using WorkManager in an Android app. WorkManager takes care of managing tasks efficiently, handling scenarios such as retries, respecting system constraints, and providing a simple API for scheduling and monitoring background work. This helps you focus on implementing your app's features while ensuring that background tasks are executed reliably and efficiently.

WorkManager classes, you need to know

1. Worker:

Worker is an abstract class where you define the actual work to be done in the background. You override the doWork() method and put your background task logic inside it.

import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters

class MyWorker(context: Context, params: WorkerParameters) : Worker(context, params) {

    override fun doWork(): Result {
        // Do your background work here
        // This is executed on a background thread managed by WorkManager
        return Result.success()
    }
}

2. WorkRequest:

WorkRequest is an abstract class representing a request to do some work, either once (OneTimeWorkRequest) or periodically (PeriodicWorkRequest). You use a WorkRequest to specify which Worker should be used and any additional constraints.

Example with OneTimeWorkRequest:

import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager

val myWorkRequest = OneTimeWorkRequest.Builder(MyWorker::class.java)
    .build()

WorkManager.getInstance(context).enqueue(myWorkRequest)

Example with PeriodicWorkRequest:

import androidx.work.PeriodicWorkRequest
import androidx.work.WorkManager
import java.util.concurrent.TimeUnit

val periodicWorkRequest = PeriodicWorkRequest.Builder(MyWorker::class.java, 1, TimeUnit.HOURS)
    .build()

WorkManager.getInstance(context).enqueue(periodicWorkRequest)

3. WorkManager:

WorkManager is the central class for managing and executing tasks. You use it to enqueue WorkRequests and observe the status of the work.

Example:

val workManager = WorkManager.getInstance(context)

// Enqueue a OneTimeWorkRequest
workManager.enqueue(myWorkRequest)

// Enqueue a PeriodicWorkRequest
workManager.enqueue(periodicWorkRequest)

// Observe the status of work
workManager.getWorkInfoByIdLiveData(myWorkRequest.id)
    .observe(owner, { workInfo ->
        // Handle work status changes here
    })

4. Constraints:

Constraints allow you to specify conditions under which your work should run, such as network connectivity, battery status, or device charging state.

import androidx.work.Constraints
import androidx.work.NetworkType

val constraints = Constraints.Builder()
    .setRequiredNetworkType(NetworkType.CONNECTED)
    .setRequiresBatteryNotLow(true)
    .setRequiresCharging(true)
    .build()

Example with Constraints:

val myWorkRequest = OneTimeWorkRequest.Builder(MyWorker::class.java)
    .setConstraints(constraints)
    .build()

In summary, WorkManager provides a set of classes and APIs to simplify the scheduling and execution of background tasks in Android apps. By using classes like Worker, WorkRequest, WorkManager, and Constraints, you can efficiently manage background work, ensuring that it executes reliably and efficiently while respecting system constraints and user preferences.

When to use WorkManager

The WorkManager library is a good choice for tasks that are useful to complete, even if the user navigates away from the particular screen or your app.

Some examples of tasks that are a good use of WorkManager:

  • Uploading logs

  • Applying filters to images and saving the image

  • Periodically syncing local data with the network

WorkManager offers guaranteed execution, and not all tasks require that. As such, it is not a catch-all for running every task off of the main thread.

Alright devs, Let's create a simple WorkManager Example. we'll create a simple task that generates a notification every 24 hours.

1. Create a Worker Class:

First, create a Worker class that extends the Worker class and defines the background task to be performed.

import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import java.util.*

class NotificationWorker(context: Context, workerParams: WorkerParameters) :
    Worker(context, workerParams) {

    override fun doWork(): Result {
        // Create a notification
        createNotification()

        // Return success
        return Result.success()
    }

    private fun createNotification() {
        val notificationManager =
            applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        val channelId = "work_manager_channel"
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                channelId,
                "WorkManager Channel",
                NotificationManager.IMPORTANCE_DEFAULT
            )
            notificationManager.createNotificationChannel(channel)
        }

        val notification = NotificationCompat.Builder(applicationContext, channelId)
            .setContentTitle("WorkManager Notification")
            .setContentText("This is a notification from WorkManager!")
            .setSmallIcon(android.R.drawable.ic_dialog_info)
            .build()

        notificationManager.notify(Random().nextInt(), notification)
    }
}

2. Enqueue WorkRequest:

Now, create and enqueue a PeriodicWorkRequest to schedule the execution of the NotificationWorker every 24 hours.

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.work.PeriodicWorkRequest
import androidx.work.WorkManager
import java.util.concurrent.TimeUnit

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Create a PeriodicWorkRequest to run NotificationWorker every 24 hours
        val periodicWorkRequest =
            PeriodicWorkRequest.Builder(
                NotificationWorker::class.java,
                1, // repeat interval
                TimeUnit.DAYS
            ).build()

        // Enqueue the work request
        WorkManager.getInstance(applicationContext).enqueue(periodicWorkRequest)
    }
}

3. Add Required Permissions:

Don't forget to add the required permissions in your AndroidManifest.xml file:

<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

4. Run the App:

Run the application, and you should start receiving notifications from the NotificationWorker every 24 hours.

This example demonstrates how to use WorkManager to schedule and execute background tasks in an Android Kotlin application. WorkManager simplifies the process of handling background tasks, ensuring they run efficiently and reliably even when the app is not in the foreground.

So devs during the start of the blog I mentioned that WorkManager and Service are both mechanisms provided by the Android framework for performing background tasks, but they serve different purposes and have different use cases. Here's a breakdown of the main differences between WorkManager and Service in Android Kotlin:

  1. Lifecycle Awareness:

    • WorkManager: WorkManager is lifecycle-aware, meaning it can schedule tasks to run even when the app's process is killed or the device restarts. It manages the execution of tasks efficiently based on the device's state and constraints.

    • Service: Services are not lifecycle-aware by default. While you can bind a service to a component like an activity, they are generally not designed to handle tasks that need to survive across configuration changes, process death, or system restarts.

  2. Execution Flexibility:

    • WorkManager: WorkManager provides flexibility in executing tasks based on various conditions such as device charging status, network connectivity, and system constraints. It allows you to schedule tasks that are guaranteed to execute, even if the app is not currently active.

    • Service: Services are typically used for tasks that need to run continuously in the background, but they don't offer built-in support for scheduling or managing tasks under different conditions. You have more control over the execution logic within a service, but you need to handle scenarios like system resource constraints and battery optimization manually.

  3. Battery Optimization:

    • WorkManager: WorkManager takes care of optimizing tasks for battery consumption by respecting system battery optimization features. It ensures that tasks are executed efficiently without draining the device's battery excessively.

    • Service: Services are not inherently optimized for battery usage. While you can implement strategies to minimize battery consumption within a service, you need to be mindful of resource usage and implement best practices to avoid draining the battery unnecessarily.

  4. Ease of Use:

    • WorkManager: WorkManager provides a high-level API for scheduling and managing background tasks, making it easier to implement deferrable, asynchronous tasks without worrying about managing threads, process lifecycle, or system constraints.

    • Service: Services offer more flexibility and control over background tasks but require more manual handling of threading, lifecycle management, and system resource constraints. They are suitable for tasks that need fine-grained control or long-running operations within the app's context.

Alright devs, It's time to wrap up this blog. I hope this blog helps you to understand WorkManager. Ok, So our next topic is Jetpack Compose. It's a cool modern toolkit to make your app's design awesome.


Connect with Me:

Hey there! If you enjoyed reading this blog and found it informative, why not connect with me on LinkedIn? 😊 You can also follow my Instagram page for more mobile development-related content. πŸ“²πŸ‘¨β€πŸ’» Let’s stay connected, share knowledge and have some fun in the exciting world of app development! 🌟

Check out my Instagram page

Check out my LinkedIn

Β