Add a GreedyScheduler.
GreedyScheduler optimistically starts executing unconstrained
non-timed work in the Processor. This is the replacement for
ForegroundProcessor that starts work without incurring an IPC
delay.
For this change to take effect, there is now a list of
Schedulers maintained by WorkManagerImpl.
Cancelled work id's are now maintained by the Processor
because there is more than one Scheduler available at a time.
The Scheduler interface no longer refers to cancelled id's.
SystemJobService and FirebaseJobService no longer need to
know about their Schedulers.
Test: Updated and ran tests.
Change-Id: Ie9e67bea0e2b12e6077a8230695374f45a156904
diff --git a/background/workmanager-firebase/src/main/java/android/arch/background/workmanager/impl/background/firebase/FirebaseDelayedJobAlarmReceiver.java b/background/workmanager-firebase/src/main/java/android/arch/background/workmanager/impl/background/firebase/FirebaseDelayedJobAlarmReceiver.java
index acf28fd..1697b19 100644
--- a/background/workmanager-firebase/src/main/java/android/arch/background/workmanager/impl/background/firebase/FirebaseDelayedJobAlarmReceiver.java
+++ b/background/workmanager-firebase/src/main/java/android/arch/background/workmanager/impl/background/firebase/FirebaseDelayedJobAlarmReceiver.java
@@ -15,6 +15,7 @@
*/
package android.arch.background.workmanager.impl.background.firebase;
+import android.arch.background.workmanager.impl.Scheduler;
import android.arch.background.workmanager.impl.WorkDatabase;
import android.arch.background.workmanager.impl.WorkManagerImpl;
import android.arch.background.workmanager.impl.logger.Logger;
@@ -36,19 +37,18 @@
@Override
public void onReceive(final Context context, Intent intent) {
- // TODO(xbhatnag): Avoid using getWorkDatabase() from WorkManager
final PendingResult pendingResult = goAsync();
final String workSpecId = intent.getStringExtra(WORKSPEC_ID_KEY);
final WorkManagerImpl workManagerImpl = WorkManagerImpl.getInstance();
- final FirebaseJobScheduler scheduler =
- (FirebaseJobScheduler) workManagerImpl.getBackgroundScheduler();
final WorkDatabase database = workManagerImpl.getWorkDatabase();
new Thread(new Runnable() {
@Override
public void run() {
WorkSpec workSpec = database.workSpecDao().getWorkSpec(workSpecId);
if (workSpec != null) {
- scheduler.scheduleNow(workSpec);
+ for (Scheduler scheduler : workManagerImpl.getSchedulers()) {
+ scheduler.schedule(workSpec);
+ }
} else {
Logger.error(TAG, "WorkSpec not found! Cannot schedule!");
}
diff --git a/background/workmanager-firebase/src/main/java/android/arch/background/workmanager/impl/background/firebase/FirebaseJobScheduler.java b/background/workmanager-firebase/src/main/java/android/arch/background/workmanager/impl/background/firebase/FirebaseJobScheduler.java
index 1726a5b..cf10b99 100644
--- a/background/workmanager-firebase/src/main/java/android/arch/background/workmanager/impl/background/firebase/FirebaseJobScheduler.java
+++ b/background/workmanager-firebase/src/main/java/android/arch/background/workmanager/impl/background/firebase/FirebaseJobScheduler.java
@@ -34,9 +34,6 @@
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
-import java.util.HashSet;
-import java.util.Set;
-
/**
* A class that schedules work using {@link FirebaseJobDispatcher}.
*
@@ -52,7 +49,6 @@
private FirebaseJobConverter mJobConverter;
private IdGenerator mIdGenerator;
private AlarmManager mAlarmManager;
- private Set<String> mCancelledIds;
public FirebaseJobScheduler(Context context) {
mAppContext = context.getApplicationContext();
@@ -63,7 +59,6 @@
}
mDispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(mAppContext));
mJobConverter = new FirebaseJobConverter(mDispatcher);
- mCancelledIds = new HashSet<>();
}
@Override
@@ -79,15 +74,9 @@
@Override
public void cancel(@NonNull String workSpecId) {
- mCancelledIds.add(workSpecId);
mDispatcher.cancel(workSpecId);
}
- @Override
- public boolean isCancelled(@NonNull String workSpecId) {
- return mCancelledIds.contains(workSpecId);
- }
-
void scheduleNow(WorkSpec workSpec) {
Job job = mJobConverter.convert(workSpec);
Logger.debug(TAG, "Scheduling work now, ID: %s", workSpec.getId());
diff --git a/background/workmanager-firebase/src/main/java/android/arch/background/workmanager/impl/background/firebase/FirebaseJobService.java b/background/workmanager-firebase/src/main/java/android/arch/background/workmanager/impl/background/firebase/FirebaseJobService.java
index 2ede916..d9d1b79 100644
--- a/background/workmanager-firebase/src/main/java/android/arch/background/workmanager/impl/background/firebase/FirebaseJobService.java
+++ b/background/workmanager-firebase/src/main/java/android/arch/background/workmanager/impl/background/firebase/FirebaseJobService.java
@@ -17,7 +17,6 @@
package android.arch.background.workmanager.impl.background.firebase;
import android.arch.background.workmanager.impl.ExecutionListener;
-import android.arch.background.workmanager.impl.Scheduler;
import android.arch.background.workmanager.impl.WorkManagerImpl;
import android.arch.background.workmanager.impl.logger.Logger;
import android.support.annotation.NonNull;
@@ -39,7 +38,6 @@
public class FirebaseJobService extends JobService implements ExecutionListener {
private static final String TAG = "FirebaseJobService";
private WorkManagerImpl mWorkManagerImpl;
- private Scheduler mScheduler;
private final Map<String, JobParameters> mJobParameters = new HashMap<>();
@Override
@@ -47,7 +45,6 @@
super.onCreate();
mWorkManagerImpl = WorkManagerImpl.getInstance();
mWorkManagerImpl.getProcessor().addExecutionListener(this);
- mScheduler = mWorkManagerImpl.getBackgroundScheduler();
}
@Override
@@ -86,7 +83,7 @@
mJobParameters.remove(workSpecId);
}
mWorkManagerImpl.stopWork(workSpecId);
- return !mScheduler.isCancelled(workSpecId);
+ return !mWorkManagerImpl.getProcessor().isCancelled(workSpecId);
}
@Override
diff --git a/background/workmanager/src/androidTest/java/android/arch/background/workmanager/executors/SynchronousExecutorService.java b/background/workmanager/src/androidTest/java/android/arch/background/workmanager/executors/SynchronousExecutorService.java
deleted file mode 100644
index 9395f12..0000000
--- a/background/workmanager/src/androidTest/java/android/arch/background/workmanager/executors/SynchronousExecutorService.java
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Copyright (C) 2017 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.executors;
-
-import android.support.annotation.NonNull;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.Delayed;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-/**
- * A synchronous {@link ScheduledExecutorService}.
- */
-public class SynchronousExecutorService implements ScheduledExecutorService {
-
- private boolean mShutdown;
-
- @NonNull
- @Override
- public ScheduledFuture<?> schedule(
- @NonNull Runnable command, long delay, @NonNull TimeUnit unit) {
- return doBlocking(command, delay, unit);
- }
-
- @NonNull
- @Override
- public <V> ScheduledFuture<V> schedule(
- @NonNull Callable<V> callable, long delay, @NonNull TimeUnit unit) {
- try {
- return doBlocking(callable, delay, unit);
- } catch (Exception e) {
- return new SynchronizedScheduledFuture<>();
- }
- }
-
- @NonNull
- @Override
- public ScheduledFuture<?> scheduleAtFixedRate(
- @NonNull Runnable command, long initialDelay, long period, @NonNull TimeUnit unit) {
- throw new UnsupportedOperationException();
- }
-
- @NonNull
- @Override
- public ScheduledFuture<?> scheduleWithFixedDelay(
- @NonNull Runnable command, long initialDelay, long delay, @NonNull TimeUnit unit) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void shutdown() {
- mShutdown = true;
- }
-
- @NonNull
- @Override
- public List<Runnable> shutdownNow() {
- mShutdown = true;
- return new ArrayList<>(0);
- }
-
- @Override
- public boolean isShutdown() {
- return mShutdown;
- }
-
- @Override
- public boolean isTerminated() {
- return mShutdown; // Assume all tasks have completed after shutdown.
- }
-
- @Override
- public boolean awaitTermination(
- long timeout, @NonNull TimeUnit unit) throws InterruptedException {
- return false;
- }
-
- @NonNull
- @Override
- public <T> Future<T> submit(@NonNull Callable<T> task) {
- try {
- return doBlocking(task, 0, TimeUnit.MILLISECONDS);
- } catch (Exception e) {
- return new SynchronizedScheduledFuture<>();
- }
- }
-
- @NonNull
- @Override
- public <T> Future<T> submit(@NonNull Runnable task, T result) {
- return doBlocking(task, 0, TimeUnit.MILLISECONDS);
- }
-
- @NonNull
- @Override
- public Future<?> submit(@NonNull Runnable task) {
- return doBlocking(task, 0, TimeUnit.MILLISECONDS);
- }
-
- @NonNull
- @Override
- public <T> List<Future<T>> invokeAll(@NonNull Collection<? extends Callable<T>> tasks)
- throws InterruptedException {
- throw new UnsupportedOperationException();
- }
-
- @NonNull
- @Override
- public <T> List<Future<T>> invokeAll(
- @NonNull Collection<? extends Callable<T>> tasks, long timeout, @NonNull TimeUnit unit)
- throws InterruptedException {
- throw new UnsupportedOperationException();
- }
-
- @NonNull
- @Override
- public <T> T invokeAny(@NonNull Collection<? extends Callable<T>> tasks)
- throws InterruptedException, ExecutionException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public <T> T invokeAny(
- @NonNull Collection<? extends Callable<T>> tasks, long timeout, @NonNull TimeUnit unit)
- throws InterruptedException, ExecutionException, TimeoutException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void execute(@NonNull Runnable command) {
- doBlocking(command, 0, TimeUnit.MILLISECONDS);
- }
-
- private <T> ScheduledFuture<T> doBlocking(Runnable command, long delay, TimeUnit unit) {
- try {
- long millis = unit.toMillis(delay);
- if (millis > 0L) {
- Thread.sleep(millis);
- }
- } catch (InterruptedException e) {
- // Do nothing.
- }
- command.run();
- return new SynchronizedScheduledFuture<>();
- }
-
- private <T> ScheduledFuture<T> doBlocking(Callable<T> command, long delay, TimeUnit unit)
- throws Exception {
- try {
- long millis = unit.toMillis(delay);
- if (millis > 0L) {
- Thread.sleep(millis);
- }
- } catch (InterruptedException e) {
- // Do nothing.
- }
- command.call();
- return new SynchronizedScheduledFuture<>();
- }
-
- private static class SynchronizedScheduledFuture<V> implements ScheduledFuture<V> {
-
- @Override
- public long getDelay(@NonNull TimeUnit unit) {
- return 0;
- }
-
- @Override
- public int compareTo(@NonNull Delayed o) {
- return 0;
- }
-
- @Override
- public boolean cancel(boolean mayInterruptIfRunning) {
- return false;
- }
-
- @Override
- public boolean isCancelled() {
- return false;
- }
-
- @Override
- public boolean isDone() {
- return true;
- }
-
- @Override
- public V get() throws InterruptedException, ExecutionException {
- return null;
- }
-
- @Override
- public V get(long timeout, @NonNull TimeUnit unit)
- throws InterruptedException, ExecutionException, TimeoutException {
- return null;
- }
- }
-}
diff --git a/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/ProcessorTest.java b/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/ProcessorTest.java
index 04bfcba3..7c355a0 100644
--- a/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/ProcessorTest.java
+++ b/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/ProcessorTest.java
@@ -32,6 +32,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Collections;
import java.util.concurrent.Executors;
@RunWith(AndroidJUnit4.class)
@@ -44,7 +45,7 @@
mProcessor = new Processor(
appContext,
mDatabase,
- mock(Scheduler.class),
+ Collections.singletonList(mock(Scheduler.class)),
Executors.newSingleThreadScheduledExecutor()) {
};
}
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 de23388..f99ceb5 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
@@ -28,7 +28,6 @@
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;
import android.arch.core.executor.ArchTaskExecutor;
@@ -47,6 +46,7 @@
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.Executors;
@RunWith(AndroidJUnit4.class)
@@ -85,7 +85,7 @@
WorkManagerConfiguration configuration = new WorkManagerConfiguration(
context,
true,
- new SynchronousExecutorService());
+ Executors.newSingleThreadExecutor());
mWorkManagerImpl = new WorkManagerImpl(context, configuration);
mDatabase = mWorkManagerImpl.getWorkDatabase();
}
diff --git a/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/WorkManagerImplTest.java b/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/WorkManagerImplTest.java
index 7c5c8c6..59016f6 100644
--- a/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/WorkManagerImplTest.java
+++ b/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/WorkManagerImplTest.java
@@ -53,7 +53,6 @@
import android.arch.background.workmanager.WorkContinuation;
import android.arch.background.workmanager.WorkManagerTest;
import android.arch.background.workmanager.WorkStatus;
-import android.arch.background.workmanager.executors.SynchronousExecutorService;
import android.arch.background.workmanager.impl.model.Dependency;
import android.arch.background.workmanager.impl.model.DependencyDao;
import android.arch.background.workmanager.impl.model.WorkSpec;
@@ -85,6 +84,7 @@
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.Executors;
@RunWith(AndroidJUnit4.class)
public class WorkManagerImplTest extends WorkManagerTest {
@@ -117,7 +117,7 @@
WorkManagerConfiguration configuration = new WorkManagerConfiguration(
context,
true,
- new SynchronousExecutorService());
+ Executors.newSingleThreadExecutor());
mWorkManagerImpl = new WorkManagerImpl(context, configuration);
mDatabase = mWorkManagerImpl.getWorkDatabase();
}
diff --git a/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/WorkerWrapperTest.java b/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/WorkerWrapperTest.java
index b9f375f..666be5b 100644
--- a/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/WorkerWrapperTest.java
+++ b/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/WorkerWrapperTest.java
@@ -63,6 +63,7 @@
import org.mockito.ArgumentCaptor;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;
@@ -240,7 +241,7 @@
new WorkerWrapper.Builder(mContext, mDatabase, prerequisiteWork.getId())
.withListener(mMockListener)
- .withScheduler(mMockScheduler)
+ .withSchedulers(Collections.singletonList(mMockScheduler))
.build()
.run();
@@ -344,7 +345,7 @@
new WorkerWrapper.Builder(mContext, mDatabase, prerequisiteWork.getId())
.withListener(mMockListener)
- .withScheduler(mMockScheduler)
+ .withSchedulers(Collections.singletonList(mMockScheduler))
.build()
.run();
@@ -469,7 +470,7 @@
Scheduler mockScheduler = mock(Scheduler.class);
new WorkerWrapper.Builder(mContext, mDatabase, work.getId())
- .withScheduler(mockScheduler)
+ .withSchedulers(Collections.singletonList(mockScheduler))
.build()
.run();
diff --git a/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/background/systemalarm/SystemAlarmDispatcherTest.java b/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/background/systemalarm/SystemAlarmDispatcherTest.java
index 6c51ed8..b9b2867 100644
--- a/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/background/systemalarm/SystemAlarmDispatcherTest.java
+++ b/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/background/systemalarm/SystemAlarmDispatcherTest.java
@@ -55,6 +55,7 @@
import org.junit.runner.RunWith;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -103,7 +104,7 @@
mProcessor = new Processor(
mContext,
mDatabase,
- mScheduler,
+ Collections.singletonList(mScheduler),
// simulate real world use-case
Executors.newSingleThreadExecutor());
mSpyProcessor = spy(mProcessor);
diff --git a/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/utils/EnqueueRunnableTest.java b/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/utils/EnqueueRunnableTest.java
index 0789e14..8a2f722 100644
--- a/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/utils/EnqueueRunnableTest.java
+++ b/background/workmanager/src/androidTest/java/android/arch/background/workmanager/impl/utils/EnqueueRunnableTest.java
@@ -45,6 +45,7 @@
public void testScheduleWorkInBackground_isCalled() {
EnqueueRunnable runnable = spy(new EnqueueRunnable(mWorkContinuation));
doNothing().when(runnable).addToDatabase();
+ doNothing().when(runnable).scheduleWorkInBackground();
runnable.run();
verify(runnable, times(1)).scheduleWorkInBackground();
}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/impl/Processor.java b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/Processor.java
index b9d14f1c..c86d8e1 100644
--- a/background/workmanager/src/main/java/android/arch/background/workmanager/impl/Processor.java
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/Processor.java
@@ -22,8 +22,10 @@
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
@@ -35,25 +37,29 @@
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class Processor implements ExecutionListener {
private static final String TAG = "Processor";
- protected Context mAppContext;
- protected WorkDatabase mWorkDatabase;
- protected Map<String, Future<?>> mEnqueuedWorkMap;
- protected Scheduler mScheduler;
- protected ExecutorService mExecutorService;
+ private Context mAppContext;
+ private WorkDatabase mWorkDatabase;
+
+ private Map<String, Future<?>> mEnqueuedWorkMap;
+ private List<Scheduler> mSchedulers;
+ private ExecutorService mExecutorService;
+
+ private Set<String> mCancelledIds;
private final List<ExecutionListener> mOuterListeners;
public Processor(
Context appContext,
WorkDatabase workDatabase,
- Scheduler scheduler,
+ List<Scheduler> schedulers,
ExecutorService executorService) {
mAppContext = appContext;
mWorkDatabase = workDatabase;
mEnqueuedWorkMap = new HashMap<>();
- mScheduler = scheduler;
+ mSchedulers = schedulers;
mExecutorService = executorService;
+ mCancelledIds = new HashSet<>();
mOuterListeners = new ArrayList<>();
}
@@ -73,7 +79,7 @@
WorkerWrapper workWrapper = new WorkerWrapper.Builder(mAppContext, mWorkDatabase, id)
.withListener(this)
- .withScheduler(mScheduler)
+ .withSchedulers(mSchedulers)
.build();
mEnqueuedWorkMap.put(id, mExecutorService.submit(workWrapper));
Logger.debug(TAG, "%s: processing %s", getClass().getSimpleName(), id);
@@ -83,10 +89,10 @@
/**
* Tries to stop a unit of work.
*
- * @param id The work id to stop.
+ * @param id The work id to stop
* @param mayInterruptIfRunning If {@code true}, we try to interrupt the {@link Future} if it's
* running
- * @return {@code true} if the work was stopped successfully.
+ * @return {@code true} if the work was stopped successfully
*/
public synchronized boolean stopWork(String id, boolean mayInterruptIfRunning) {
Logger.debug(TAG,
@@ -112,6 +118,26 @@
}
/**
+ * Sets the given {@code id} as cancelled. This does not actually stop any processing; call
+ * {@link #stopWork(String, boolean)} to do that.
+ *
+ * @param id The work id to mark as cancelled
+ */
+ public synchronized void setCancelled(String id) {
+ mCancelledIds.add(id);
+ }
+
+ /**
+ * Determines if the given {@code id} is marked as cancelled.
+ *
+ * @param id The work id to query
+ * @return {@code true} if the id has already been marked as cancelled
+ */
+ public synchronized boolean isCancelled(String id) {
+ return mCancelledIds.contains(id);
+ }
+
+ /**
* @return {@code true} if the processor has work to process.
*/
public synchronized boolean hasWork() {
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/impl/Scheduler.java b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/Scheduler.java
index ce4d181..45e98a6 100644
--- a/background/workmanager/src/main/java/android/arch/background/workmanager/impl/Scheduler.java
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/Scheduler.java
@@ -41,13 +41,4 @@
* @param workSpecId The id of the work to stopWork
*/
void cancel(@NonNull String workSpecId);
-
- /**
- * Determines if the given {@link WorkSpec} id has already been cancelled.
- *
- * @param workSpecId The id of the work to check
- * @return {@code true} if the work has been cancelled through {@link #cancel(String)};
- * {@code false} otherwise
- */
- boolean isCancelled(@NonNull String workSpecId);
}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/impl/WorkManagerImpl.java b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/WorkManagerImpl.java
index 98570a7..6d8fe52 100644
--- a/background/workmanager/src/main/java/android/arch/background/workmanager/impl/WorkManagerImpl.java
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/WorkManagerImpl.java
@@ -22,6 +22,7 @@
import android.arch.background.workmanager.WorkContinuation;
import android.arch.background.workmanager.WorkManager;
import android.arch.background.workmanager.WorkStatus;
+import android.arch.background.workmanager.impl.background.greedy.GreedyScheduler;
import android.arch.background.workmanager.impl.model.WorkSpec;
import android.arch.background.workmanager.impl.model.WorkSpecDao;
import android.arch.background.workmanager.impl.utils.CancelWorkRunnable;
@@ -57,10 +58,9 @@
public static final int MAX_PRE_JOB_SCHEDULER_API_LEVEL = 23;
public static final int MIN_JOB_SCHEDULER_API_LEVEL = 24;
- private WorkManagerConfiguration mConfiguration;
private WorkDatabase mWorkDatabase;
private TaskExecutor mTaskExecutor;
- private Scheduler mBackgroundScheduler;
+ private List<Scheduler> mSchedulers;
private Processor mProcessor;
private static WorkManagerImpl sInstance = null;
@@ -86,15 +86,17 @@
}
WorkManagerImpl(Context context, WorkManagerConfiguration configuration) {
- // TODO(janclarin): Remove context parameter.
- mConfiguration = configuration;
mWorkDatabase = configuration.getWorkDatabase();
- mBackgroundScheduler = configuration.getBackgroundScheduler();
+
+ mSchedulers = new ArrayList<>();
+ mSchedulers.add(configuration.getBackgroundScheduler());
+ mSchedulers.add(new GreedyScheduler(this));
+
mTaskExecutor = WorkManagerTaskExecutor.getInstance();
mProcessor = new Processor(
context.getApplicationContext(),
mWorkDatabase,
- mBackgroundScheduler,
+ mSchedulers,
configuration.getExecutorService());
}
@@ -108,13 +110,13 @@
}
/**
- * @return The {@link Scheduler} associated with this WorkManager based on the device's
+ * @return The {@link Scheduler}s associated with this WorkManager based on the device's
* capabilities, SDK version, etc.
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public @NonNull Scheduler getBackgroundScheduler() {
- return mBackgroundScheduler;
+ public @NonNull List<Scheduler> getSchedulers() {
+ return mSchedulers;
}
/**
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/impl/WorkerWrapper.java b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/WorkerWrapper.java
index 9aa9137..4301e70 100644
--- a/background/workmanager/src/main/java/android/arch/background/workmanager/impl/WorkerWrapper.java
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/WorkerWrapper.java
@@ -54,7 +54,7 @@
private Context mAppContext;
private String mWorkSpecId;
private ExecutionListener mListener;
- private Scheduler mScheduler;
+ private List<Scheduler> mSchedulers;
private WorkSpec mWorkSpec;
Worker mWorker;
@@ -66,7 +66,7 @@
mAppContext = builder.mAppContext;
mWorkSpecId = builder.mWorkSpecId;
mListener = builder.mListener;
- mScheduler = builder.mScheduler;
+ mSchedulers = builder.mSchedulers;
mWorkDatabase = builder.mWorkDatabase;
mWorkSpecDao = mWorkDatabase.workSpecDao();
@@ -278,12 +278,15 @@
"Setting status to enqueued for %s Works that were dependent on Work ID %s",
unblockedWorkCount, mWorkSpecId);
}
- mWorkDatabase.setTransactionSuccessful();
- if (mScheduler != null) {
+ if (mSchedulers != null) {
WorkSpec[] unblockedWorkSpecs = mWorkSpecDao.getWorkSpecs(unblockedWorkIds);
- mScheduler.schedule(unblockedWorkSpecs);
+ for (Scheduler scheduler : mSchedulers) {
+ scheduler.schedule(unblockedWorkSpecs);
+ }
}
+
+ mWorkDatabase.setTransactionSuccessful();
} finally {
mWorkDatabase.endTransaction();
notifyListener(true, false);
@@ -318,7 +321,7 @@
private WorkDatabase mWorkDatabase;
private String mWorkSpecId;
private ExecutionListener mListener;
- private Scheduler mScheduler;
+ private List<Scheduler> mSchedulers;
Builder(@NonNull Context context,
@NonNull WorkDatabase database,
@@ -333,8 +336,8 @@
return this;
}
- Builder withScheduler(Scheduler scheduler) {
- mScheduler = scheduler;
+ Builder withSchedulers(List<Scheduler> schedulers) {
+ mSchedulers = schedulers;
return this;
}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/impl/background/greedy/GreedyScheduler.java b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/background/greedy/GreedyScheduler.java
new file mode 100644
index 0000000..acaa2d5
--- /dev/null
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/background/greedy/GreedyScheduler.java
@@ -0,0 +1,63 @@
+/*
+ * 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.background.greedy;
+
+import android.arch.background.workmanager.State;
+import android.arch.background.workmanager.impl.Scheduler;
+import android.arch.background.workmanager.impl.WorkManagerImpl;
+import android.arch.background.workmanager.impl.logger.Logger;
+import android.arch.background.workmanager.impl.model.WorkSpec;
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+
+/**
+ * A greedy {@link Scheduler} that schedules unconstrained, non-timed work. It intentionally does
+ * not acquire any WakeLocks, instead trying to brute-force them as time allows before the process
+ * gets killed.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class GreedyScheduler implements Scheduler {
+
+ private static final String TAG = "GreedyScheduler";
+
+ private WorkManagerImpl mWorkManagerImpl;
+
+ public GreedyScheduler(WorkManagerImpl workManagerImpl) {
+ mWorkManagerImpl = workManagerImpl;
+ }
+
+ @Override
+ public void schedule(WorkSpec... workSpecs) {
+ for (WorkSpec workSpec : workSpecs) {
+ if (workSpec.getState() == State.ENQUEUED
+ && !workSpec.isPeriodic()
+ && workSpec.getInitialDelay() == 0L
+ && !workSpec.hasConstraints()) {
+ Logger.debug(TAG, "Scheduling work ID %s", workSpec.getId());
+ mWorkManagerImpl.startWork(workSpec.getId());
+ }
+ }
+ }
+
+ @Override
+ public void cancel(@NonNull String workSpecId) {
+ Logger.debug(TAG, "Cancelling work ID %s", workSpecId);
+ mWorkManagerImpl.stopWork(workSpecId);
+ }
+}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/impl/background/systemalarm/SystemAlarmScheduler.java b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/background/systemalarm/SystemAlarmScheduler.java
index 7bde0b8..865b76d 100644
--- a/background/workmanager/src/main/java/android/arch/background/workmanager/impl/background/systemalarm/SystemAlarmScheduler.java
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/background/systemalarm/SystemAlarmScheduler.java
@@ -24,9 +24,6 @@
import android.support.annotation.NonNull;
import android.support.annotation.RestrictTo;
-import java.util.HashSet;
-import java.util.Set;
-
/**
* A {@link Scheduler} that schedules work using {@link android.app.AlarmManager}.
*
@@ -38,11 +35,9 @@
private static final String TAG = "SystemAlarmScheduler";
private final Context mContext;
- private Set<String> mCancelledIds;
public SystemAlarmScheduler(@NonNull Context context) {
mContext = context.getApplicationContext();
- mCancelledIds = new HashSet<>();
}
@Override
@@ -55,16 +50,10 @@
@Override
public void cancel(@NonNull String workSpecId) {
//TODO (rahulrav@) Store mapping alarm ids along / in a separate table
- mCancelledIds.add(workSpecId);
Intent cancelIntent = CommandHandler.createStopWorkIntent(mContext, workSpecId);
mContext.startService(cancelIntent);
}
- @Override
- public boolean isCancelled(@NonNull String workSpecId) {
- return mCancelledIds.contains(workSpecId);
- }
-
/**
* Periodic work is rescheduled using one-time alarms after each run. This allows the delivery
* times to drift to guarantee that the interval duration always elapses between alarms.
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/impl/background/systemjob/SystemJobScheduler.java b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/background/systemjob/SystemJobScheduler.java
index 70741d4..004473a 100644
--- a/background/workmanager/src/main/java/android/arch/background/workmanager/impl/background/systemjob/SystemJobScheduler.java
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/background/systemjob/SystemJobScheduler.java
@@ -26,9 +26,7 @@
import android.support.annotation.NonNull;
import android.support.annotation.RestrictTo;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
/**
* A class that schedules work using {@link android.app.job.JobScheduler}.
@@ -43,12 +41,10 @@
private JobScheduler mJobScheduler;
private SystemJobInfoConverter mSystemJobInfoConverter;
- private Set<String> mCancelledIds;
public SystemJobScheduler(Context context) {
mJobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
mSystemJobInfoConverter = new SystemJobInfoConverter(context);
- mCancelledIds = new HashSet<>();
}
@Override
@@ -62,7 +58,6 @@
@Override
public void cancel(@NonNull String workSpecId) {
- mCancelledIds.add(workSpecId);
// Note: despite what the word "pending" and the associated Javadoc might imply, this is
// actually a list of all unfinished jobs that JobScheduler knows about for the current
// process.
@@ -75,9 +70,4 @@
}
}
}
-
- @Override
- public boolean isCancelled(@NonNull String workSpecId) {
- return mCancelledIds.contains(workSpecId);
- }
}
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/impl/background/systemjob/SystemJobService.java b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/background/systemjob/SystemJobService.java
index 8a9d391..1db061c 100644
--- a/background/workmanager/src/main/java/android/arch/background/workmanager/impl/background/systemjob/SystemJobService.java
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/background/systemjob/SystemJobService.java
@@ -20,7 +20,6 @@
import android.app.job.JobParameters;
import android.app.job.JobService;
import android.arch.background.workmanager.impl.ExecutionListener;
-import android.arch.background.workmanager.impl.Scheduler;
import android.arch.background.workmanager.impl.WorkManagerImpl;
import android.arch.background.workmanager.impl.logger.Logger;
import android.os.PersistableBundle;
@@ -41,7 +40,6 @@
public class SystemJobService extends JobService implements ExecutionListener {
private static final String TAG = "SystemJobService";
private WorkManagerImpl mWorkManagerImpl;
- private Scheduler mScheduler;
private final Map<String, JobParameters> mJobParameters = new HashMap<>();
@Override
@@ -49,7 +47,6 @@
super.onCreate();
mWorkManagerImpl = WorkManagerImpl.getInstance();
mWorkManagerImpl.getProcessor().addExecutionListener(this);
- mScheduler = mWorkManagerImpl.getBackgroundScheduler();
}
@Override
@@ -97,7 +94,7 @@
mJobParameters.remove(workSpecId);
}
mWorkManagerImpl.stopWork(workSpecId);
- return !mScheduler.isCancelled(workSpecId);
+ return !mWorkManagerImpl.getProcessor().isCancelled(workSpecId);
}
@Override
diff --git a/background/workmanager/src/main/java/android/arch/background/workmanager/impl/utils/CancelWorkRunnable.java b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/utils/CancelWorkRunnable.java
index 0fe28a5..55b64fc 100644
--- a/background/workmanager/src/main/java/android/arch/background/workmanager/impl/utils/CancelWorkRunnable.java
+++ b/background/workmanager/src/main/java/android/arch/background/workmanager/impl/utils/CancelWorkRunnable.java
@@ -21,6 +21,8 @@
import static android.arch.background.workmanager.State.SUCCEEDED;
import android.arch.background.workmanager.State;
+import android.arch.background.workmanager.impl.Processor;
+import android.arch.background.workmanager.impl.Scheduler;
import android.arch.background.workmanager.impl.WorkDatabase;
import android.arch.background.workmanager.impl.WorkManagerImpl;
import android.arch.background.workmanager.impl.model.DependencyDao;
@@ -72,8 +74,14 @@
private void cancel(String workSpecId) {
recursivelyCancelWorkAndDependencies(workSpecId);
- mWorkManagerImpl.getProcessor().stopWork(workSpecId, true);
- mWorkManagerImpl.getBackgroundScheduler().cancel(workSpecId);
+
+ Processor processor = mWorkManagerImpl.getProcessor();
+ processor.stopWork(workSpecId, true);
+ processor.setCancelled(workSpecId);
+
+ for (Scheduler scheduler : mWorkManagerImpl.getSchedulers()) {
+ scheduler.cancel(workSpecId);
+ }
}
private void recursivelyCancelWorkAndDependencies(String workSpecId) {
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 c09cb9b..609c62c 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
@@ -26,6 +26,7 @@
import android.arch.background.workmanager.BaseWork;
import android.arch.background.workmanager.ExistingWorkPolicy;
import android.arch.background.workmanager.impl.InternalWorkImpl;
+import android.arch.background.workmanager.impl.Scheduler;
import android.arch.background.workmanager.impl.WorkContinuationImpl;
import android.arch.background.workmanager.impl.WorkDatabase;
import android.arch.background.workmanager.impl.WorkManagerImpl;
@@ -92,10 +93,10 @@
@VisibleForTesting
public void scheduleWorkInBackground() {
// Schedule in the background. This list contains work that does not have prerequisites.
- for (InternalWorkImpl work : mWorkToBeScheduled) {
- mWorkContinuation.getWorkManagerImpl()
- .getBackgroundScheduler()
- .schedule(work.getWorkSpec());
+ for (Scheduler scheduler : mWorkContinuation.getWorkManagerImpl().getSchedulers()) {
+ for (InternalWorkImpl work : mWorkToBeScheduled) {
+ scheduler.schedule(work.getWorkSpec());
+ }
}
}