Workmanager 中唯一的 OneTimeWorkRequest
Posted
技术标签:
【中文标题】Workmanager 中唯一的 OneTimeWorkRequest【英文标题】:Unique OneTimeWorkRequest in Workmanager 【发布时间】:2019-01-14 10:29:20 【问题描述】:我们正在使用 OneTimeWorkRequest 在我们的项目中启动后台任务。
-
在应用程序启动时,我们正在启动 OneTimeWorkRequest(比如 req A)
取决于用户的操作,我们启动相同的工作请求 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 以使唯一队列入队 OneTimeWorkRequests 无需创建 WorkContinuation。 https://developer.android.com/jetpack/docs/release-notes
【问题讨论】:
你为什么要重新运行任务,你不应该开火然后忘记吗? 不,OneTimeWorkRequest 只是一次性触发。我们不会定期运行此工作器。我在应用程序重新启动期间运行此工作程序,因为应用程序不知道应用程序没有运行多少时间。该工作人员用于与电话簿内容同步。 Check if WorkManager is scheduled already的可能重复 @Khemraj 我的问题是关于如何唯一地启动 OneTimeWorkRequest()。这包含在较新的版本中。请检查我的编辑。 【参考方案1】:编辑 2:
11 月 8 日发布说明:
https://developer.android.com/jetpack/docs/release-notes
添加 WorkManager.enqueueUniqueWork() API 以使唯一队列入队 OneTimeWorkRequests,而无需创建 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 对象并检查它们中是否至少有一个处于运行或入队状态。由于系统将所有已完成的工作都保存在数据库中几天(参考 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);
【讨论】:
你好,感谢更新,我对更新的代码没有什么困惑,如果我使用enqueueUniqueWork
,如果之前的工作已经在运行,它是否会添加一个新的工作,即它是否检查@987654326 @ 就像您在代码中所做的那样。谢谢:)【参考方案2】:
您可以使用具有唯一名称的beginUniqueWork()
。
如果您使用ExistingWorkPolicy:
APPEND:这 2 个请求将串行运行。
KEEP:如果第一个请求正在运行,则不会运行第二个请求。
REPLACE:2 个请求将并行运行。
【讨论】:
好的,这是我的确切问题。如果我调用 beginUniqueWork(),它会返回延续对象。如果我只有一个工作请求,那会更贵吗?我的意思是我没有一连串的工作请求。我们只有一个工作请求。 REPLACE:如果存在具有相同唯一名称的待处理(未完成)工作,请取消并删除它。然后,插入新指定的作品。【参考方案3】:使用 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 的返回值,如果它是真的,那么启动它是安全的,如果不是这意味着另一个具有相同标签的任务正在运行
【讨论】:
【参考方案4】:由于所有答案大多都已过时,您可以像这样监听带标签的工人的变化:
LiveData<List<WorkInfo>> workInfosByTag = WorkManager.getInstance().getWorkInfosByTagLiveData(tag);
workInfosByTag.observeForever(workInfos ->
for (WorkInfo workInfo : workInfos)
workInfo.toString();
);
【讨论】:
以上是关于Workmanager 中唯一的 OneTimeWorkRequest的主要内容,如果未能解决你的问题,请参考以下文章