Workmanager中唯一的OneTimeWorkRequest

时间:2018-08-07 10:49:42

标签: android android-architecture-components android-jetpack android-workmanager

我们正在使用OneTimeWorkRequest在项目中启动后台任务。

  1. 在应用程序启动时,我们正在启动OneTimeWorkRequest(例如req A)
  2. 根据用户的操作,我们启动相同的工作请求A。

在某些情况下,如果在工作请求A进行时该应用被杀死,则Android在应用重新启动时会自动重新启动请求A。同样,我们也再次启动了请求A。因此,请求A的两个实例并行运行并导致死锁。

为避免这种情况,我在app start中执行了以下代码,以检查worker是否正在运行,但始终返回false。

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

有没有更好的方法来解决这个问题?

我检查了beginUniqueWork()。如果我只有一个请求,会更贵吗?

修改2: 这个问题是关于唯一的一次性任务。为了启动唯一的定期任务,我们有一个单独的API enqueueUniquePeriodicWork()。但是我们没有用于启动独特的一次性工作的API。我很困惑在连续对象之间使用还是手动检查并启动方法。

在最近的构建中,Android为此enqueueUniqueWork()添加了新的api。这就是他们在发行说明中提到的确切原因。

  

添加WorkManager.enqueueUniqueWork()API以使唯一队列入队   无需创建WorkContinuation的OneTimeWorkRequests。   https://developer.android.com/jetpack/docs/release-notes

4 个答案:

答案 0 :(得分:3)

编辑2:

11月8日发行说明:

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

  

添加WorkManager.enqueueUniqueWork()API以使唯一队列入队   无需创建WorkContinuation的OneTimeWorkRequests。

这表示,alpha11拥有此新API,可以唯一地排队一次工作。

我尝试如下更改代码:

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

我尝试使用beginUniqueWork API。但是有时无法运行。因此,我最终编写了以下函数。

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);
}

答案 1 :(得分:2)

您可以使用具有唯一名称的beginUniqueWork()
如果您使用ExistingWorkPolicy
附录:这2个请求将串行运行。
KEEP:如果第一个请求正在运行,则不会运行第二个请求。
替换:这2个请求将并行运行。

答案 2 :(得分:1)

使用getStatusesByTag返回List<WorkStatus>的LiveData 之所以将其作为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的返回值是否为true时,您才开始执行该任务,否则就可以安全地启动它,这意味着另一个具有相同标签的任务正在运行

答案 3 :(得分:0)

由于所有答案大部分都已过时,因此您可以像这样在标记的工作程序上侦听更改:

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

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

            }
        });