WorkManager如何调度发送到REST API的GET请求?

31

我已经查看了WorkManager的Codelab以及这里的一些示例,但我看到的所有代码都与在设备上本地执行工作或上传到服务器有关,而不是下载数据并响应接收到的数据。甚至在开发者指南中也说,“例如,一个应用程序可能需要不时从网络下载新的资源”,因此我认为它非常适合这项任务。我的问题是,如果WorkManager能够处理以下情况,如果不能,那么处理它的正确工具是什么:

  1. 计划在后台每天运行一次作业
  2. 作业是从REST API获取数据(如果可能,将其发布到LiveData对象)。
  3. 当数据返回时,请检查它是否比本地数据更新。
  4. 通知用户新数据可用。

我的Worker类大致如下:

public class MyWorker extends Worker {

@NonNull
@Override
public WorkerResult doWork() {
    lookForNewData();
    return WorkerResult.SUCCESS;
}

public void lookForNewData() {
    MutableLiveData<MyObject> liveData = new MutableLiveData<>();

    liveData.observe(lifeCycleOwner, results -> {
        notifyOnNewData(results);
    })

    APILayer.getInstance().fetchData(searchParams, liveData)
}

我的问题当然是LiveData对象无法观察,因为没有活动或片段可以作为其LifecycleOwner。但是,即使我使用API的回调来响应数据到达,我的工作者也已经发布了它成功完成的消息,并且可能不会继续执行回调,对吧?所以我知道这种方法完全是错误的,但是我看不到任何用WorkManager获取数据的代码。

请提供一个适当的解决方案,一些示例代码或链接,无论是使用WorkManager处理此类工作还是其他更合适的东西。

2个回答

34
  1. 安排一个每天在后台运行一次的任务

您可以使用 PeriodicWorkRequest 安排这个任务,需要使用 enqueueUniquePeriodicWork 进行排队。这可以确保同一名称的 PeriodicWorkRequest 同时只有一个处于活动状态。

Constraints constraint = new Constraints.Builder()
     .setRequiredNetworkType(NetworkType.CONNECTED)
     .build();

PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(MyWorker.class, 1, TimeUnit.DAYS)
     .setConstraints(constraint)
     .build();

WorkManager workManager = WorkManager.getInstance();
workManager.enqueueUniquePeriodicWork("my_unique_worker", ExistingPeriodicWorkPolicy.KEEP, workRequest);
  1. 这份工作的任务是从REST API获取数据(如果可能,将其发布到LiveData对象中)。

这可以通过在工作器的doWork()方法中同步发送请求来完成。我不会在Worker类中使用LiveData,稍后再讨论此事。例如,使用Retrofit进行API调用的代码如下:

@Override
public WorkerResult doWork() {
     Call<MyData> call = APILayer.getInstance().fetchData();
     Response<MyData> response = call.execute();
     if (response.code() == 200) {
          MyData data = response.body();
          // ...
     } else {
          return Result.RETRY;
     }
     // ...
     return Result.SUCCESS;
}
  1. 当数据返回时,请检查其是否比本地数据更新。

您以同步方式获取了API数据。同步获取本地数据,然后进行必要的比较操作。

  1. 通知用户新数据可用。

如果您使用WorkManager调度任务,即使应用程序被强制停止或设备重新启动,也会保证它运行。因此,您的任务可能在应用程序未运行时完成。如果您想在任何情况下通知用户,可以发送通知。如果您想在特定屏幕内通知用户,则可以订阅任务状态。例如(来自官方指南):

WorkManager.getInstance().getStatusById(compressionWork.getId())
.observe(lifecycleOwner, workStatus -> {
    // Do something with the status
    if (workStatus != null && workStatus.getState().isFinished()) {
        // ...
    }
});

对于我们的示例,还有getStatusesForUniqueWork(String uniqueWorkName)

官方指南还解释了如何从您的任务中返回数据,这些数据可以调用setValue()在您的MutableLiveData上,例如。

我建议在您的Worker中更新本地数据,订阅您的工作状态,并在成功后使用本地数据更新您的UI(如果您没有订阅本地数据,例如使用Room和LiveData)。

编辑:关于第4点,周期性工作请求的读取状态有所不同。它们只在CANCELLED之前在ENQUEUEDRUNNING之间切换。但是永远不会出现SUCCEEDEDFAILED状态。因此,监听isFinished()可能不是您期望的内容。


谢谢!在回调函数返回一个响应之后才在doWork()中返回结果是我所缺少的!非常感谢。 - Arno Schoonbee
你好,能否请您查看我的问题:https://stackoverflow.com/questions/73842223/workmanager-and-retrofit-doesnt-work-properly - Nima Khalili

0

这是初步想法。如果我错了,请有经验的人纠正我。

我的工作程序已经发布成功了,它可能不会继续进行回调,对吗?

我们可以使用API响应中的回调函数来构建工作程序的输出数据,并使用worker.setOutputData()设置它。然后监听来自workManager的LiveData<WorkStatus>。从这个workstatus中,我们可以使用workStatus.getOutputdata()获取输出数据。这些数据可以给我们想要的API响应。

我们可以将此响应传递给工作程序链中的下一个工作程序,以执行更新本地数据库等任务。


我刚刚实现了这个功能,它似乎正在工作,我也会将其作为一个示例参考:https://github.com/googlecodelabs/android-workmanager/blob/master/app/src/main/java/com/example/background/BlurViewModel.java - Josh
@Josh,网址链接已经失效了兄弟。 - CrazyMind
@CrazyMind 他们把Java代码改成了Kotlin。新的URL为[https://github.com/googlecodelabs/android-workmanager/blob/master/app/src/main/java/com/example/background/BlurViewModel.kt][1]。 - Ved

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