1616package org .androidannotations .api ;
1717
1818import java .util .ArrayList ;
19- import java .util .HashMap ;
20- import java .util .HashSet ;
2119import java .util .List ;
22- import java .util .Map ;
23- import java .util .Set ;
2420import java .util .concurrent .Executor ;
2521import java .util .concurrent .Executors ;
2622import java .util .concurrent .ScheduledExecutorService ;
@@ -30,92 +26,86 @@ public class BackgroundExecutor {
3026
3127 private static Executor executor = Executors .newScheduledThreadPool (2 * Runtime .getRuntime ().availableProcessors ());
3228
33- /*
34- * serialRunning is used as a lock in synchronized blocks for both
35- * serialRunning and serialQueues access
36- */
37-
38- /* Set of queueIds having a currently running task */
39- private static final Set <String > serialRunning = new HashSet <String >();
40-
41- /* Tasks queues for each serial */
42- private static final Map <String , List <Task >> serialQueues = new HashMap <String , List <Task >>();
29+ private static final List <Task > tasks = new ArrayList <Task >();
4330
4431 /**
45- * Execute a task after (at least) the given delay <strong>and</strong>
46- * after all tasks added with the same non-null <code>serial</code> (if any)
47- * have completed execution.
32+ * Execute a runnable after the given delay.
4833 *
4934 * @param runnable
5035 * the task to execute
5136 * @param delay
5237 * the time from now to delay execution, in milliseconds
53- * @param serial
54- * the serial queue to use (<code>null</code> or <code>""</code>
55- * for no serial execution)
5638 * @throws IllegalArgumentException
5739 * if <code>delay</code> is strictly positive and the current
5840 * executor does not support scheduling (if
5941 * {@link #setExecutor(Executor)} has been called with such an
6042 * executor)
6143 */
62- public static void execute (Runnable runnable , int delay , String serial ) {
63- /* "" means null (a default annotation String value cannot be null) */
64- if (serial == null || serial .isEmpty ()) {
65- if (delay > 0 ) {
66- /* no serial, but a delay: schedule the task */
67- if (!(executor instanceof ScheduledExecutorService )) {
68- throw new IllegalArgumentException ("The executor set does not support scheduling" );
69- }
70- ((ScheduledExecutorService ) executor ).schedule (runnable , delay , TimeUnit .MILLISECONDS );
71- } else {
72- /* no serial, no delay: execute now */
73- executor .execute (runnable );
44+ private static void directExecute (Runnable runnable , int delay ) {
45+ if (delay > 0 ) {
46+ /* no serial, but a delay: schedule the task */
47+ if (!(executor instanceof ScheduledExecutorService )) {
48+ throw new IllegalArgumentException ("The executor set does not support scheduling" );
7449 }
50+ ((ScheduledExecutorService ) executor ).schedule (runnable , delay , TimeUnit .MILLISECONDS );
7551 } else {
76- /* serial is defined, the delay is managed by Task */
77- Task task = new Task (runnable , delay , serial );
78-
79- synchronized (serialRunning ) {
80- if (serialRunning .contains (serial )) {
81- /* a task for this serial is already running, queue this one */
82- List <Task > queue = serialQueues .get (serial );
83- if (queue == null ) {
84- /* the queue does not exist yet */
85- queue = new ArrayList <Task >();
86- serialQueues .put (serial , queue );
87- }
88- /* queue the task for later execution */
89- queue .add (task );
90- } else {
91- /* mark this serial as having a running task */
92- serialRunning .add (serial );
93- /* execute the task (a wrapper for runnable) now */
94- execute (task , delay ); /* do not pass serial here */
95- }
96- }
52+ /* no serial, no delay: execute now */
53+ executor .execute (runnable );
9754 }
9855 }
9956
10057 /**
101- * Execute a task.
58+ * Execute a task after (at least) its delay <strong>and</strong> after all
59+ * tasks added with the same non-null <code>serial</code> (if any) have
60+ * completed execution.
10261 *
103- * Equivalent to {@link #execute(Runnable, int, String) execute(runnable, 0,
104- * null)}.
62+ * @param task
63+ * the task to execute
64+ * @throws IllegalArgumentException
65+ * if <code>task.delay</code> is strictly positive and the
66+ * current executor does not support scheduling (if
67+ * {@link #setExecutor(Executor)} has been called with such an
68+ * executor)
69+ */
70+ public static synchronized void execute (Task task ) {
71+ if (task .serial == null || !hasSerialRunning (task .serial )) {
72+ task .executionAsked = true ;
73+ directExecute (task , task .delay );
74+ }
75+ if (task .serial != null ) {
76+ /* keep task */
77+ tasks .add (task );
78+ }
79+ }
80+
81+ /**
82+ * Execute a task.
10583 *
10684 * @param runnable
10785 * the task to execute
86+ * @param delay
87+ * the time from now to delay execution, in milliseconds
88+ * @param serial
89+ * the serial queue (<code>null</code> or <code>""</code> for no
90+ * serial execution)
91+ * @throws IllegalArgumentException
92+ * if <code>delay</code> is strictly positive and the current
93+ * executor does not support scheduling (if
94+ * {@link #setExecutor(Executor)} has been called with such an
95+ * executor)
10896 */
109- public static void execute (Runnable runnable ) {
110- execute (runnable , 0 , null );
97+ public static void execute (final Runnable runnable , int delay , String serial ) {
98+ execute (new Task (delay , serial ) {
99+ @ Override
100+ public void execute () {
101+ runnable .run ();
102+ }
103+ });
111104 }
112105
113106 /**
114107 * Execute a task after the given delay.
115108 *
116- * Equivalent to {@link #execute(Runnable, int, String) execute(runnable,
117- * delay, null)}.
118- *
119109 * @param runnable
120110 * the task to execute
121111 * @param delay
@@ -127,7 +117,17 @@ public static void execute(Runnable runnable) {
127117 * executor)
128118 */
129119 public static void execute (Runnable runnable , int delay ) {
130- execute (runnable , delay , null );
120+ directExecute (runnable , delay );
121+ }
122+
123+ /**
124+ * Execute a task.
125+ *
126+ * @param runnable
127+ * the task to execute
128+ */
129+ public static void execute (Runnable runnable ) {
130+ directExecute (runnable , 0 );
131131 }
132132
133133 /**
@@ -160,50 +160,88 @@ public static void setExecutor(Executor executor) {
160160 BackgroundExecutor .executor = executor ;
161161 }
162162
163- private static class Task implements Runnable {
163+ /**
164+ * Indicates whether a task with the specified <code>serial</code> has been
165+ * submitted to the executor.
166+ *
167+ * @param serial
168+ * the serial queue
169+ * @return <code>true</code> if such a task has been submitted,
170+ * <code>false</code> otherwise
171+ */
172+ private static boolean hasSerialRunning (String serial ) {
173+ for (Task task : tasks ) {
174+ if (task .executionAsked && serial .equals (task .serial )) {
175+ return true ;
176+ }
177+ }
178+ return false ;
179+ }
180+
181+ /**
182+ * Retrieve and remove the first task having the specified
183+ * <code>serial</code> (if any).
184+ *
185+ * @param serial
186+ * the serial queue
187+ * @return task if found, <code>null</code> otherwise
188+ */
189+ private static Task take (String serial ) {
190+ int len = tasks .size ();
191+ for (int i = 0 ; i < len ; i ++) {
192+ if (serial .equals (tasks .get (i ).serial )) {
193+ return tasks .remove (i );
194+ }
195+ }
196+ return null ;
197+ }
198+
199+ public static abstract class Task implements Runnable {
164200
165- Runnable runnable ;
166- long targetTime ; /* in milliseconds since epoch */
167- String serial ;
201+ private int delay ;
202+ private long targetTime ; /* in milliseconds since epoch */
203+ private String serial ;
204+ private boolean executionAsked ;
168205
169- Task (Runnable runnable , int delay , String serial ) {
170- this .runnable = runnable ;
206+ public Task (int delay , String serial ) {
171207 if (delay > 0 ) {
208+ this .delay = delay ;
172209 targetTime = System .currentTimeMillis () + delay ;
173210 }
174- this .serial = serial ;
211+ if (!"" .equals (serial )) {
212+ this .serial = serial ;
213+ }
175214 }
176215
177216 @ Override
178217 public void run () {
179218 try {
180- runnable . run ();
219+ execute ();
181220 } finally {
182221 /* handle next tasks */
183222 postExecute ();
184223 }
185224 }
186225
226+ public abstract void execute ();
227+
187228 private void postExecute () {
188- synchronized (serialRunning ) {
189- List <Task > queue = serialQueues .get (serial );
190- if (queue == null ) {
191- /* no task is queue for this serial, mark it as not running */
192- serialRunning .remove (serial );
193- } else {
194- /* queue is not empty, retrieve the oldest queued task */
195- Task nextTask = queue .remove (0 );
196-
197- if (queue .isEmpty ()) {
198- /* no more tasks in the queue */
199- serialQueues .remove (serial );
229+ if (serial == null ) {
230+ /* nothing to do */
231+ return ;
232+ }
233+ synchronized (BackgroundExecutor .class ) {
234+ /* execution complete */
235+ tasks .remove (this );
236+
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 ()));
200242 }
201-
202- /* compute the remaining delay */
203- int delay = Math .max (0 , (int ) (nextTask .targetTime - System .currentTimeMillis ()));
204-
205- /* execute the next task */
206- execute (nextTask , delay ); /* do not pass serial here */
243+ /* a task having the same serial was queued, execute it */
244+ BackgroundExecutor .execute (next );
207245 }
208246 }
209247 }
0 commit comments