web-dev-qa-db-ja.com

Workmanagerの一意のOneTimeWorkRequest

OneTimeWorkRequestを使用して、プロジェクトのバックグラウンドタスクを開始しています。

  1. アプリケーションの開始時に、OneTimeWorkRequestを開始しています(req Aと言います)
  2. ユーザーのアクションに応じて、同じ作業要求Aを開始します。

場合によっては、作業リクエストAの進行中にアプリが強制終了された場合、Androidアプリの再起動時にリクエストAが自動的に再起動されます。もう一度リクエストAを再起動します。リクエストAの2つのインスタンスが並行して実行され、デッドロックが発生します。

これを避けるために、以下のアプリ開始コードでワーカーが実行されているかどうかを確認しましたが、これは常にfalseを返します。

public static boolean isMyWorkerRunning(String tag) {
        List<WorkStatus> status = WorkManager.getInstance().getStatusesByTag(tag).getValue();
        return status != null;
    }

これを処理するより良い方法はありますか?

BeginUniqueWork()を確認しました。リクエストが1つしかない場合、費用がかかりますか?

編集2:この質問は、ユニークなワンタイムタスクに関するものです。一意の定期タスクを開始するために、別個のAPI enqueueUniquePeriodicWork()がありました。しかし、ユニークなワンタイム作業を開始するためのAPIがありませんでした。継続オブジェクト間で使用するのか、アプローチを手動で確認して開始するのか混乱しています。

最近のビルドでは、AndroidこのenqueueUniqueWork()に新しいapiを追加しました。これが、リリースノートで言及した正確な理由です。

WorkManager.enqueueUniqueWork()APIを追加して、WorkContinuationを作成せずに一意のOneTimeWorkRequestsをキューに入れます。 https://developer.Android.com/jetpack/docs/release-notes

9
Aram

編集2:

11月8日リリースノート:

https://developer.Android.com/jetpack/docs/release-notes

WorkManager.enqueueUniqueWork()APIを追加して、WorkContinuationを作成せずに一意のOneTimeWorkRequestsをキューに入れます。

つまり、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オブジェクトを取得し、少なくとも1つが実行中またはエンキュー状態にあるかどうかを確認する必要があります。システムは完了したすべての作業をDBに数日間保持するため(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);
}
6
Aram

beginUniqueWork()を一意の名前で使用できます。
ExistingWorkPolicy を使用する場合:
APPEND:2つのリクエストはシリアルで実行されます。
KEEP:最初の要求が実行されている場合、2番目の要求は実行されません。
REPLACE:2つのリクエストは並行して実行されます。

3
NickF

getStatusesByTagを使用すると、List<WorkStatus>_のLiveDataが返されます。これは、WorkStatusがルームDBに保持され、WorkMangerが最初にバックグラウンドスレッドでクエリしてから結果を配信するため、LiveDataとして作成されましたそのため、実際の値が利用可能になったときに取得するために注意する必要があります。 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の戻り値を確認するまでタスクを開始しません。trueの場合は、同じタグを持つ別のタスクが実行されていることを意味します。

1
Omar El Hefny