Skip to content

Commit ce449c8

Browse files
Drozdov ArtyomWonderCsabo
authored andcommitted
add: cleaning up the map at cost of synchronization
1 parent 930f599 commit ce449c8

2 files changed

Lines changed: 69 additions & 16 deletions

File tree

AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/api/UiThreadExecutor.java

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515
*/
1616
package org.androidannotations.api;
1717

18-
import java.util.concurrent.ConcurrentHashMap;
18+
import java.util.HashMap;
19+
import java.util.Map;
1920

2021
import android.os.Handler;
2122
import android.os.Looper;
23+
import android.os.Message;
2224
import android.os.SystemClock;
2325

2426
/**
@@ -27,9 +29,20 @@
2729
*/
2830
public class UiThreadExecutor {
2931

30-
private static final Handler HANDLER = new Handler(Looper.getMainLooper());
32+
private static final Handler HANDLER = new Handler(Looper.getMainLooper()) {
33+
@Override
34+
public void handleMessage(Message msg) {
35+
Runnable callback = msg.getCallback();
36+
if (callback != null) {
37+
callback.run();
38+
decrementToken((Token) msg.obj);
39+
} else {
40+
super.handleMessage(msg);
41+
}
42+
}
43+
};
3144

32-
private static final ConcurrentHashMap<String, Object> TOKENS = new ConcurrentHashMap<String, Object>();
45+
private static final Map<String, Token> TOKENS = new HashMap<String, Token>();
3346

3447
private UiThreadExecutor() {
3548
// should not be instantiated
@@ -52,20 +65,33 @@ public static void runTask(String id, Runnable task, long delay) {
5265
return;
5366
}
5467
long time = SystemClock.uptimeMillis() + delay;
55-
HANDLER.postAtTime(task, getToken(id), time);
68+
HANDLER.postAtTime(task, nextToken(id), time);
5669
}
5770

58-
private static Object getToken(String id) {
59-
Object token = TOKENS.get(id);
60-
if (token == null) {
61-
token = new Object();
62-
Object oldObject = TOKENS.putIfAbsent(id, token);
63-
// if a concurrent thread was faster
64-
if (oldObject != null) {
65-
token = oldObject;
71+
private static Token nextToken(String id) {
72+
synchronized (TOKENS) {
73+
Token token = TOKENS.get(id);
74+
if (token == null) {
75+
token = new Token(id);
76+
TOKENS.put(id, token);
77+
}
78+
token.runnablesCount++;
79+
return token;
80+
}
81+
}
82+
83+
private static void decrementToken(Token token) {
84+
synchronized (TOKENS) {
85+
if (--token.runnablesCount == 0) {
86+
String id = token.id;
87+
Token old = TOKENS.remove(id);
88+
if (old != token) {
89+
// a runnable finished after cancelling, we just removed a
90+
// wrong token, lets put it back
91+
TOKENS.put(id, old);
92+
}
6693
}
6794
}
68-
return token;
6995
}
7096

7197
/**
@@ -75,12 +101,24 @@ private static Object getToken(String id) {
75101
* the cancellation identifier
76102
*/
77103
public static void cancelAll(String id) {
78-
Object token = TOKENS.get(id);
104+
Token token;
105+
synchronized (TOKENS) {
106+
token = TOKENS.remove(id);
107+
}
79108
if (token == null) {
80109
// nothing to cancel
81110
return;
82111
}
83112
HANDLER.removeCallbacksAndMessages(token);
84113
}
85114

115+
private static class Token {
116+
int runnablesCount = 0;
117+
final String id;
118+
119+
private Token(String id) {
120+
this.id = id;
121+
}
122+
}
123+
86124
}

AndroidAnnotations/functional-test-1-5/src/test/java/org/androidannotations/test15/UiThreadExecutorTest.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.androidannotations.test15;
1717

18+
import static org.junit.Assert.assertFalse;
1819
import static org.junit.Assert.assertTrue;
1920

2021
import java.util.concurrent.CountDownLatch;
@@ -38,11 +39,25 @@ public void oneTaskTest() throws Exception {
3839
public void run() {
3940
done.set(true);
4041
}
41-
}, 0);
42+
}, 10);
4243
Robolectric.runUiThreadTasksIncludingDelayedTasks();
4344
assertTrue("Task is still under execution", done.get());
4445
}
4546

47+
@Test
48+
public void oneTaskCancelTest() throws Exception {
49+
final AtomicBoolean done = new AtomicBoolean(false);
50+
UiThreadExecutor.runTask("test", new Runnable() {
51+
@Override
52+
public void run() {
53+
done.set(true);
54+
}
55+
}, 10);
56+
UiThreadExecutor.cancelAll("test");
57+
Robolectric.runUiThreadTasksIncludingDelayedTasks();
58+
assertFalse("Task is not cancelled", done.get());
59+
}
60+
4661
@Test
4762
public void oneTaskInThreadTest() throws Exception {
4863
final CountDownLatch taskStartedLatch = new CountDownLatch(1);
@@ -56,7 +71,7 @@ public void run() {
5671
await(taskStartedLatch);
5772
taskFinishedLatch.countDown();
5873
}
59-
}, 0);
74+
}, 10);
6075
taskStartedLatch.countDown();
6176
Robolectric.runUiThreadTasksIncludingDelayedTasks();
6277
}

0 commit comments

Comments
 (0)