检查是否已经安排了WorkManager任务

59

我如何检查是否已经安排了WorkManager

这是我的代码,用于安排WorkManager

public static void scheduleWork() {
    PeriodicWorkRequest.Builder photoCheckBuilder =
            new PeriodicWorkRequest.Builder(WorkManagerService.class, TIME_INTERVAL_IN_SECONDS,
                    TimeUnit.SECONDS);
    PeriodicWorkRequest photoCheckWork = photoCheckBuilder.build();
    WorkManager instance = WorkManager.getInstance();
    if (instance != null) {
        instance.enqueueUniquePeriodicWork("TAG", ExistingPeriodicWorkPolicy.KEEP , photoCheckWork);
    }
}

我在我的Application类的onCreate()中调用scheduleWork()。 我可以通过这种方法检查我的服务是否正在运行。但是,如果已经安排了工作管理器以消除已安排时间不一致,我不想再次安排它。

例如:


if(!workManagerIsScheduled())
   {
     scheduleWork();
   }

有什么解决方案吗?


您可以获取工作请求的状态...以进行检查。 - Man
你试过了吗? - Man
请查看@Man的答案。 - Khemraj Sharma
1
不需要检查工作程序是否已经在运行,因为您正在使用instance.enqueueUniquePeriodicWork()和'ExistingPeriodicWorkPolicy.KEEP'。只有在没有带有uniqueWorkName标签的待处理工作时,它才会运行新的PeriodicWorkRequest。 - Rai_Gaurav
'ExistingPeriodicWorkPolicy.KEEP' 是针对特定任务而非工作的。如果某个服务正在执行其工作,则此标志将使服务保持运行状态,直到完成其工作。 - Khemraj Sharma
没错,这就是我想说的...@Khemraj,很好... - Man
7个回答

115

更新

如果您需要检查已经运行的工作管理器,只是因为您不想重复工作。您可以简单地使用 enqueueUniquePeriodicWork()

此方法允许您排队一个具有唯一名称的 PeriodicWorkRequest, 其中特定名称的一个 PeriodicWorkRequest 同时只能处于活动状态。例如, 您可能只希望一个同步操作处于活动状态。如果有一个挂起,您可以选择让其运行或将其替换为新的工作。

因此,您不必担心工作重复问题。

 workmanager.enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.KEEP , photoCheckWork);
  • TAG是工作管理器检查重复性的唯一名称。
  • 您可以在ExistingPeriodicWorkPolicy中选择ExistingPeriodicWorkPolicy.KEEPExistingPeriodicWorkPolicy.REPLACE之间。

原始帖子

当我找不到任何方法时,我创建了这个方法。

检查工作是否通过TAG运行

if (your_work_manager.version >= 1.0.0-alpha11)

private boolean isWorkScheduled(String tag) {
    WorkManager instance = WorkManager.getInstance();
    ListenableFuture<List<WorkInfo>> statuses = instance.getWorkInfosByTag(tag);
    try {
        boolean running = false;
        List<WorkInfo> workInfoList = statuses.get();
        for (WorkInfo workInfo : workInfoList) {
            WorkInfo.State state = workInfo.getState();
            running = state == WorkInfo.State.RUNNING | state == WorkInfo.State.ENQUEUED;
        }
        return running;
    } catch (ExecutionException e) {
        e.printStackTrace();
        return false;
    } catch (InterruptedException e) {
        e.printStackTrace();
        return false;
    }
}

如果(your_work_manager.version < 1.0.0-alpha11)

private boolean isWorkScheduled(String tag) {
    WorkManager instance = WorkManager.getInstance();
    LiveData<List<WorkStatus>> statuses = instance.getStatusesByTag(tag);
    if (statuses.getValue() == null) return false;
    boolean running = false;
    for (WorkStatus workStatus : statuses.getValue()) {
        running = workStatus.getState() == State.RUNNING | workStatus.getState() == State.ENQUEUED;
    }
    return running;
}

当其任务中的某些状态为RUNNINGENQUEUED时,它将返回true

示例代码

public static final String TAG_MY_WORK = "mywork";

if(!isWorkScheduled(TAG_MY_WORK)) { // check if your work is not already scheduled
    scheduleWork(TAG_MY_WORK); // schedule your work
}

