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

Я посмотрел на codelab для 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, если он может справиться с такой работой или чем-то еще, если это более уместно.

Ответ 1

  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() может оказаться не тем, что вы ожидаете.

Ответ 2

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

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

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

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