Add a WorkContinuation.join(...) which adds the ability to do more advanced continuations.
Test: Added tests for the new join() APIs.
Change-Id: I06e7ffbae7518d6a79ba1e5c24185a542a2b7c0e
diff --git a/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/WorkContinuationImplTest.java b/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/WorkContinuationImplTest.java
index f0c3a22..81387c9 100644
--- a/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/WorkContinuationImplTest.java
+++ b/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/WorkContinuationImplTest.java
@@ -27,6 +27,7 @@
import android.arch.background.workmanager.TestLifecycleOwner;
import android.arch.background.workmanager.Work;
+import android.arch.background.workmanager.WorkContinuation;
import android.arch.background.workmanager.executors.SynchronousExecutorService;
import android.arch.background.workmanager.impl.utils.taskexecutor.InstantTaskExecutorRule;
import android.arch.background.workmanager.worker.TestWorker;
@@ -106,9 +107,9 @@
WorkContinuationImpl continuation =
new WorkContinuationImpl(mWorkManagerImpl, testWork);
- assertThat(continuation.getParent(), is(nullValue()));
- assertThat(continuation.getIds().length, is(1));
- assertThat(continuation.getIds()[0], is(testWork.getId()));
+ assertThat(continuation.getParents(), is(nullValue()));
+ assertThat(continuation.getIds().size(), is(1));
+ assertThat(continuation.getIds().get(0), is(testWork.getId()));
assertThat(continuation.getAllIds().size(), is(1));
}
@@ -121,10 +122,9 @@
WorkContinuationImpl dependent = (WorkContinuationImpl) (continuation.then(
dependentWork));
- assertThat(dependent.getParent(), is(notNullValue()));
- assertThat(dependent.getParent(), is(continuation));
- assertThat(dependent.getIds().length, is(1));
- assertThat(dependent.getIds()[0], is(dependentWork.getId()));
+ assertThat(dependent.getParents(), containsInAnyOrder(continuation));
+ assertThat(dependent.getIds().size(), is(1));
+ assertThat(dependent.getIds().get(0), is(dependentWork.getId()));
assertThat(dependent.getAllIds().size(), is(2));
assertThat(
dependent.getAllIds(),
@@ -167,12 +167,65 @@
verify(spy, times(0)).markEnqueued();
}
+ @Test
+ public void testContinuation_join() {
+ WorkContinuationImpl first = new WorkContinuationImpl(mWorkManagerImpl,
+ createTestWorker());
+ WorkContinuationImpl second = new WorkContinuationImpl(mWorkManagerImpl,
+ createTestWorker());
+ WorkContinuationImpl dependent = (WorkContinuationImpl) WorkContinuation.join(first,
+ second);
+ assertThat(dependent.getParents(), is(notNullValue()));
+ assertThat(dependent.getParents(), containsInAnyOrder(first, second));
+ }
+
+ @Test
+ public void testContinuation_joinAndEnqueue() {
+ WorkContinuationImpl first = new WorkContinuationImpl(mWorkManagerImpl,
+ createTestWorker());
+ WorkContinuationImpl second = new WorkContinuationImpl(mWorkManagerImpl,
+ createTestWorker());
+
+ WorkContinuationImpl third = new WorkContinuationImpl(mWorkManagerImpl,
+ createTestWorker());
+ WorkContinuationImpl fourth = new WorkContinuationImpl(mWorkManagerImpl,
+ createTestWorker());
+
+ WorkContinuationImpl firstDependent = (WorkContinuationImpl) WorkContinuation.join(first,
+ second);
+ WorkContinuationImpl secondDependent = (WorkContinuationImpl) WorkContinuation.join(third,
+ fourth);
+ WorkContinuationImpl dependent = (WorkContinuationImpl) WorkContinuation.join(
+ firstDependent, secondDependent);
+ dependent.enqueue();
+ verifyEnqueued(dependent);
+ }
+
+ @Test
+ public void testContinuation_joinAndEnqueueWithOverlaps() {
+ WorkContinuationImpl first = new WorkContinuationImpl(mWorkManagerImpl,
+ createTestWorker());
+ WorkContinuationImpl second = new WorkContinuationImpl(mWorkManagerImpl,
+ createTestWorker());
+ WorkContinuationImpl third = new WorkContinuationImpl(mWorkManagerImpl,
+ createTestWorker());
+ WorkContinuationImpl firstDependent = (WorkContinuationImpl) WorkContinuation.join(first,
+ second);
+ WorkContinuationImpl secondDependent = (WorkContinuationImpl) WorkContinuation.join(first,
+ third);
+ WorkContinuationImpl dependent = (WorkContinuationImpl) WorkContinuation.join(
+ firstDependent, secondDependent);
+ dependent.enqueue();
+ verifyEnqueued(dependent);
+ }
+
private void verifyEnqueued(WorkContinuationImpl continuation) {
assertThat(continuation.isEnqueued(), is(true));
- WorkContinuationImpl parent = continuation.getParent();
- while (parent != null) {
- assertThat(parent.isEnqueued(), is(true));
- parent = parent.getParent();
+ List<WorkContinuationImpl> parents = continuation.getParents();
+ if (parents != null) {
+ for (WorkContinuationImpl parent : parents) {
+ verifyEnqueued(parent);
+ }
}
}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/WorkContinuation.java b/background/workmanager/src/main/java/android/arch/background/workmanager/WorkContinuation.java
index 958e07d..3ac8624 100644
--- a/background/workmanager/src/main/java/android/arch/background/workmanager/WorkContinuation.java
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/WorkContinuation.java
@@ -16,6 +16,7 @@
package android.arch.background.workmanager;
import android.arch.lifecycle.LiveData;
+import android.support.annotation.NonNull;
import java.util.Arrays;
import java.util.List;
@@ -65,5 +66,23 @@
*/
public abstract void enqueue();
+ /**
+ * Joins multiple {@link WorkContinuation}s to allow for complex chaining.
+ *
+ * @param continuations Two or more {@link WorkContinuation}s that are prerequisites for the
+ * return value
+ * @return A {@link WorkContinuation} that allows further chaining
+ */
+ public static WorkContinuation join(@NonNull WorkContinuation... continuations) {
+ if (continuations.length < 2) {
+ throw new IllegalArgumentException(
+ "WorkContinuation.join() needs at least 2 continuations.");
+ }
+
+ return continuations[0].joinInternal(continuations);
+ }
+
protected abstract WorkContinuation then(List<Class<? extends Worker>> workerClasses);
+
+ protected abstract WorkContinuation joinInternal(@NonNull WorkContinuation... continuations);
}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/impl/WorkContinuationImpl.java b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/WorkContinuationImpl.java
index 3183532..f3b8525 100644
--- a/background/workmanager/src/main/java/android/arch/background/workmanager/impl/WorkContinuationImpl.java
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/WorkContinuationImpl.java
@@ -23,6 +23,7 @@
import android.arch.background.workmanager.Worker;
import android.arch.background.workmanager.impl.utils.BaseWorkHelper;
import android.arch.background.workmanager.impl.utils.EnqueueRunnable;
+import android.arch.background.workmanager.impl.workers.JoinWorker;
import android.arch.lifecycle.LiveData;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -31,6 +32,7 @@
import android.util.Log;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -48,10 +50,10 @@
private final String mUniqueTag;
private final @WorkManager.ExistingWorkPolicy int mExistingWorkPolicy;
private final BaseWork[] mWork;
- private final String[] mIds;
+ private final List<String> mIds;
private final List<String> mAllIds;
+ private final List<WorkContinuationImpl> mParents;
private boolean mEnqueued;
- private WorkContinuationImpl mParent;
@NonNull
public WorkManagerImpl getWorkManagerImpl() {
@@ -73,7 +75,7 @@
}
@NonNull
- public String[] getIds() {
+ public List<String> getIds() {
return mIds;
}
@@ -92,40 +94,42 @@
mEnqueued = true;
}
- public WorkContinuationImpl getParent() {
- return mParent;
+ public List<WorkContinuationImpl> getParents() {
+ return mParents;
}
- WorkContinuationImpl(@NonNull WorkManagerImpl workManagerImpl, @NonNull BaseWork... work) {
+ WorkContinuationImpl(
+ @NonNull WorkManagerImpl workManagerImpl,
+ @NonNull BaseWork... work) {
this(workManagerImpl, null, WorkManager.KEEP_EXISTING_WORK, work, null);
}
WorkContinuationImpl(@NonNull WorkManagerImpl workManagerImpl,
- @NonNull String uniqueTag,
+ String uniqueTag,
@WorkManager.ExistingWorkPolicy int existingWorkPolicy,
@NonNull BaseWork... work) {
this(workManagerImpl, uniqueTag, existingWorkPolicy, work, null);
}
- private WorkContinuationImpl(@NonNull WorkManagerImpl workManagerImpl,
+ WorkContinuationImpl(@NonNull WorkManagerImpl workManagerImpl,
String uniqueTag,
@WorkManager.ExistingWorkPolicy int existingWorkPolicy,
@NonNull BaseWork[] work,
- @Nullable WorkContinuationImpl parent) {
+ @Nullable List<WorkContinuationImpl> parents) {
mWorkManagerImpl = workManagerImpl;
mUniqueTag = uniqueTag;
mExistingWorkPolicy = existingWorkPolicy;
mWork = work;
- mParent = parent;
-
- final int parentSize = mParent != null ? mParent.mAllIds.size() : 0;
- mIds = new String[mWork.length];
- mAllIds = new ArrayList<>(mWork.length + parentSize);
- if (parent != null) {
- mAllIds.addAll(mParent.mAllIds);
+ mParents = parents;
+ mIds = new ArrayList<>(mWork.length);
+ mAllIds = new ArrayList<>();
+ if (parents != null) {
+ for (WorkContinuationImpl parent : parents) {
+ mAllIds.addAll(parent.mAllIds);
+ }
}
for (int i = 0; i < work.length; i++) {
- mIds[i] = work[i].getId();
+ mIds.add(work[i].getId());
mAllIds.add(work[i].getId());
}
}
@@ -138,7 +142,7 @@
mUniqueTag,
WorkManager.KEEP_EXISTING_WORK,
work,
- this);
+ Collections.singletonList(this));
}
@Override
@@ -147,7 +151,7 @@
mUniqueTag,
WorkManager.KEEP_EXISTING_WORK,
BaseWorkHelper.convertWorkerClassListToWorkArray(workerClasses),
- this);
+ Collections.singletonList(this));
}
@Override
@@ -168,4 +172,19 @@
String.format("Already enqueued work ids (%s).", TextUtils.join(", ", mIds)));
}
}
+
+ @Override
+ protected WorkContinuation joinInternal(@NonNull WorkContinuation... continuations) {
+ List<WorkContinuationImpl> parents = new ArrayList<>(continuations.length);
+ for (WorkContinuation continuation : continuations) {
+ parents.add((WorkContinuationImpl) continuation);
+ }
+
+ Work work = Work.newBuilder(JoinWorker.class).build();
+ return new WorkContinuationImpl(mWorkManagerImpl,
+ null,
+ WorkManager.KEEP_EXISTING_WORK,
+ new BaseWork[]{work},
+ parents);
+ }
}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/impl/utils/EnqueueRunnable.java b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/utils/EnqueueRunnable.java
index bb14f31..e1c0541 100644
--- a/background/workmanager/src/main/java/android/arch/background/workmanager/impl/utils/EnqueueRunnable.java
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/utils/EnqueueRunnable.java
@@ -36,7 +36,9 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* Manages the enqueuing of a {@link WorkContinuationImpl}.
@@ -96,19 +98,20 @@
@NonNull WorkContinuationImpl workContinuation,
@NonNull List<InternalWorkImpl> workToBeScheduled) {
- WorkContinuationImpl parent = workContinuation.getParent();
- if (parent != null) {
- // When chaining off a completed continuation we need to pay
- // attention to parents that may have been marked as enqueued before.
- if (!parent.isEnqueued()) {
- processContinuation(parent, workToBeScheduled);
- } else {
- Log.w(TAG,
- String.format(
- "Already enqueued work ids (%s).",
- TextUtils.join(", ", parent.getIds())));
+ List<WorkContinuationImpl> parents = workContinuation.getParents();
+ if (parents != null) {
+ for (WorkContinuationImpl parent : parents) {
+ // When chaining off a completed continuation we need to pay
+ // attention to parents that may have been marked as enqueued before.
+ if (!parent.isEnqueued()) {
+ processContinuation(parent, workToBeScheduled);
+ } else {
+ Log.w(TAG,
+ String.format(
+ "Already enqueued work ids (%s).",
+ TextUtils.join(", ", parent.getIds())));
+ }
}
-
}
enqueueContinuation(workContinuation, workToBeScheduled);
}
@@ -117,13 +120,18 @@
@NonNull WorkContinuationImpl workContinuation,
@NonNull List<InternalWorkImpl> workToBeScheduled) {
- WorkContinuationImpl parent = workContinuation.getParent();
- String[] prerequisiteIds = parent != null ? parent.getIds() : null;
+ List<WorkContinuationImpl> parents = workContinuation.getParents();
+ Set<String> prerequisiteIds = new HashSet<>();
+ if (parents != null) {
+ for (WorkContinuationImpl parent : parents) {
+ prerequisiteIds.addAll(parent.getIds());
+ }
+ }
enqueueWorkWithPrerequisites(
workContinuation.getWorkManagerImpl(),
workContinuation.getWork(),
- prerequisiteIds,
+ prerequisiteIds.toArray(new String[0]),
workContinuation.getUniqueTag(),
workContinuation.getExistingWorkPolicy(),
workToBeScheduled);
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/impl/workers/JoinWorker.java b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/workers/JoinWorker.java
new file mode 100644
index 0000000..a8038eb
--- /dev/null
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/workers/JoinWorker.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.background.workmanager.impl.workers;
+
+import android.arch.background.workmanager.Worker;
+import android.support.annotation.RestrictTo;
+
+/**
+ * Is a implementation of the {@link android.arch.background.workmanager.Worker} that helps join
+ * work continuations.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class JoinWorker extends Worker {
+ @Override
+ public int doWork() {
+ return WORKER_RESULT_SUCCESS;
+ }
+}