public static void scheduleWork(String tag) {
    PeriodicWorkRequest.Builder photoCheckBuilder =
            new PeriodicWorkRequest.Builder(WorkManagerService.class, TIME_INTERVAL_IN_SECONDS,
                    TimeUnit.SECONDS);
    PeriodicWorkRequest photoCheckWork = photoCheckBuilder.build();
    WorkManager instance = WorkManager.getInstance();
    instance.enqueueUniquePeriodicWork(tag, ExistingPeriodicWorkPolicy.KEEP , photoCheckWork);
}

你好Khemraj,你的解决方案给了我这种类型的错误 androidx.work.impl.utils.futures.SettableFuture无法转换为android.arch.lifecycle.LiveData 它无法转换 LiveData<List<WorkStatus>> statuses = instance.getStatusesByTag(tag); - Rahul Patil
5
检查实例的 getWorkInfosByTag(tag) 方法总是返回空列表,因此我使用了 instance.getWorkInfosForUniqueWork(tag),在依赖中添加 implementation "androidx.work:work-runtime:2.2.0" 可以正常工作。 - Sarath Kumar
1
否则,如果您不使用 running |= state == WorkInfo.State.RUNNING | state == WorkInfo.State.ENQUEUED;,那么 running 将会拥有最后一个 workInfo 的值。 - Täg
有没有办法获取所有工人的列表? - android developer
以上代码无法运行。@SarathKumar,你说得对!我们应该使用你的方法让它正常工作。 - Burak
显示剩余2条评论

7

被接受的答案是错误的(严重的,因为它会默默失败)。 这里是一个正确的答案。

