web-dev-qa-db-ja.com

WorkManagerはREST APIへのGETリクエストをどのようにスケジュールしますか?

WorkManagerのコードラボといくつかの例をここで見てきましたが、コードで見たものはすべて、デバイスでローカルに作業するか、サーバーにアップロードする作業に関連しています。データをダウンロードせず、受信したデータに応答します。開発者ガイドラインでは、「たとえば、アプリはネットワークから新しいリソースを時々ダウンロードする必要があるかもしれない」とさえ言っているので、このタスクに最適だと思いました。私の質問は、WorkManagerが次のシナリオを処理できるかどうか、そうでない場合、それを処理するための適切なツールは何ですか?

  1. 1日に1回実行されるジョブをバックグラウンドでスケジュールする
  2. ジョブは、REST APIからデータフェッチを実行することです(可能な場合は、LiveDataオブジェクトにポストします)。
  3. データが返されたら、ローカルデータよりも新しいことを確認します。
  4. 新しいデータが利用可能であることをユーザーに通知します。

私のワーカークラスは次のようになります。

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

もちろん、LifecycleOwnerになる可能性のあるアクティビティやフラグメントがないため、LiveDataオブジェクトが監視できないという問題があります。しかし、APIからのコールバックを使用して、到着したデータに応答したとしても、私のワーカーはすでにそれが成功し、おそらくコールバックを続行しないことを投稿しているでしょう。だから私はこのアプローチが完全に間違っていることを知っていますが、WorkManagerでデータを取得するためのコードを見ることができません

適切な解決策といくつかのサンプルコードまたはいくつかのリンクを手伝ってください。この種の作業を処理できる場合はWorkManagerを使用し、より適切な場合は何か他のものを使用してください。

15
Arno Schoonbee
  1. 1日に1回実行されるジョブをバックグラウンドでスケジュールする

そのためにPeriodicWorkRequestをキューに入れる必要があるenqueueUniquePeriodicWorkをスケジュールできます。これにより、特定の名前のPeriodicWorkRequestを一度に1つだけアクティブにできます。

_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()内で同期的にリクエストを送信することで実行できます。 LiveDataクラス内ではWorkerを使用しません。それについては後で説明します。たとえば、API呼び出しはRetrofitで次のようになります。

_@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)もあります。

公式ガイドでは、たとえばMutableLiveDatasetValue()を呼び出すことができるタスクからデータを返す方法についても説明しています。

Worker内のローカルデータを更新し、ワーカーステータスをサブスクライブして、ローカルデータでUIを更新することをお勧めします(ローカルデータでサブスクライブしていない場合、つまり、RoomとLiveDataを使用します)。

編集:ポイント4を参照すると、定期的な作業要求のステータスの読み取りは少し異なります。 ENQUEUEDまでRUNNINGCANCELLEDを切り替えるだけです。ただし、SUCCEEDEDまたはFAILEDの状態になることはありません。したがって、isFinished()をリッスンすることは、期待したことではない可能性があります。

13
kphil

これは最初の考えです。私が間違っている場合、誰かが私を訂正してください。

私の労働者はすでにそれが成功し、おそらくコールバックを続行しないだろうと投稿していましたよね?

aPIレスポンスからのコールバックを使用して、ワーカーの出力データを作成し、worker.setOutputData()を使用して設定できます。次に、workManagerから_LiveData<WorkStatus>_をリッスンします。この作業ステータスから、workStatus.getOutputdata()を使用してoutputDataを取得できます。このデータにより、必要なAPI応答が得られます。

この応答をワーカーチェーンの次のワーカーに渡して、ローカルDBの更新などのタスクを実行できます。

0
Sudhasri