Workmanager 中唯一的 OneTimeWorkRequest
Unique OneTimeWorkRequest in Workmanager
我们正在使用 OneTimeWorkRequest 在我们的项目中启动后台任务。
- 在应用程序启动时,我们将启动 OneTimeWorkRequest(比如请求 A)
- 根据用户的操作,我们启动相同的工作请求 A。
在某些情况下,如果应用程序在工作请求 A 正在进行时被终止,Android 会在应用程序重新启动时自动重新启动请求 A。我们也再次开始请求 A。因此请求 A 的两个实例并行运行并导致死锁。
为了避免这种情况,我在应用程序开始时做了以下代码来检查工作人员是否 运行 但这总是 returns 错误。
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。这就是他们在发行说明中提到的确切原因。
Add WorkManager.enqueueUniqueWork() API to enqueue unique
OneTimeWorkRequests without having to create a WorkContinuation.
https://developer.android.com/jetpack/docs/release-notes
您可以使用具有唯一名称的 beginUniqueWork()
。
如果你使用 ExistingWorkPolicy:
附加:2 个请求将 运行 串行。
KEEP:如果第一个请求是 运行ning,则不会 运行 第二个请求。
替换:2 个请求将 运行 并行。
使用 getStatusesByTag
returns List<WorkStatus>
的 LiveData
它被制作为 LiveData,因为 WorkStatus 保存在 Room DB 中,WorkManger 必须首先在后台线程上查询它,然后传递结果。
所以你必须在它可用时观察以获得真正的价值。
调用 getValue()
将 return 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 的 returning 值,如果它是真的那么启动它是安全的,如果不是这意味着另一个具有相同标签的任务是 运行
编辑 2:
11 月 8 日发行说明:
https://developer.android.com/jetpack/docs/release-notes
Add WorkManager.enqueueUniqueWork() API to enqueue unique
OneTimeWorkRequests without having to create a WorkContinuation.
这表示,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 对象并检查其中是否至少有一个处于 运行ning 或 Enqueued 状态。由于系统将所有已完成的作品保存在数据库中几天(参考p运行eWork()),我们需要检查所有的作品实例。
在启动 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);
}
由于大部分答案都已过时,您可以像这样监听标记工人的变化:
LiveData<List<WorkInfo>> workInfosByTag = WorkManager.getInstance().getWorkInfosByTagLiveData(tag);
workInfosByTag.observeForever(workInfos -> {
for (WorkInfo workInfo : workInfos) {
workInfo.toString();
}
});
我们正在使用 OneTimeWorkRequest 在我们的项目中启动后台任务。
- 在应用程序启动时,我们将启动 OneTimeWorkRequest(比如请求 A)
- 根据用户的操作,我们启动相同的工作请求 A。
在某些情况下,如果应用程序在工作请求 A 正在进行时被终止,Android 会在应用程序重新启动时自动重新启动请求 A。我们也再次开始请求 A。因此请求 A 的两个实例并行运行并导致死锁。
为了避免这种情况,我在应用程序开始时做了以下代码来检查工作人员是否 运行 但这总是 returns 错误。
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。这就是他们在发行说明中提到的确切原因。
Add WorkManager.enqueueUniqueWork() API to enqueue unique OneTimeWorkRequests without having to create a WorkContinuation. https://developer.android.com/jetpack/docs/release-notes
您可以使用具有唯一名称的 beginUniqueWork()
。
如果你使用 ExistingWorkPolicy:
附加:2 个请求将 运行 串行。
KEEP:如果第一个请求是 运行ning,则不会 运行 第二个请求。
替换:2 个请求将 运行 并行。
使用 getStatusesByTag
returns List<WorkStatus>
的 LiveData
它被制作为 LiveData,因为 WorkStatus 保存在 Room DB 中,WorkManger 必须首先在后台线程上查询它,然后传递结果。
所以你必须在它可用时观察以获得真正的价值。
调用 getValue()
将 return 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 的 returning 值,如果它是真的那么启动它是安全的,如果不是这意味着另一个具有相同标签的任务是 运行
编辑 2:
11 月 8 日发行说明:
https://developer.android.com/jetpack/docs/release-notes
Add WorkManager.enqueueUniqueWork() API to enqueue unique OneTimeWorkRequests without having to create a WorkContinuation.
这表示,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 对象并检查其中是否至少有一个处于 运行ning 或 Enqueued 状态。由于系统将所有已完成的作品保存在数据库中几天(参考p运行eWork()),我们需要检查所有的作品实例。
在启动 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);
}
由于大部分答案都已过时,您可以像这样监听标记工人的变化:
LiveData<List<WorkInfo>> workInfosByTag = WorkManager.getInstance().getWorkInfosByTagLiveData(tag);
workInfosByTag.observeForever(workInfos -> {
for (WorkInfo workInfo : workInfos) {
workInfo.toString();
}
});