Skip to content

Commit 99ca871

Browse files
committed
Cancellable background tasks
Adds an optional id attribute to @background: @background(id="some_id") The user is able to cancel all background tasks having a specified id: BackgroundExecutor.cancelAll("some_id");
1 parent ec9fea3 commit 99ca871

3 files changed

Lines changed: 73 additions & 22 deletions

File tree

‎AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/Background.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
@Retention(RetentionPolicy.CLASS)
2929
@Target(ElementType.METHOD)
3030
public @interface Background {
31+
String id() default ""; /* used for task cancellation */
32+
3133
int delay() default 0; /* in milliseconds */
3234

3335
String serial() default "";

‎AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/api/BackgroundExecutor.java‎

Lines changed: 69 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
import java.util.ArrayList;
1919
import java.util.List;
2020
import java.util.concurrent.Executor;
21+
import java.util.concurrent.ExecutorService;
2122
import java.util.concurrent.Executors;
23+
import java.util.concurrent.Future;
2224
import java.util.concurrent.ScheduledExecutorService;
2325
import java.util.concurrent.TimeUnit;
2426

@@ -40,18 +42,26 @@ public class BackgroundExecutor {
4042
* executor does not support scheduling (if
4143
* {@link #setExecutor(Executor)} has been called with such an
4244
* executor)
45+
* @return Future associated to the running task
4346
*/
44-
private static void directExecute(Runnable runnable, int delay) {
47+
private static Future<?> directExecute(Runnable runnable, int delay) {
48+
Future<?> future = null;
4549
if (delay > 0) {
4650
/* no serial, but a delay: schedule the task */
4751
if (!(executor instanceof ScheduledExecutorService)) {
4852
throw new IllegalArgumentException("The executor set does not support scheduling");
4953
}
50-
((ScheduledExecutorService) executor).schedule(runnable, delay, TimeUnit.MILLISECONDS);
54+
future = ((ScheduledExecutorService) executor).schedule(runnable, delay, TimeUnit.MILLISECONDS);
5155
} else {
5256
/* no serial, no delay: execute now */
53-
executor.execute(runnable);
57+
if (executor instanceof ExecutorService) {
58+
future = ((ExecutorService) executor).submit(runnable);
59+
} else {
60+
/* non-cancellable task */
61+
executor.execute(runnable);
62+
}
5463
}
64+
return future;
5565
}
5666

5767
/**
@@ -68,12 +78,17 @@ private static void directExecute(Runnable runnable, int delay) {
6878
* executor)
6979
*/
7080
public static synchronized void execute(Task task) {
81+
Future<?> future = null;
7182
if (task.serial == null || !hasSerialRunning(task.serial)) {
7283
task.executionAsked = true;
73-
directExecute(task, task.delay);
84+
future = directExecute(task, task.delay);
85+
if (task.id != null && future == null) {
86+
throw new IllegalArgumentException("The executor set does not support task cancellation");
87+
}
7488
}
75-
if (task.serial != null) {
89+
if (task.id != null || task.serial != null) {
7690
/* keep task */
91+
task.future = future;
7792
tasks.add(task);
7893
}
7994
}
@@ -83,6 +98,8 @@ public static synchronized void execute(Task task) {
8398
*
8499
* @param runnable
85100
* the task to execute
101+
* @param id
102+
* identifier used for task cancellation
86103
* @param delay
87104
* the time from now to delay execution, in milliseconds
88105
* @param serial
@@ -94,8 +111,8 @@ public static synchronized void execute(Task task) {
94111
* {@link #setExecutor(Executor)} has been called with such an
95112
* executor)
96113
*/
97-
public static void execute(final Runnable runnable, int delay, String serial) {
98-
execute(new Task(delay, serial) {
114+
public static void execute(final Runnable runnable, String id, int delay, String serial) {
115+
execute(new Task(id, delay, serial) {
99116
@Override
100117
public void execute() {
101118
runnable.run();
@@ -134,24 +151,28 @@ public static void execute(Runnable runnable) {
134151
* Execute a task after all tasks added with the same non-null
135152
* <code>serial</code> (if any) have completed execution.
136153
*
137-
* Equivalent to {@link #execute(Runnable, int, String) execute(runnable, 0,
138-
* serial)}.
154+
* Equivalent to {@link #execute(Runnable, String, int, String)
155+
* execute(runnable, id, 0, serial)}.
139156
*
140157
* @param runnable
141158
* the task to execute
159+
* @param id
160+
* identifier used for task cancellation
142161
* @param serial
143162
* the serial queue to use (<code>null</code> or <code>""</code>
144163
* for no serial execution)
145164
*/
146-
public static void execute(Runnable runnable, String serial) {
147-
execute(runnable, 0, serial);
165+
public static void execute(Runnable runnable, String id, String serial) {
166+
execute(runnable, id, 0, serial);
148167
}
149168

150169
/**
151170
* Change the executor.
152171
*
153172
* Note that if the given executor is not a {@link ScheduledExecutorService}
154-
* then executing a task after a delay will not be supported anymore.
173+
* then executing a task after a delay will not be supported anymore. If it
174+
* is not even a {@link ExecutorService} then tasks will not be cancellable
175+
* anymore.
155176
*
156177
* @param executor
157178
* the new executor
@@ -160,6 +181,26 @@ public static void setExecutor(Executor executor) {
160181
BackgroundExecutor.executor = executor;
161182
}
162183

184+
/**
185+
* Cancel all tasks having the specified <code>id</code>.
186+
*
187+
* @param id
188+
* the cancellation identifier
189+
* @param mayInterruptIfRunning
190+
* <code>true<.code> if the thread executing this task should be interrupted; otherwise, in-progress tasks are allowed to complete
191+
*/
192+
public static synchronized void cancelAll(String id, boolean mayInterruptIfRunning) {
193+
for (int i = tasks.size() - 1; i >= 0; i--) {
194+
Task task = tasks.get(i);
195+
if (id.equals(task.id)) {
196+
tasks.remove(i);
197+
if (task.future != null) {
198+
task.future.cancel(mayInterruptIfRunning);
199+
}
200+
}
201+
}
202+
}
203+
163204
/**
164205
* Indicates whether a task with the specified <code>serial</code> has been
165206
* submitted to the executor.
@@ -198,12 +239,17 @@ private static Task take(String serial) {
198239

199240
public static abstract class Task implements Runnable {
200241

242+
private String id;
201243
private int delay;
202244
private long targetTime; /* in milliseconds since epoch */
203245
private String serial;
204246
private boolean executionAsked;
247+
private Future<?> future;
205248

206-
public Task(int delay, String serial) {
249+
public Task(String id, int delay, String serial) {
250+
if (!"".equals(id)) {
251+
this.id = id;
252+
}
207253
if (delay > 0) {
208254
this.delay = delay;
209255
targetTime = System.currentTimeMillis() + delay;
@@ -226,22 +272,24 @@ public void run() {
226272
public abstract void execute();
227273

228274
private void postExecute() {
229-
if (serial == null) {
275+
if (id == null && serial == null) {
230276
/* nothing to do */
231277
return;
232278
}
233279
synchronized (BackgroundExecutor.class) {
234280
/* execution complete */
235281
tasks.remove(this);
236282

237-
Task next = take(serial);
238-
if (next != null) {
239-
if (next.delay != 0) {
240-
/* compute remaining delay */
241-
next.delay = Math.max(0, (int) (targetTime - System.currentTimeMillis()));
283+
if (serial != null) {
284+
Task next = take(serial);
285+
if (next != null) {
286+
if (next.delay != 0) {
287+
/* compute remaining delay */
288+
next.delay = Math.max(0, (int) (targetTime - System.currentTimeMillis()));
289+
}
290+
/* a task having the same serial was queued, execute it */
291+
BackgroundExecutor.execute(next);
242292
}
243-
/* a task having the same serial was queued, execute it */
244-
BackgroundExecutor.execute(next);
245293
}
246294
}
247295
}

‎AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/BackgroundProcessor.java‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,10 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) t
6666
Background annotation = element.getAnnotation(Background.class);
6767
String id = annotation.id();
6868
int delay = annotation.delay();
69+
String serial = annotation.serial();
6970

7071
JClass backgroundExecutorClass = holder.refClass(BackgroundExecutor.class);
71-
JInvocation newTask = _new(anonymousTaskClass).arg(lit(id)).arg(lit(delay));
72+
JInvocation newTask = _new(anonymousTaskClass).arg(lit(id)).arg(lit(delay)).arg(lit(serial));
7273
JInvocation executeCall = backgroundExecutorClass.staticInvoke("execute").arg(newTask);
7374

7475
delegatingMethod.body().add(executeCall);

0 commit comments

Comments
 (0)