Как WorkManager планирует запросы GET к REST API?

Я просмотрел кодовую лабораторию для WorkManager плюс несколько примеров здесь, но все в коде, который я видел, связано либо с выполнением работы локально на устройстве, либо с загрузкой работы на сервер, а не с загрузкой данных и ответом на полученные данные . В руководстве для разработчиков даже сказано: «Например, приложению может потребоваться время от времени загружать новые ресурсы из сети», поэтому я подумал, что это идеально подходит для этой задачи. Мой вопрос в том, может ли WorkManager справиться со следующим сценарием, а если нет, то каков подходящий инструмент для его обработки:

  1. Запланируйте задание, которое будет выполняться один раз в день в фоновом режиме
  2. Задача состоит в том, чтобы получить данные из REST API (и, если возможно, отправить их в объект LiveData).
  3. Когда данные вернутся, убедитесь, что они новее локальных данных.
  4. Сообщите пользователю, что доступны новые данные.

Мой рабочий класс выглядит примерно так:

public class MyWorker extends Worker {

@NonNull
@Override
public WorkerResult doWork() {
    lookForNewData();
    return WorkerResult.SUCCESS;
}

public void lookForNewData() {
    MutableLiveData<MyObject> liveData = new MutableLiveData<>();

    liveData.observe(lifeCycleOwner, results -> {
        notifyOnNewData(results);
    })

    APILayer.getInstance().fetchData(searchParams, liveData)
}

Моя проблема, конечно, в том, что объект LiveData не может наблюдать, потому что нет активности или фрагмента, который мог бы быть его LifecycleOwner. Но даже если бы я использовал обратный вызов из API для ответа на поступающие данные, мой рабочий уже бы опубликовал, что он был успешным, и, вероятно, он не продолжил бы обратный вызов, верно? Так что я как бы знаю, что этот подход совершенно неправильный, но я не вижу кода для получения данных с помощью WorkManager.

Пожалуйста, помогите с правильным решением и некоторыми примерами кода или некоторыми ссылками, либо с помощью WorkManager, если он может справиться с такой работой, либо с чем-то еще, если это более уместно.


person Arno Schoonbee    schedule 08.06.2018    source источник


Ответы (2)


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

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

Constraints constraint = new Constraints.Builder()
     .setRequiredNetworkType(NetworkType.CONNECTED)
     .build();

PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(MyWorker.class, 1, TimeUnit.DAYS)
     .setConstraints(constraint)
     .build();

WorkManager workManager = WorkManager.getInstance();
workManager.enqueueUniquePeriodicWork("my_unique_worker", ExistingPeriodicWorkPolicy.KEEP, workRequest);
  1. Задача состоит в том, чтобы получить данные из REST API (и, если возможно, отправить их в объект LiveData).

Это можно сделать, отправив ваш запрос синхронно в doWork() от вашего воркера. Я бы не стал использовать LiveData в вашем Worker классе. Мы вернемся к этому позже. Вызов API с Retrofit будет выглядеть, например, так:

@Override
public WorkerResult doWork() {
     Call<MyData> call = APILayer.getInstance().fetchData();
     Response<MyData> response = call.execute();
     if (response.code() == 200) {
          MyData data = response.body();
          // ...
     } else {
          return Result.RETRY;
     }
     // ...
     return Result.SUCCESS;
}
  1. Когда данные вернутся, убедитесь, что они новее локальных данных.

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

  1. Сообщите пользователю, что доступны новые данные.

Если вы планируете задачу с WorkManager, она гарантированно запускается, даже если ваше приложение принудительно завершает работу или устройство перезагружается. Таким образом, ваша задача может быть выполнена, пока ваше приложение не работает. Если вы хотите уведомить пользователя, вы в любом случае можете отправить уведомление. Если вы хотите уведомить пользователя на определенном экране, вы можете подписаться на статус ваших задач. Например, вот так (взято из официального руководства):

WorkManager.getInstance().getStatusById(compressionWork.getId())
.observe(lifecycleOwner, workStatus -> {
    // Do something with the status
    if (workStatus != null && workStatus.getState().isFinished()) {
        // ...
    }
});

Также есть getStatusesForUniqueWork(String uniqueWorkName) для нашего примера.

В официальном руководстве также объясняется, как вернуть данные из вашей задачи, с помощью которой вы можете, например, вызвать setValue() на своем MutableLiveData.

Я бы предложил обновить ваши локальные данные в вашем Worker, подписаться на статус ваших рабочих и, как только это будет успешно, обновить ваш пользовательский интерфейс локальными данными (если вы все равно не подписаны на свои локальные данные, то есть с помощью Room и LiveData).

Изменить. Что касается пункта 4, статус чтения периодических запросов на работу работает немного иначе. Они переключаются только между ENQUEUED и RUNNING до CANCELLED. Но никогда не будет состояния SUCCEEDED или FAILED. Так что прислушивание к isFinished() может оказаться не тем, чего вы ожидаете.

person kphil    schedule 13.07.2018
comment
Спасибо! Мне не хватало возврата результата в doWork () только после того, как обратный вызов дает ответ! Ценить это - person Arno Schoonbee; 23.07.2018

Это первоначальная мысль. Кто-нибудь, пожалуйста, поправьте меня, если я ошибаюсь.

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

мы можем использовать обратный вызов из ответа API, чтобы создать выходные данные работника и установить их с помощью worker.setOutputData(). Затем прослушайте LiveData<WorkStatus> из workManager. Из этого рабочего состояния мы можем получить outputData, используя workStatus.getOutputdata(). Эти данные могут дать нам ответ API, который мы хотим.

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

person Sudhasri    schedule 19.06.2018
comment
Я только что реализовал это, и, похоже, он работает, я бы также сослался на это как на пример: github.com/googlecodelabs/android-workmanager/blob/master/app/ - person Josh; 17.09.2018