Ваш IP: 18.97.14.87






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

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

    Отправка и получение данных

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

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

    Входные данные

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

    val myData: Data = Data.Builder()  
        .putString("keyA", "value1")  
        .putInt("keyB", 1)  
        .build()
    
    val myWorkRequest: OneTimeWorkRequest = OneTimeWorkRequestBuilder<MyFirstWorker>()  
        .setInputData(myData)  
        .build()
    
      
    WorkManager  
        .getInstance(this)  
        .enqueue(myWorkRequest)
    

    Данные помещаются в Dataобъект с помощью его построителя. Далее мы передаем этот объект в setInputDataметод построителя WorkRequest.

    Когда задача запущена, то внутри нее (в MyFirstWorker) мы можем получить ввод вот так:

    class MyFirstWorker(context: Context, parameters: WorkerParameters): Worker(context, parameters) {  
    
        private val TAG = this.javaClass.simpleName  
    
        override fun doWork(): Result {  
    
            val valueA = inputData.getString("keyA")  
            val valueB = inputData.getInt("keyB", 0)  
            Log.d(TAG, "doWork:valueA $valueA")  
            Log.d(TAG, "doWork:valueB $valueB")  
    
            return Result.success()  
    
        }  
    
    }
    

    Журналы:

    2023-04-20 12:44:00 29725-29754 MyFirstWorker D  doWork:valueA value1
    2023-04-20 12:44:00 29725-29754 MyFirstWorker D  doWork:valueB 1
    

    Выходные данные

    Чтобы задача вернула данные, вы должны передать их методу setOutputData. Код MyFirstWorkerбудет таким:

        override fun doWork(): Result {   
    
            val output = Data.Builder()  
                .putString("keyC", "value11")  
                .putInt("keyD", 11)  
                .build()  
    
            return Result.success(output)  
    
        }
    

    Мы можем получить этот вывод из WorkInfo:

    WorkManager  
        .getInstance(this)  
        .getWorkInfoByIdLiveData(myWorkRequest.id)  
        .observe(this) { info ->  
            when (info?.state) {  
                WorkInfo.State.FAILED -> {  
    
                }  
    
                WorkInfo.State.SUCCEEDED -> {  
    
                    val valueC = info.outputData.getString("keyC")  
                    val valueD = info.outputData.getInt("keyD", 0)  
                    Log.e(TAG, "value: $valueC" )  
                    Log.e(TAG, "value: $valueD" )  
    
                }  
    
                else -> {  
    
                }  
    
            }  
    
        }
    

    Результат:

    2023-04-21 08:33:20 32749-32749 WorkManagerActivity E  value: value11
    2023-04-21 08:33:20 32749-32749 WorkManagerActivity E  value: 11
    

    Объект Data, в котором хранятся данные, имеет getKeyValueMapметод, который возвращает вам Mapвсю информацию об этом файле Data.

    И Data.Builderимеет putAll(Map<String, Object> values)метод, в который можно передать Mapвсю информацию, которая будет помещена в Data.

    Данные между задачами

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

    Например, запускаем последовательность из первой и второй задач:

    WorkManager  
        .getInstance(this)  
        .beginWith(myWorkRequest1)  
        .then(myWorkRequest2)  
        .enqueue()
    

    Если первая задача возвращает выходные данные следующим образом:

        override fun doWork(): Result {    
    
            val output = Data.Builder()  
                .putString("keyA", "value1")  
                .putInt("keyB", 1)  
                .build()  
    
            return Result.success(output)  
    
        }
    

    Тогда во втором они придут на вход и мы сможем их получить обычным способом:

    override fun doWork(): Result {  
    
        val valueA = inputData.getString("keyA")  
        val valueB = inputData.getInt("keyB", 0)  
        Log.d(TAG, "doWork:valueA $valueA")  
        Log.d(TAG, "doWork:valueB $valueB")  
    
        return Result.success()  
    
    }
    

    Журналы:

    2023-04-21 08:53:24 628-661 MySecondWorker D  doWork:valueA value1
    2023-04-21 08:53:24 628-661 MySecondWorker D  doWork:valueB 1
    

    Немного усложним пример:

    WorkManager  
        .getInstance(this)  
        .beginWith(listOf(myWorkRequest1, myWorkRequest2))  
        .then(myWorkRequest3)  
        .enqueue()
    

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

    Пусть первая задача возвращает следующие данные:

        override fun doWork(): Result {  
    
            val output = Data.Builder()  
                .putString("keyA", "value1")  
                .putInt("keyB", 1)  
                .putString("keyC", "valueC")  
                .build()  
    
            return Result.success(output)  
    
        }
    

    И второй такой:

        override fun doWork(): Result {  
    
            val output = Data.Builder()  
                .putString("keyA", "value2")  
                .putInt("keyB", 2)  
                .putString("keyD", "valueD")  
                .build()  
    
            return Result.success(output)  
    
        }
    

    Обратите внимание, я специально сделал одинаковые ключи: keyAи keyBдля того, чтобы проверить, какие значения этих ключей придут в третью задачу — из первой задачи или из второй.

    Вывожу в лог входные данные третьей задачи:

        override fun doWork(): Result {  
    
            Log.d(TAG, "data " + inputData.keyValueMap)  
            
            return Result.success()  
    
        }
    

    Результат:

    2023-04-21 09:04:02 783-838 MyThirdWorker D data {keyA=value2, keyB=2, keyC=valueC, keyD=valueD}
    

    В тех же ключах (keyA и keyB) мы видим, что данные пришли из второй задачи. Сначала я подумал, что это произошло из-за того, что вторая задача заняла немного больше времени, чем первая, и логично, что ее значения перезапишут значения из первой задачи при совпадении ключей. Но затем я снова запустил эту последовательность и получил такой результат.

    2023-04-21 09:05:47 963-995 MyThirdWorker D data {keyA=value1, keyB=1, keyC=valueC, keyD=valueD}
    

    Теперь мы видим значения первой задачи в ключах keyAи keyB.

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

    ВводСлияние

    Чтобы преобразовать несколько выходов в один вход, используйте InputMerger. Для этого существует несколько реализаций, и по умолчанию используется OverwritingInputMerger. Мы уже видели, как это работает. Если ключ совпадает, то останется только одно значение.

    Рассмотрим еще один InputMerger — ArrayCreatingInputMerger. Если ключи совпадают, он создаст массив, в который поместит все значения этого ключа.

    Укажем его для третьей задачи с помощью метода setInputMerger:

    val myWorkRequest3: OneTimeWorkRequest = OneTimeWorkRequestBuilder<MyThirdWorker>()  
        .setInputMerger(ArrayCreatingInputMerger::class.java)  
        .build()
    

    Теперь будет ArrayCreatingInputMergerиспользоваться при объединении выходных данных предыдущих задач с входными данными третьей задачи.

    Результатом его работы всегда является массив, даже если совпадений ключей не было:

        override fun doWork(): Result {  
    
            val valueA = inputData.getStringArray("keyA")  
            val valueB = inputData.getIntArray("keyB")  
            val valueC = inputData.getStringArray("keyC")  
            val valueD = inputData.getStringArray("keyD")  
    
            Log.d(TAG, "valueA ${valueA?.toList()}")  
            Log.d(TAG, "valueB ${valueB?.toList()}")  
            Log.d(TAG, "valueC ${valueC?.toList()}")  
            Log.d(TAG, "valueD ${valueD?.toList()}")  
    
            return Result.success()  
    
        }
    

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

    WorkManager  
        .getInstance(this)  
        .beginWith(listOf(myWorkRequest1, myWorkRequest2))  
        .then(myWorkRequest3)  
        .enqueue()
    

    Первая задача вернет следующие данные:

    val output = Data.Builder()  
        .putString("keyA", "value1")  
        .putInt("keyB", 1)  
        .putString("keyC", "valueC")  
        .build()
    

    а второй такой:

    val output = Data.Builder()  
        .putString("keyA", "value2")  
        .putInt("keyB", 2)  
        .putString("keyD", "valueD")  
        .build()
    

    В третьем мы получим следующие входные данные:

    2023-04-21 10:22:55  2307-2340  MyThirdWorker D valueA [value2, value1]
    2023-04-21 10:22:55  2307-2340  MyThirdWorker D valueB [2, 1]
    2023-04-21 10:22:55  2307-2340  MyThirdWorker D valueC [valueC]
    2023-04-21 10:22:55  2307-2340  MyThirdWorker D valueD [valueD]
    

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


    Также опубликовано здесь .

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