27
27
import android .os .Looper ;
28
28
import android .os .Message ;
29
29
import java .util .ArrayList ;
30
+ import java .util .HashSet ;
30
31
import java .util .Iterator ;
31
32
import java .util .LinkedHashMap ;
32
33
import java .util .List ;
33
34
import java .util .Map ;
35
+ import java .util .Set ;
34
36
import java .util .WeakHashMap ;
35
37
import java .util .concurrent .ExecutorService ;
36
38
45
47
import static com .squareup .picasso .Utils .VERB_DELIVERED ;
46
48
import static com .squareup .picasso .Utils .VERB_ENQUEUED ;
47
49
import static com .squareup .picasso .Utils .VERB_IGNORED ;
50
+ import static com .squareup .picasso .Utils .VERB_PAUSED ;
48
51
import static com .squareup .picasso .Utils .VERB_REPLAYING ;
49
52
import static com .squareup .picasso .Utils .VERB_RETRYING ;
50
53
import static com .squareup .picasso .Utils .getLogIdsForHunter ;
@@ -67,6 +70,9 @@ class Dispatcher {
67
70
static final int HUNTER_BATCH_COMPLETE = 8 ;
68
71
static final int NETWORK_STATE_CHANGE = 9 ;
69
72
static final int AIRPLANE_MODE_CHANGE = 10 ;
73
+ static final int TAG_PAUSE = 11 ;
74
+ static final int TAG_RESUME = 12 ;
75
+ static final int REQUEST_BATCH_RESUME = 13 ;
70
76
71
77
private static final String DISPATCHER_THREAD_NAME = "Dispatcher" ;
72
78
private static final int BATCH_DELAY = 200 ; // ms
@@ -77,6 +83,8 @@ class Dispatcher {
77
83
final Downloader downloader ;
78
84
final Map <String , BitmapHunter > hunterMap ;
79
85
final Map <Object , Action > failedActions ;
86
+ final Map <Object , Action > pausedActions ;
87
+ final Set <Object > pausedTags ;
80
88
final Handler handler ;
81
89
final Handler mainThreadHandler ;
82
90
final Cache cache ;
@@ -95,6 +103,8 @@ class Dispatcher {
95
103
this .service = service ;
96
104
this .hunterMap = new LinkedHashMap <String , BitmapHunter >();
97
105
this .failedActions = new WeakHashMap <Object , Action >();
106
+ this .pausedActions = new WeakHashMap <Object , Action >();
107
+ this .pausedTags = new HashSet <Object >();
98
108
this .handler = new DispatcherHandler (dispatcherThread .getLooper (), this );
99
109
this .downloader = downloader ;
100
110
this .mainThreadHandler = mainThreadHandler ;
@@ -121,6 +131,14 @@ void dispatchCancel(Action action) {
121
131
handler .sendMessage (handler .obtainMessage (REQUEST_CANCEL , action ));
122
132
}
123
133
134
+ void dispatchPauseTag (Object tag ) {
135
+ handler .sendMessage (handler .obtainMessage (TAG_PAUSE , tag ));
136
+ }
137
+
138
+ void dispatchResumeTag (Object tag ) {
139
+ handler .sendMessage (handler .obtainMessage (TAG_RESUME , tag ));
140
+ }
141
+
124
142
void dispatchComplete (BitmapHunter hunter ) {
125
143
handler .sendMessage (handler .obtainMessage (HUNTER_COMPLETE , hunter ));
126
144
}
@@ -143,6 +161,15 @@ void dispatchAirplaneModeChange(boolean airplaneMode) {
143
161
}
144
162
145
163
void performSubmit (Action action ) {
164
+ if (pausedTags .contains (action .getTag ())) {
165
+ pausedActions .put (action .getTarget (), action );
166
+ if (action .getPicasso ().loggingEnabled ) {
167
+ log (OWNER_DISPATCHER , VERB_PAUSED , action .request .logId (),
168
+ "because tag '" + action .getTag () + "' is paused" );
169
+ }
170
+ return ;
171
+ }
172
+
146
173
BitmapHunter hunter = hunterMap .get (action .getKey ());
147
174
if (hunter != null ) {
148
175
hunter .attach (action );
@@ -178,12 +205,101 @@ void performCancel(Action action) {
178
205
}
179
206
}
180
207
}
208
+
209
+ if (pausedTags .contains (action .getTag ())) {
210
+ pausedActions .remove (action .getTarget ());
211
+ if (action .getPicasso ().loggingEnabled ) {
212
+ log (OWNER_DISPATCHER , VERB_CANCELED , action .getRequest ().logId (),
213
+ "because paused request got canceled" );
214
+ }
215
+ }
216
+
181
217
Action remove = failedActions .remove (action .getTarget ());
182
218
if (remove != null && remove .getPicasso ().loggingEnabled ) {
183
219
log (OWNER_DISPATCHER , VERB_CANCELED , remove .getRequest ().logId (), "from replaying" );
184
220
}
185
221
}
186
222
223
+ void performPauseTag (Object tag ) {
224
+ // Trying to pause a tag that is already paused.
225
+ if (!pausedTags .add (tag )) {
226
+ return ;
227
+ }
228
+
229
+ // Go through all active hunters and detach/pause the requests
230
+ // that have the paused tag.
231
+ for (Iterator <BitmapHunter > it = hunterMap .values ().iterator (); it .hasNext ();) {
232
+ BitmapHunter hunter = it .next ();
233
+ boolean loggingEnabled = hunter .getPicasso ().loggingEnabled ;
234
+
235
+ Action single = hunter .getAction ();
236
+ List <Action > joined = hunter .getActions ();
237
+ boolean hasMultiple = joined != null && !joined .isEmpty ();
238
+
239
+ // Hunter has no requests, bail early.
240
+ if (single == null && !hasMultiple ) {
241
+ continue ;
242
+ }
243
+
244
+ if (single != null && single .getTag ().equals (tag )) {
245
+ hunter .detach (single );
246
+ pausedActions .put (single .getTarget (), single );
247
+ if (loggingEnabled ) {
248
+ log (OWNER_DISPATCHER , VERB_PAUSED , single .request .logId (),
249
+ "because tag '" + tag + "' was paused" );
250
+ }
251
+ }
252
+
253
+ if (hasMultiple ) {
254
+ for (int i = joined .size () - 1 ; i >= 0 ; i --) {
255
+ Action action = joined .get (i );
256
+ if (!action .getTag ().equals (tag )) {
257
+ continue ;
258
+ }
259
+
260
+ hunter .detach (action );
261
+ pausedActions .put (action .getTarget (), action );
262
+ if (loggingEnabled ) {
263
+ log (OWNER_DISPATCHER , VERB_PAUSED , action .request .logId (),
264
+ "because tag '" + tag + "' was paused" );
265
+ }
266
+ }
267
+ }
268
+
269
+ // Check if the hunter can be cancelled in case all its requests
270
+ // had the tag being paused here.
271
+ if (hunter .cancel ()) {
272
+ it .remove ();
273
+ if (loggingEnabled ) {
274
+ log (OWNER_DISPATCHER , VERB_CANCELED , getLogIdsForHunter (hunter ), "all actions paused" );
275
+ }
276
+ }
277
+ }
278
+ }
279
+
280
+ void performResumeTag (Object tag ) {
281
+ // Trying to resume a tag that is not paused.
282
+ if (!pausedTags .remove (tag )) {
283
+ return ;
284
+ }
285
+
286
+ List <Action > batch = null ;
287
+ for (Iterator <Action > i = pausedActions .values ().iterator (); i .hasNext ();) {
288
+ Action action = i .next ();
289
+ if (action .getTag ().equals (tag )) {
290
+ if (batch == null ) {
291
+ batch = new ArrayList <Action >();
292
+ }
293
+ batch .add (action );
294
+ i .remove ();
295
+ }
296
+ }
297
+
298
+ if (batch != null ) {
299
+ mainThreadHandler .sendMessage (mainThreadHandler .obtainMessage (REQUEST_BATCH_RESUME , batch ));
300
+ }
301
+ }
302
+
187
303
void performRetry (BitmapHunter hunter ) {
188
304
if (hunter .isCancelled ()) return ;
189
305
@@ -350,6 +466,16 @@ public DispatcherHandler(Looper looper, Dispatcher dispatcher) {
350
466
dispatcher .performCancel (action );
351
467
break ;
352
468
}
469
+ case TAG_PAUSE : {
470
+ Object tag = msg .obj ;
471
+ dispatcher .performPauseTag (tag );
472
+ break ;
473
+ }
474
+ case TAG_RESUME : {
475
+ Object tag = msg .obj ;
476
+ dispatcher .performResumeTag (tag );
477
+ break ;
478
+ }
353
479
case HUNTER_COMPLETE : {
354
480
BitmapHunter hunter = (BitmapHunter ) msg .obj ;
355
481
dispatcher .performComplete (hunter );
0 commit comments