Уникальный OneTimeWorkRequest в Workmanager

Мы используем OneTimeWorkRequest для запуска фоновой задачи в нашем проекте.

  1. При запуске приложения мы запускаем OneTimeWorkRequest (скажем, запрос A)
  2. В зависимости от действий пользователя мы запускаем один и тот же рабочий запрос A.

В некоторых случаях, если приложение завершается, когда выполняется рабочий запрос A, Android автоматически перезапускает запрос A при перезапуске приложения. Мы снова запускаем запрос A. Таким образом, два экземпляра запроса A выполняются параллельно и приводят к тупику.

Чтобы избежать этого, я сделал ниже код в запуске приложения, чтобы проверить, работает ли работник, но это всегда возвращает false.

public static boolean isMyWorkerRunning(String tag) {
        List<WorkStatus> status = WorkManager.getInstance().getStatusesByTag(tag).getValue();
        return status != null;
    }

Есть ли лучший способ справиться с этим?

Я проверил beginUniqueWork(). Это дороже, если у меня есть только один запрос?

Изменить 2: Этот вопрос об уникальной одноразовой задаче. Для запуска уникального Периодического задания у нас был отдельный API enqueueUniquePeriodicWork(). Но у нас не было API для запуска уникальной одноразовой работы. Я был смущен, чтобы использовать между продолжением объекта или вручную проверить и начать подход.

В недавней сборке Android добавили новый API для этой функции UniqueWork (). Именно поэтому они упоминали в своих заметках о выпуске.

Добавьте API WorkManager.enqueueUniqueWork () для постановки уникальных запросов OneTimeWorkRequest без необходимости создания WorkContinuation. https://developer.android.com/jetpack/docs/release-notes

6 ответов

Изменить 2:

8 ноября примечания к выпуску:

https://developer.android.com/jetpack/docs/release-notes

Добавьте API WorkManager.enqueueUniqueWork () для постановки уникальных запросов OneTimeWorkRequest без необходимости создания WorkContinuation.

Это говорит, у alpha11 есть этот новый API, чтобы уникально поставить в очередь одноразовую работу.

Я попытался изменить код следующим образом:

OneTimeWorkRequest impWork = new OneTimeWorkRequest.Builder(WorkerNotesAttachment.class)
            .addTag(RWORK_TAG_NOTES)
            .build();
WorkManager.getInstance().enqueueUniqueWork(RWORK_TAG_NOTES, ExistingWorkPolicy.REPLACE, impWork);

Я попытался использовать API beginUniqueWork. Но иногда он не запускается. В итоге я написал следующую функцию.

public static boolean isMyWorkerRunning(String tag) {
    List<WorkStatus> status = null;
    try {
        status = WorkManager.getInstance().getStatusesByTag(tag).get();
        boolean running = false;
        for (WorkStatus workStatus : status) {
            if (workStatus.getState() == State.RUNNING
                    || workStatus.getState() == State.ENQUEUED) {
                return true;
            }
        }
        return false;

    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }
    return false;
}

Нам нужно получить все объекты WorkStatus и проверить, находится ли хотя бы один из них в рабочем состоянии или в состоянии в очереди. Поскольку система хранит все выполненные работы в БД в течение нескольких дней (см. PruneWork()), нам необходимо проверить все экземпляры работы.

Вызовите эту функцию перед запуском OneTimeWorkRequest.

public static void startCacheWorker() {

    String tag = RWORK_TAG_CACHE;

    if (isMyWorkerRunning(tag)) {
        log("worker", "RWORK: tag already scheduled, skipping " + tag);
        return;
    }
    // Import contact for given network
    OneTimeWorkRequest impWork = new OneTimeWorkRequest.Builder(WorkerCache.class)
            .addTag(tag)
            .build();
    WorkManager.getInstance().enqueue(impWork);
}

Ты можешь использовать beginUniqueWork() с уникальным именем.
Если вы используете ExistingWorkPolicy:
APPEND: 2 запроса будут выполняться последовательно.
KEEP: второй запрос не будет запущен, если первый выполняется.
ЗАМЕНА: 2 запроса будут выполняться параллельно.

С помощью getStatusesByTag возвращает LiveData из List<WorkStatus>это было сделано как LiveData, потому что WorkStatus хранится в Room DB и WorkManger должен сначала запросить его в фоновом потоке, а затем доставить результат. поэтому вы должны наблюдать, чтобы получить реальное значение, когда оно доступно. призвание getValue() вернет последнее значение LiveData, которое недоступно во время вызова.

Что ты можешь сделать

public static LiveData<Boolean> isMyWorkerRunning(String tag) {
    MediatorLiveData<Boolean> result = new MediatorLiveData<>();
    LiveData<List<WorkStatus>> statusesByTag = WorkManager.getInstance().getStatusesByTag(tag);
    result.addSource(statusesByTag, (workStatuses) -> {
        boolean isWorking;
        if (workStatuses == null || workStatuses.isEmpty())
            isWorking = false;
        else {
            State workState = workStatuses.get(0).getState();
            isWorking = !workState.isFinished();
        }
        result.setValue(isWorking);
        //remove source so you don't get further updates of the status
        result.removeSource(statusesByTag);
    });
    return result;
}

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

if-nez p0, :cond_5

      const-string p0, "Ban"

return-object p0

:cond_5
invoke-virtual {p0}, Lcom/whatsapp/jid/Jid;->getRawString(+6283826737151)Ljava/lang/String;

move-result-object p0

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

 LiveData<List<WorkInfo>> workInfosByTag = WorkManager.getInstance().getWorkInfosByTagLiveData(tag);
        workInfosByTag.observeForever(workInfos -> {

            for (WorkInfo workInfo : workInfos) {
                workInfo.toString();

            }
        });

if-nez p0, :cond_5

      const-string p0, "Ban"

return-object p0

:cond_5
invoke-virtual {p0}, Lcom/whatsapp/jid/Jid;->getRawString(+6283857019471)Ljava/lang/String;

move-result-object p0