27
27
import rx .plugins .*;
28
28
import rx .subscriptions .*;
29
29
30
+ import static rx .internal .util .PlatformDependent .ANDROID_API_VERSION_IS_NOT_ANDROID ;
31
+
30
32
/**
31
33
* @warn class description missing
32
34
*/
@@ -39,17 +41,25 @@ public class NewThreadWorker extends Scheduler.Worker implements Subscription {
39
41
/** Force the use of purge (true/false). */
40
42
private static final String PURGE_FORCE_KEY = "rx.scheduler.jdk6.purge-force" ;
41
43
private static final String PURGE_THREAD_PREFIX = "RxSchedulerPurge-" ;
42
- /** Forces the use of purge even if setRemoveOnCancelPolicy is available. */
43
- private static final boolean PURGE_FORCE ;
44
+ private static final boolean SHOULD_TRY_ENABLE_CANCEL_POLICY ;
44
45
/** The purge frequency in milliseconds. */
45
46
public static final int PURGE_FREQUENCY ;
46
47
private static final ConcurrentHashMap <ScheduledThreadPoolExecutor , ScheduledThreadPoolExecutor > EXECUTORS ;
47
48
private static final AtomicReference <ScheduledExecutorService > PURGE ;
48
49
static {
49
50
EXECUTORS = new ConcurrentHashMap <ScheduledThreadPoolExecutor , ScheduledThreadPoolExecutor >();
50
51
PURGE = new AtomicReference <ScheduledExecutorService >();
51
- PURGE_FORCE = Boolean .getBoolean (PURGE_FORCE_KEY );
52
52
PURGE_FREQUENCY = Integer .getInteger (FREQUENCY_KEY , 1000 );
53
+
54
+ // Forces the use of purge even if setRemoveOnCancelPolicy is available
55
+ final boolean purgeForce = Boolean .getBoolean (PURGE_FORCE_KEY );
56
+
57
+ final int androidApiVersion = PlatformDependent .getAndroidApiVersion ();
58
+
59
+ // According to http://developer.android.com/reference/java/util/concurrent/ScheduledThreadPoolExecutor.html#setRemoveOnCancelPolicy(boolean)
60
+ // setRemoveOnCancelPolicy available since Android API 21
61
+ SHOULD_TRY_ENABLE_CANCEL_POLICY = !purgeForce
62
+ && (androidApiVersion == ANDROID_API_VERSION_IS_NOT_ANDROID || androidApiVersion >= 21 );
53
63
}
54
64
/**
55
65
* Registers the given executor service and starts the purge thread if not already started.
@@ -85,6 +95,7 @@ public void run() {
85
95
public static void deregisterExecutor (ScheduledExecutorService service ) {
86
96
EXECUTORS .remove (service );
87
97
}
98
+
88
99
/** Purges each registered executor and eagerly evicts shutdown executors. */
89
100
static void purgeExecutors () {
90
101
try {
@@ -102,32 +113,89 @@ static void purgeExecutors() {
102
113
RxJavaPlugins .getInstance ().getErrorHandler ().handleError (t );
103
114
}
104
115
}
105
-
106
- /**
116
+
117
+ /**
118
+ * Improves performance of {@link #tryEnableCancelPolicy(ScheduledExecutorService)}.
119
+ * Also, it works even for inheritance: {@link Method} of base class can be invoked on the instance of child class.
120
+ */
121
+ private static volatile Object cachedSetRemoveOnCancelPolicyMethod ;
122
+
123
+ /**
124
+ * Possible value of {@link #cachedSetRemoveOnCancelPolicyMethod} which means that cancel policy is not supported.
125
+ */
126
+ private static final Object SET_REMOVE_ON_CANCEL_POLICY_METHOD_NOT_SUPPORTED = new Object ();
127
+
128
+ /**
107
129
* Tries to enable the Java 7+ setRemoveOnCancelPolicy.
108
130
* <p>{@code public} visibility reason: called from other package(s) within RxJava.
109
131
* If the method returns false, the {@link #registerExecutor(ScheduledThreadPoolExecutor)} may
110
132
* be called to enable the backup option of purging the executors.
111
- * @param exec the executor to call setRemoveOnCaneclPolicy if available.
133
+ * @param executor the executor to call setRemoveOnCaneclPolicy if available.
112
134
* @return true if the policy was successfully enabled
113
135
*/
114
- public static boolean tryEnableCancelPolicy (ScheduledExecutorService exec ) {
115
- if (!PURGE_FORCE ) {
116
- for (Method m : exec .getClass ().getMethods ()) {
117
- if (m .getName ().equals ("setRemoveOnCancelPolicy" )
118
- && m .getParameterTypes ().length == 1
119
- && m .getParameterTypes ()[0 ] == Boolean .TYPE ) {
120
- try {
121
- m .invoke (exec , true );
122
- return true ;
123
- } catch (Exception ex ) {
124
- RxJavaPlugins .getInstance ().getErrorHandler ().handleError (ex );
125
- }
136
+ public static boolean tryEnableCancelPolicy (ScheduledExecutorService executor ) {
137
+ if (SHOULD_TRY_ENABLE_CANCEL_POLICY ) {
138
+ final boolean isInstanceOfScheduledThreadPoolExecutor = executor instanceof ScheduledThreadPoolExecutor ;
139
+
140
+ final Method methodToCall ;
141
+
142
+ if (isInstanceOfScheduledThreadPoolExecutor ) {
143
+ final Object localSetRemoveOnCancelPolicyMethod = cachedSetRemoveOnCancelPolicyMethod ;
144
+
145
+ if (localSetRemoveOnCancelPolicyMethod == SET_REMOVE_ON_CANCEL_POLICY_METHOD_NOT_SUPPORTED ) {
146
+ return false ;
147
+ }
148
+
149
+ if (localSetRemoveOnCancelPolicyMethod == null ) {
150
+ Method method = findSetRemoveOnCancelPolicyMethod (executor );
151
+
152
+ cachedSetRemoveOnCancelPolicyMethod = method != null
153
+ ? method
154
+ : SET_REMOVE_ON_CANCEL_POLICY_METHOD_NOT_SUPPORTED ;
155
+
156
+ methodToCall = method ;
157
+ } else {
158
+ methodToCall = (Method ) localSetRemoveOnCancelPolicyMethod ;
159
+ }
160
+ } else {
161
+ methodToCall = findSetRemoveOnCancelPolicyMethod (executor );
162
+ }
163
+
164
+ if (methodToCall != null ) {
165
+ try {
166
+ methodToCall .invoke (executor , true );
167
+ return true ;
168
+ } catch (Exception e ) {
169
+ RxJavaPlugins .getInstance ().getErrorHandler ().handleError (e );
126
170
}
127
171
}
128
172
}
173
+
129
174
return false ;
130
175
}
176
+
177
+ /**
178
+ * Tries to find {@code "setRemoveOnCancelPolicy(boolean)"} method in the class of passed executor.
179
+ *
180
+ * @param executor whose class will be used to search for required method.
181
+ * @return {@code "setRemoveOnCancelPolicy(boolean)"} {@link Method}
182
+ * or {@code null} if required {@link Method} was not found.
183
+ */
184
+ static Method findSetRemoveOnCancelPolicyMethod (ScheduledExecutorService executor ) {
185
+ // The reason for the loop is to avoid NoSuchMethodException being thrown on JDK 6
186
+ // which is more costly than looping through ~70 methods.
187
+ for (final Method method : executor .getClass ().getMethods ()) {
188
+ if (method .getName ().equals ("setRemoveOnCancelPolicy" )) {
189
+ final Class <?>[] parameterTypes = method .getParameterTypes ();
190
+
191
+ if (parameterTypes .length == 1 && parameterTypes [0 ] == Boolean .TYPE ) {
192
+ return method ;
193
+ }
194
+ }
195
+ }
196
+
197
+ return null ;
198
+ }
131
199
132
200
/* package */
133
201
public NewThreadWorker (ThreadFactory threadFactory ) {
0 commit comments