private boolean isWorkScheduled(String tag, Context context) {

        WorkManager instance = WorkManager.getInstance(context);
        ListenableFuture<List<WorkInfo>> statuses = instance.getWorkInfosByTag(tag);

        boolean running = false;
        List<WorkInfo> workInfoList = Collections.emptyList(); // Singleton, no performance penalty

        try {
            workInfoList = statuses.get();
        } catch (ExecutionException e) {
            Log.d(TAG, "ExecutionException in isWorkScheduled: " + e);
        } catch (InterruptedException e) {
            Log.d(TAG, "InterruptedException in isWorkScheduled: " + e);
        }

        for (WorkInfo workInfo : workInfoList) {
            WorkInfo.State state = workInfo.getState();
            running = running || (state == WorkInfo.State.RUNNING | state == WorkInfo.State.ENQUEUED);
        }
        return running;

除了一些重构以避免误导的多次返回之外,bug 在这一行。

running = state == WorkInfo.State.RUNNING | state == WorkInfo.State.ENQUEUED;

如果使用这行代码,你只会得到最后一个被评估的 running。有一个注释建议使用运算符=|。即使结果是正确的,但是这段代码将变得不清晰和(稍微)不够优化。| 是一个按位运算符,而 || 是一个逻辑运算符。我们要执行的操作是逻辑运算,而不是按位运算。对于布尔值,||| 给出相同的结果,但只有 || 是短路的,这是我们所期望的行为。

这段代码适用于至少WorkManager 2.5.0和2.6.0。


6

1.0.0-alpha11版本开始,许多东西将不再适用于WorkStatus,因为它已被移除,这是一项重大变更。请参阅发行说明

WorkStatus已更名为WorkInfo。所有相应的getStatus方法变体已更名为相应的getWorkInfo变体。这是一个重大变更。

在更新到alpha11之后,工作代码如下。

private boolean isWorkScheduled(List<WorkInfo> workInfos) {

    boolean running = false;

    if (workInfos == null || workInfos.size() == 0) return false;

    for (WorkInfo workStatus : workInfos) {
        running = workStatus.getState() == WorkInfo.State.RUNNING | workStatus.getState() == WorkInfo.State.ENQUEUED;
    }

    return running;
}

3
自2018年10月11日起,您可以使用ListenableFuture代替已删除的SynchronousWorkManager。
您现在可以使用ListenableFutures同步获取和观察。例如,WorkManager.enqueue()以前返回void;现在它返回ListenableFuture。您可以调用ListenableFuture.addListener(Runnable, Executor)或ListenableFuture.get()在操作完成后运行代码。
更多信息可以在此处找到。
另一个选择是使用ListenableWorker:
这是一个可以在WorkManager中异步执行工作的类。对于大多数情况,我们建议使用Worker,它提供了一个简单的同步API,该API在预先指定的后台线程上执行。
在调用startWork()函数后,它将返回ListenableFuture。
更多信息可以在此处找到。

我似乎无法在我的项目中解决SynchronousWorkManager...它已经被删除了吗? - drmrbrewer
是的,自10月11日以来似乎已经消失了。您可以使用ListenableFuture代替。 - David Sucharda
由于 SynchronousWorkManager 已被删除,所以此答案已过时,您可以考虑将其删除。 - Ridcully
@Ridcully 删除了过时的信息,并添加了有关ListenableWorker的更多信息。 - David Sucharda

2

我在这篇文章中找到了答案并做了一些编辑。

在我的项目中,我每隔15分钟向服务器发送位置信息。如果已经安排了WorkManager,我不想再次安排。

// Fırst I'm creating workRequest here.
   private void createWorkRequest() {
        Constraints constraints = new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build();
        PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest.Builder
                (LocationWorker.class, 15, TimeUnit.MINUTES)
                .setConstraints(constraints)
                .build();
        WorkManager.getInstance(this)
                .enqueueUniquePeriodicWork("sendLocation", ExistingPeriodicWorkPolicy.REPLACE, periodicWorkRequest);
    }

// Then i'm learning the state of Work
    private WorkInfo.State getStateOfWork() {
        try {
            if (WorkManager.getInstance(this).getWorkInfosForUniqueWork("sendLocation").get().size() > 0) {
                return WorkManager.getInstance(this).getWorkInfosForUniqueWork("sendLocation")
                        .get().get(0).getState();
                // this can return WorkInfo.State.ENQUEUED or WorkInfo.State.RUNNING
                // you can check all of them in WorkInfo class.
            } else {
                return WorkInfo.State.CANCELLED;
            }
        } catch (ExecutionException e) {
            e.printStackTrace();
            return WorkInfo.State.CANCELLED;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return WorkInfo.State.CANCELLED;
        }
    }

// If work not ( ENQUEUED and RUNNING ) i'm running the work.
// You can check with other ways. It's up to you.
    private void startServerWork() {
        if (getStateOfWork() != WorkInfo.State.ENQUEUED && getStateOfWork() != WorkInfo.State.RUNNING) {
            createWorkRequest();
            Log.wtf("startLocationUpdates", ": server started");
        } else {
            Log.wtf("startLocationUpdates", ": server already working");
        }
    }

1
我认为我们应该通过UniqueWorkName检查所有六个WorkRequest的状态。如果工作请求处于任何一种状态,我们应该标记它已经被安排了。您也可以根据业务需求删除其中几个状态。请注意保留HTML标签。
private fun isWorkEverScheduledBefore(context: Context, tag: String): Boolean {
    val instance = WorkManager.getInstance(context)
    val statuses: ListenableFuture<List<WorkInfo>> = instance.getWorkInfosForUniqueWork(tag)
    var workScheduled = false
    statuses.get()?.let {
        for (workStatus in it) {
            workScheduled = (
                        workStatus.state == WorkInfo.State.ENQUEUED
                                || workStatus.state == WorkInfo.State.RUNNING
                                || workStatus.state == WorkInfo.State.BLOCKED
                                || workStatus.state.isFinished // It checks SUCCEEDED, FAILED, CANCELLED already
                    )
        }
    }
    return workScheduled
}

0
如果工作管理器已被安排。
private val _workManagerObserver = WorkManager.getInstance(application)
    .getWorkInfosForUniqueWorkLiveData(TAG)
    .map { it.lastOrNull() }
    .map { it?.state?.isFinished != true }
    .map {
        Timber.d("_workManagerObserver $it")
        it
    }
    .asFlow()

如果工作管理器正在运行
private val _workManagerObserver = WorkManager.getInstance(application)
    .getWorkInfosForUniqueWorkLiveData(TAG)
    .map { it.lastOrNull() }
    .map { it?.state == WorkInfo.State.RUNNING }
    .map {
        Timber.d("_workManagerObserver $it")
        it
    }
    .asFlow()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接