Ваш IP: 18.97.14.87






    Запуск фоновых задач в Android с помощью WorkManager: часть 3

    Предыдущая статья из этой серии:Запуск фоновых задач в Android с помощью WorkManager: часть 2

    Последовательность задач

    В этом руководстве будут рассмотрены методы запуска задач в определенном последовательном порядке.

    Например, скажем, у вас есть задача, где вы хотите скачать архив с файлами, распаковать его, а затем обработать файлы.

    Это можно сделать с помощью трех последовательных задач:

    • скачать архив
    • распаковка архива
    • обработка файлов

    Давайте посмотрим, как мы можем запускать эти задачи последовательно

    Во-первых, давайте удостоверимся, что задачи, запущенные обычным образом, будут выполняться параллельно. Запускаем сразу три задачи:

    val worker1: WorkRequest = OneTimeWorkRequestBuilder<Worker1>()
        .build()  
    
    val worker2: WorkRequest = OneTimeWorkRequestBuilder<Worker2>()  
        .build()  
    
    val worker3: WorkRequest = OneTimeWorkRequestBuilder<Worker3>()  
        .build()  
    
    WorkManager  
        .getInstance(this)  
        .enqueue(listOf(worker1, worker2, worker3))
    

    Смотрим лог:

    2023-04-18 21:07:05 18984-19015 Worker2  D  doWork: start
    2023-04-18 21:07:05 18984-19016 Worker3  D  doWork: start
    2023-04-18 21:07:05 18984-19014 Worker1  D  doWork: start
    
    2023-04-18 21:07:06 18984-19014 Worker1  D  doWork: end
    2023-04-18 21:07:06 18984-19016 Worker3  D  doWork: end
    2023-04-18 21:07:06 18984-19015 Worker2  D  doWork: end
    

    Задачи запускались в  21:07:05и выполнялись параллельно в разных потоках, и каждый выполнялся в свое время.

    Мы видели параллельное исполнение. Теперь выполним их последовательно. Передаем в beginWithметод первую задачу и тем самым создаем начало последовательности задач. Далее, вызвав thenметод, добавляем в последовательность вторую и третью задачи. И с помощью enqueueметода отправляем эту последовательность на запуск.

    Результат:

    2023-04-18 21:28:59 20007-20036 Worker1  D  doWork: start
    2023-04-18 21:29:00 20007-20036 Worker1  D  doWork: end
    2023-04-18 21:29:00 20007-20044 Worker2  D  doWork: start
    2023-04-18 21:29:01 20007-20044 Worker2  D  doWork: end
    2023-04-18 21:29:01 20007-20045 Worker3  D  doWork: start
    2023-04-18 21:29:02 20007-20045 Worker3  D  doWork: end
    

    В логах видно, что задачи выполнялись одна за другой, и именно в той последовательности, которую мы указали.

    Как критерии повлияют на выполнение последовательности задач?

    Задача, которую нельзя запустить в данный момент, будет ждать. И, соответственно, все остальные задачи, которые стоят в последовательности после этой задачи, тоже будут ждать.

    Давайте посмотрим на пример.

    Пусть у второй задачи есть критерий — наличие интернета. Отключаем интернет на устройстве и запускаем последовательность. Первой задаче все равно; он работает. Приходит очередь второй задачи, а интернета нет; поэтому вторая задача откладывается, а третья задача может быть запущена только после завершения второй. Так что приходится ждать. Включаем интернет, выполняется второе задание, а за ним и третье.

    Если какая-либо задача в последовательности заканчивается статусом FAILURE, вся цепочка будет остановлена.

    Мы можем комбинировать последовательное и параллельное выполнение задач.

    WorkManager  
        .getInstance(this)  
        .beginWith(listOf(worker1, worker2))  
        .then(listOf(worker3, worker4))  
        .then(worker5)  
        .enqueue()
    

    Здесь мы формируем последовательность, но при этом указываем две задачи для первого (beginWith) и второго (first then) шага последовательности.

    В результате задачи worker1и worker2будут выполняться первыми, а выполняться они будут параллельно. После этого worker3и worker4будут выполняться, также параллельно друг другу. И после этого — worker5.

    В логах это будет выглядеть так:

    2023-04-18 21:41:31 20434-20464 Worker1  D  doWork: start
    2023-04-18 21:41:31 20434-20465 Worker2  D  doWork: start
    2023-04-18 21:41:32 20434-20464 Worker1  D  doWork: end
    2023-04-18 21:41:32 20434-20465 Worker2  D  doWork: end
    2023-04-18 21:41:32 20434-20476 Worker3  D  doWork: start
    2023-04-18 21:41:32 20434-20464 Worker4  D  doWork: start
    2023-04-18 21:41:33 20434-20476 Worker3  D  doWork: end
    2023-04-18 21:41:33 20434-20464 Worker4  D  doWork: end
    2023-04-18 21:41:33 20434-20465 Worker5  D  doWork: start
    2023-04-18 21:41:34 20434-20465 Worker5  D  doWork: end
    

    Первая и вторая задачи запускаются одновременно. Когда они оба завершены, начинаются третий и четвертый, также одновременно. Когда они оба выполнены, начинается пятая задача.

    Рассмотрим другой случай. Предположим, нам нужно, чтобы вторая задача выполнялась после первой, а четвертая — после третьей. У нас есть две последовательности, и их можно запускать параллельно. И когда эти две последовательности выполнены, нужно приступить к пятому заданию.

    Вот как это делается:

    val chain12: WorkContinuation = WorkManager  
        .getInstance(this)  
        .beginWith(worker1)  
        .then(worker2)  
    
    val chain34: WorkContinuation = WorkManager  
        .getInstance(this)  
        .beginWith(worker3)  
        .then(worker4)  
    
    WorkContinuation.combine(listOf(chain12, chain34))  
        .then(worker5)  
        .enqueue()
    

    WorkContinuationпредставляет собой последовательность задач. Создаем chain12последовательность, состоящую из первой и второй задач, и chain34последовательность, состоящую из третьей и четвертой задач. Чтобы эти последовательности выполнялись параллельно друг другу, мы передаем их в combineметод. Затем мы передаем методу пятую задачу then, которая запускается после того, как все последовательности из combineбудут выполнены.

    Результат:

    2023-04-19 15:58:20  1511-1534  Worker1 D  doWork: start
    2023-04-19 15:58:20  1511-1535  Worker3 D  doWork: start
    2023-04-19 15:58:21  1511-1534  Worker1 D  doWork: end
    2023-04-19 15:58:21  1511-1535  Worker3 D  doWork: end
    2023-04-19 15:58:21  1511-1571  Worker2 D  doWork: start
    2023-04-19 15:58:21  1511-1534  Worker4 D  doWork: start
    2023-04-19 15:58:22  1511-1571  Worker2 D  doWork: end
    2023-04-19 15:58:22  1511-1534  Worker4 D  doWork: end
    2023-04-19 15:58:22  1511-1571  Worker5 D  doWork: start
    2023-04-19 15:58:23  1511-1571  Worker5 D  doWork: end
    

    Запускаются первая и третья задачи, т.е. последовательности начинают выполняться параллельно. Когда обе последовательности выполнены, начинается пятая задача.

    Уникальная работа

    Мы можем сделать последовательность задач уникальной. Для этого мы запускаем последовательность с помощью метода beginUniqueWork.

    private fun work() {  
    
        val worker1 = OneTimeWorkRequestBuilder<Worker1>()  
            .build()   
    
        val worker3 = OneTimeWorkRequestBuilder<Worker3>()  
            .build()  
    
        val worker5 = OneTimeWorkRequestBuilder<Worker5>()  
            .build()  
    
        WorkManager.getInstance(this)  
            .beginUniqueWork("work123", ExistingWorkPolicy.REPLACE, worker1)  
            .then(worker3)  
            .then(worker5)  
            .enqueue()  
    
    }
    
    findViewById<AppCompatButton>(R.id.startWork).setOnClickListener {  
        Log.e(TAG, "enqueue REPLACE")  
        work()  
    }
    

    Укажите имя последовательности, режим и первую задачу последовательности.

    Мы указали REPLACE в качестве режима. Это означает, что если последовательность с тем же именем уже запущена, то при следующем запуске текущая последовательность будет остановлена ​​и запущена новая.

    Я добавил логирование в вызов метода enqueue, который запускает последовательность. Посмотрим, что происходит в логах.

    2023-04-19 16:52:32 17809-17809 WorkManagerActivity3 E  enqueue REPLACE
    2023-04-19 16:52:33 17809-17882 Worker1              D  doWork: start
    2023-04-19 16:52:38 17809-17882 Worker1              D  doWork: end
    2023-04-19 16:52:38 17809-17914 Worker3              D  doWork: start
    2023-04-19 16:52:43 17809-17914 Worker3              D  doWork: end
    2023-04-19 16:52:43 17809-17952 Worker5              D  doWork: start
    
    2023-04-19 16:52:46 17809-17809 WorkManagerActivity3 E  enqueue REPLACE
    2023-04-19 16:52:46 17809-17880 Worker5              D  onStopped
    2023-04-19 16:52:46 17809-17882 Worker1              D  doWork: start
    2023-04-19 16:52:48 17809-17952 Worker5              D  doWork: end
    2023-04-19 16:52:51 17809-17882 Worker1              D  doWork: end
    2023-04-19 16:52:51 17809-17914 Worker3              D  doWork: start
    2023-04-19 16:52:56 17809-17914 Worker3              D  doWork: end
    2023-04-19 16:52:56 17809-17952 Worker5              D  doWork: start
    2023-04-19 16:53:01 17809-17952 Worker5              D  doWork: end
    

    16:52:32 — это первый запуск последовательности. Задачи начинают выполняться одна за другой.

    16:52:46 — пока работает MyWorker5, создаю и запускаю такую ​​же последовательность с тем же именем — work123. Выполняемая в данный момент последовательность останавливается и запускается новая.

    Режим KEEP будет поддерживать текущую выполняемую последовательность. Новый будет проигнорирован.

    Код:

    WorkManager.getInstance(this)  
        .beginUniqueWork("work123", ExistingWorkPolicy.KEEP, worker1)  
        .then(worker3)  
        .then(worker5)  
        .enqueue()
    

    Журналы:

    2023-04-19 17:05:17 20030-20030 WorkManagerActivity3 E  enqueue KEEP
    2023-04-19 17:05:18 20030-20207 Worker1              D  doWork: start
    2023-04-19 17:05:23 20030-20207 Worker1              D  doWork: end
    2023-04-19 17:05:23 20030-20227 Worker3              D  doWork: start
    2023-04-19 17:05:28 20030-20227 Worker3              D  doWork: end
    2023-04-19 17:05:28 20030-20272 Worker5              D  doWork: start
    2023-04-19 17:05:29 20030-20030 WorkManagerActivity3 E  enqueue KEEP
    2023-04-19 17:05:33 20030-20272 Worker5              D  doWork: end
    

    17:05:29 — Я попытался запустить последовательность еще раз, но был проигнорирован, потому что в работе уже есть последовательность с таким названием.

    Режим APPEND запустит новую последовательность после выполнения текущей.

    Код:

    WorkManager.getInstance(this)  
        .beginUniqueWork("work123", ExistingWorkPolicy.APPEND, worker1)  
        .then(worker3)  
        .then(worker5)  
        .enqueue()
    

    Журналы:

    2023-04-19 17:10:28 21636-21636 WorkManagerActivity3 E  enqueue APPEND
    2023-04-19 17:10:28 21636-21730 Worker1              D  doWork: start
    2023-04-19 17:10:33 21636-21730 Worker1              D  doWork: end
    2023-04-19 17:10:33 21636-21784 Worker3              D  doWork: start
    2023-04-19 17:10:38 21636-21784 Worker3              D  doWork: end
    2023-04-19 17:10:38 21636-21836 Worker5              D  doWork: start
    2023-04-19 17:10:39 21636-21636 WorkManagerActivity3 E  enqueue APPEND
    2023-04-19 17:10:43 21636-21836 Worker5              D  doWork: end
    2023-04-19 17:10:43 21636-21730 Worker1              D  doWork: start
    2023-04-19 17:10:48 21636-21730 Worker1              D  doWork: end
    2023-04-19 17:10:48 21636-21784 Worker3              D  doWork: start
    2023-04-19 17:10:53 21636-21784 Worker3              D  doWork: end
    2023-04-19 17:10:53 21636-21836 Worker5              D  doWork: start
    2023-04-19 17:10:58 21636-21836 Worker5              D  doWork: end
    

    17:10:39 — текущая последовательность не прерывалась, а новая запускалась сразу после окончания текущей.

    Будьте осторожны с этим режимом, т.к. ошибка в текущей последовательности может привести к тому, что новая последовательность не запустится.

    В этих последних примерах я создал и перезапустил одну и ту же последовательность, но текущая и новая последовательности могут состоять из разных задач. Главное здесь такое же название, как и последовательности.

     

    реагировать на историю с копией
    отнеситесь к истории с душой