Using WorkManager on Android | Kotlin
WorkManager library allows you to schedule running your tasks on certain conditions as well as monitor, control, and cancel them. This is really useful in many scenarios. In this article, I will show you the basics of how you can use it in Kotlin.
Dependencies
Usage
Worker Class Implementation
To use WorkManager, we need to create a new class that will extend to the Worker class while taking context and work parameters as arguments. Afterward, we will override the doWork() function in the class in which we will store our task which we want to run using WorkManager. In my case, I will name this new class MainActivityWorkManager.kt . Here is how it looks like:
class MainActivityWorkManager(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) < override fun doWork(): Result < //TODO: implement the task >>
The doWork() function returns a type of Result using which one can query the result of the work. As for our example, let’s display a test notification. To do this, we will need to create a new notification channel and show the notification using it. I won’t be explaining how to do that as that’s out of the scope of this article. Here is how my code will look like once the class implementation is finished.
package dev.theimpulson.workmanagerexample import android.app.NotificationChannel import android.app.NotificationManager import android.content.Context import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.work.Worker import androidx.work.WorkerParameters class MainActivityWorkManager(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) < private val CHANNEL_ID = "MainActivity" val notification = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.ic_android_black_24dp) .setContentTitle("Hello World") .setContentText("This is your test notification") .setPriority(NotificationCompat.PRIORITY_HIGH) .build() override fun doWork(): Result < createNotificationChannel() with(NotificationManagerCompat.from(applicationContext)) < notify(1, notification) >return Result.success() > fun createNotificationChannel() < val channel = NotificationChannel( CHANNEL_ID, "Test Notification", NotificationManager.IMPORTANCE_HIGH ).apply < description = "This is your MainActivity's test channel" >val notificationManager = applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannel(channel) > >
The key takeaway from the above code is that, whatever code we want to schedule to run via WorkManager, put it in the doWork() function.
WorkRequest Implentation
- OneTimeWorkRequest that only runs once, and
- PeriodicWorkRequest that runs periodically.
Creating both types of requests is pretty easy. We will create a new val which will store these requests. Create an instance using OneTimeWorkRequestBuilder or PeriodicWorkRequestBuilder as necessary to do this.
Here is how an instance with OneTimeWorkRequestBuilder will look:
val workRequest = OneTimeWorkRequestBuilder() .build()
and here is how an instance with PeriodicWorkRequestBuilder will look:
val workRequest = PeriodicWorkRequestBuilder(1, TimeUnit.HOURS) .build()
For the sake of simplicity, I will use OneTimeWorkRequestBuilder while proceeding further with the article. Creating both types of requests is similar to each other except for the difference of specifying time in periodic requests.
WorkManager Implementation
Now as our worker and work request both are ready, we can use WorkManager to queue the work. To do this, simply get an instance of the WorkManager using the getInstance method with context and call enqueue on it with your work request as an argument.
WorkManager .getInstance(it.context) .enqueue(workRequest)
Considering I have kept my work request and manager inside my button’s setOnClickListener function, I am getting the context using it .
Work Constraints
You can use Constraints to define some constraints which will ensure that your work requests only run if they are satisfied.
To do this, use Constraints.Builder() to build a new constraint and use setConstraints method on the work request which takes the above-defined constraint as an argument.
Currently, 5 types of constraints can be defined which you can check on developer.android.com.
Here is an example of a constraint that ensures that the work requests only runs when the device is charging.
val constraints = Constraints.Builder() .setRequiresCharging(true) .build() val workRequest = OneTimeWorkRequestBuilder() .setConstraints(constraints) .build()
Here is how my MainActivity.kt looks after implementing everything:
package dev.theimpulson.workmanagerexample import android.os.Bundle import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.work.Constraints import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkManager import dev.theimpulson.workmanagerexample.databinding.ActivityMainBinding class MainActivity : AppCompatActivity() < private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) < super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) binding.button.setOnClickListener < val constraints = Constraints.Builder() .setRequiresCharging(true) .build() val workRequest = OneTimeWorkRequestBuilder() .setConstraints(constraints) .build() WorkManager .getInstance(it.context) .enqueue(workRequest) Toast.makeText(it.context, "WorkManager Request Made", Toast.LENGTH_SHORT).show() > > >
Observing and Cancelling Enqueued Work Requests
You can also observe the progress and cancel the enqueued work requests if required. Doing this fairly easy and is documented in android documentation as well. Check them out here:
and that’s it. You can now use WorkManager to queue your work requests to run at any time you need.
Workmanager android kotlin пример
Ты запускаешь процесс в приложении, но ждать результата тебе лень, поэтому ты уходишь в мессенджер, уверенный, что через пять минут процесс закончится сам собой. А он не заканчивается, потому что система убила процесс этого приложения, высвободив ресурсы на другую работу.
Как бороться с Android’ом и не позволять ему уничтожать важные фоновые процессы, расскажет Сергей Смирнов, Android-разработчик CleverPumpkin, который так овладел инструментом WorkManager, что смог в фоновом режиме написать эту статью.
WorkManager появился как инструмент для отложенной фоновой работы. Начиная с первого выпуска, он предоставлял несколько базовых API:
- Запуск отложенных задач. Это позволяло разгрузить приложение от задач, выполнение которых не требуется немедленно — например, синхронизации с сервером в определенное время суток.
Изначально это был инструмент только для отложенной фоновой работы, но в последних версиях Workmanager появилось несколько новых функций. Так, теперь появилась возможность запросить немедленное выполнение задачи, если приложение находится на переднем плане, и быть при этом уверенным в завершении этой работы. Например, приложение может продолжать обрабатывать фото, загружая их на сервер, даже если пользователь уже делает что-то другое.
В больших и сложных приложениях WorkManager помогает оптимизировать и эффективно использовать ресурсы, выделяя работу в отдельный процесс.
Кроме того, появились API-интерфейсы, позволяющие тестировать выполнение одиночных, повторяющихся и последовательных задач. Также улучшена инструментальная поддержка – теперь можно следить за выполнением задач прямо из Android-студии.
WorkManager имеет свою базу данных, что позволяет сохранять информацию и статус работы в случае сбоя процесса или даже перезагрузки устройства. Это дает возможность восстановить статус выполнения работы и продолжить его с того места, на котором остановились, независимо от обстоятельств.
То есть, теперь WorkManager — не только инструмент для выполнения отложенных фоновых задач, но и инструмент для любой задачи, которая обязательно должна быть выполнена и завершена. Google рекомендует WorkManager как лучшее решение для устойчивого выполнения каких-либо задач в global scope: если ваше приложение работает, то задача будет выполнена даже при смене ориентации/конфигурации и других остановок Activity.
Давайте рассмотрим, как WorkManager поможет нам с выполнением длительных задач, даже когда пользователь переводит их в фоновый режим.
Стоит напомнить о сервисах – программных компонентах. Существуют background сервис – он же сервис для выполнения фоновых задач; и foreground сервис, который при выполнении таких же фоновых задач показывает пользователю уведомление, информирующее о том, что приложение запущено.
Пока приложение на переднем плане, мы можем запускать любые сервисы для фоновых задач. Но когда приложение переводится в фон, оно получает от системы еще несколько минут на выполнение задач, после чего по воле Android отключается. У foreground сервиса меньше шансов на уничтожение, но система все равно попытается за счет фонового процесса освободить ресурсы для основного на этот момент приложения. В этой ситуации мы не можем надеяться на то, что наши задачи будут выполнены – и тем более возобновлены позже. Выполнение длительных задач в этой ситуации становится практически невозможным.
WorkManager как раз решает эту проблему, помогая запустить длительную работу и гарантируя устойчивость ее выполнения.
Чтобы сделать это возможным, WorkManager связывает жизненный цикл процесса, в котором выполняется работа, с жизненным циклом foreground service. WorkManager по-прежнему осведомлен об этой работе, но foreground service полностью владеет жизненным циклом выполнения.
Так как foreground service требует показ уведомления пользователю, разработчики добавили API для WorkManager. Также WorkManager предоставляет API-интерфейсы, чтобы пользователь мог остановить выполнение работы прямо из уведомления.