Skip to content

Commit 547ebdb

Browse files
committed
Merge pull request square#665 from lucasr/request-group
Implement request group and pause/resume support
2 parents c21d17f + 1582094 commit 547ebdb

25 files changed

+506
-47
lines changed

picasso-sample/src/main/java/com/example/picasso/PicassoSampleActivity.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import android.widget.ListView;
1111
import android.widget.ToggleButton;
1212

13+
import com.squareup.picasso.Picasso;
14+
1315
import static android.view.View.GONE;
1416
import static android.view.View.VISIBLE;
1517

@@ -40,6 +42,12 @@ public void onItemClick(AdapterView<?> adapterView, View view, int position, lon
4042
});
4143
}
4244

45+
@Override
46+
protected void onDestroy() {
47+
super.onDestroy();
48+
Picasso.with(this).cancelTag(this);
49+
}
50+
4351
@Override public void onBackPressed() {
4452
if (showHide.isChecked()) {
4553
showHide.setChecked(false);

picasso-sample/src/main/java/com/example/picasso/SampleContactsActivity.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public class SampleContactsActivity extends PicassoSampleActivity
4242

4343
ListView lv = (ListView) findViewById(android.R.id.list);
4444
lv.setAdapter(adapter);
45+
lv.setOnScrollListener(new SampleScrollListener(this));
4546

4647
getSupportLoaderManager().initLoader(ContactsQuery.QUERY_ID, null, this);
4748
}

picasso-sample/src/main/java/com/example/picasso/SampleContactsAdapter.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public SampleContactsAdapter(Context context) {
6161
Picasso.with(context)
6262
.load(contactUri)
6363
.placeholder(R.drawable.contact_picture_placeholder)
64+
.tag(context)
6465
.into(holder.icon);
6566
}
6667

picasso-sample/src/main/java/com/example/picasso/SampleGridViewActivity.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ public class SampleGridViewActivity extends PicassoSampleActivity {
1010

1111
GridView gv = (GridView) findViewById(R.id.grid_view);
1212
gv.setAdapter(new SampleGridViewAdapter(this));
13+
gv.setOnScrollListener(new SampleScrollListener(this));
1314
}
1415
}

picasso-sample/src/main/java/com/example/picasso/SampleGridViewAdapter.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public SampleGridViewAdapter(Context context) {
4444
.placeholder(R.drawable.placeholder) //
4545
.error(R.drawable.error) //
4646
.fit() //
47+
.tag(context) //
4748
.into(view);
4849

4950
return view;

picasso-sample/src/main/java/com/example/picasso/SampleListDetailActivity.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public static ListFragment newInstance() {
4343
ListView listView = (ListView) LayoutInflater.from(activity)
4444
.inflate(R.layout.sample_list_detail_list, container, false);
4545
listView.setAdapter(adapter);
46+
listView.setOnScrollListener(new SampleScrollListener(activity));
4647
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
4748
@Override
4849
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
@@ -83,6 +84,7 @@ public static DetailFragment newInstance(String url) {
8384
Picasso.with(activity)
8485
.load(url)
8586
.fit()
87+
.tag(activity)
8688
.into(imageView);
8789

8890
return view;

picasso-sample/src/main/java/com/example/picasso/SampleListDetailAdapter.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public SampleListDetailAdapter(Context context) {
4545
.error(R.drawable.error)
4646
.resizeDimen(R.dimen.list_detail_image_size, R.dimen.list_detail_image_size)
4747
.centerInside()
48+
.tag(context)
4849
.into(holder.image);
4950

5051
return view;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.example.picasso;
2+
3+
import android.content.Context;
4+
import android.widget.AbsListView;
5+
6+
import com.squareup.picasso.Picasso;
7+
8+
public class SampleScrollListener implements AbsListView.OnScrollListener {
9+
private final Context context;
10+
11+
public SampleScrollListener(Context context) {
12+
this.context = context;
13+
}
14+
15+
@Override
16+
public void onScrollStateChanged(AbsListView view, int scrollState) {
17+
final Picasso picasso = Picasso.with(context);
18+
if (scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_TOUCH_SCROLL) {
19+
picasso.resumeTag(context);
20+
} else {
21+
picasso.pauseTag(context);
22+
}
23+
}
24+
25+
@Override
26+
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
27+
int totalItemCount) {
28+
// Do nothing.
29+
}
30+
}

picasso/src/main/java/com/squareup/picasso/Action.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,13 @@ public RequestWeakReference(Action action, T referent, ReferenceQueue<? super T>
3939
final int errorResId;
4040
final Drawable errorDrawable;
4141
final String key;
42+
final Object tag;
4243

4344
boolean willReplay;
4445
boolean cancelled;
4546

4647
Action(Picasso picasso, T target, Request request, boolean skipCache, boolean noFade,
47-
int errorResId, Drawable errorDrawable, String key) {
48+
int errorResId, Drawable errorDrawable, String key, Object tag) {
4849
this.picasso = picasso;
4950
this.request = request;
5051
this.target = new RequestWeakReference<T>(this, target, picasso.referenceQueue);
@@ -53,6 +54,7 @@ public RequestWeakReference(Action action, T referent, ReferenceQueue<? super T>
5354
this.errorResId = errorResId;
5455
this.errorDrawable = errorDrawable;
5556
this.key = key;
57+
this.tag = (tag != null ? tag : this);
5658
}
5759

5860
abstract void complete(Bitmap result, Picasso.LoadedFrom from);
@@ -90,4 +92,8 @@ Picasso getPicasso() {
9092
Priority getPriority() {
9193
return request.priority;
9294
}
95+
96+
Object getTag() {
97+
return tag;
98+
}
9399
}

picasso/src/main/java/com/squareup/picasso/Dispatcher.java

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@
2727
import android.os.Looper;
2828
import android.os.Message;
2929
import java.util.ArrayList;
30+
import java.util.HashSet;
3031
import java.util.Iterator;
3132
import java.util.LinkedHashMap;
3233
import java.util.List;
3334
import java.util.Map;
35+
import java.util.Set;
3436
import java.util.WeakHashMap;
3537
import java.util.concurrent.ExecutorService;
3638

@@ -45,6 +47,7 @@
4547
import static com.squareup.picasso.Utils.VERB_DELIVERED;
4648
import static com.squareup.picasso.Utils.VERB_ENQUEUED;
4749
import static com.squareup.picasso.Utils.VERB_IGNORED;
50+
import static com.squareup.picasso.Utils.VERB_PAUSED;
4851
import static com.squareup.picasso.Utils.VERB_REPLAYING;
4952
import static com.squareup.picasso.Utils.VERB_RETRYING;
5053
import static com.squareup.picasso.Utils.getLogIdsForHunter;
@@ -67,6 +70,9 @@ class Dispatcher {
6770
static final int HUNTER_BATCH_COMPLETE = 8;
6871
static final int NETWORK_STATE_CHANGE = 9;
6972
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;
7076

7177
private static final String DISPATCHER_THREAD_NAME = "Dispatcher";
7278
private static final int BATCH_DELAY = 200; // ms
@@ -77,6 +83,8 @@ class Dispatcher {
7783
final Downloader downloader;
7884
final Map<String, BitmapHunter> hunterMap;
7985
final Map<Object, Action> failedActions;
86+
final Map<Object, Action> pausedActions;
87+
final Set<Object> pausedTags;
8088
final Handler handler;
8189
final Handler mainThreadHandler;
8290
final Cache cache;
@@ -95,6 +103,8 @@ class Dispatcher {
95103
this.service = service;
96104
this.hunterMap = new LinkedHashMap<String, BitmapHunter>();
97105
this.failedActions = new WeakHashMap<Object, Action>();
106+
this.pausedActions = new WeakHashMap<Object, Action>();
107+
this.pausedTags = new HashSet<Object>();
98108
this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
99109
this.downloader = downloader;
100110
this.mainThreadHandler = mainThreadHandler;
@@ -121,6 +131,14 @@ void dispatchCancel(Action action) {
121131
handler.sendMessage(handler.obtainMessage(REQUEST_CANCEL, action));
122132
}
123133

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+
124142
void dispatchComplete(BitmapHunter hunter) {
125143
handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));
126144
}
@@ -143,6 +161,15 @@ void dispatchAirplaneModeChange(boolean airplaneMode) {
143161
}
144162

145163
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+
146173
BitmapHunter hunter = hunterMap.get(action.getKey());
147174
if (hunter != null) {
148175
hunter.attach(action);
@@ -178,12 +205,101 @@ void performCancel(Action action) {
178205
}
179206
}
180207
}
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+
181217
Action remove = failedActions.remove(action.getTarget());
182218
if (remove != null && remove.getPicasso().loggingEnabled) {
183219
log(OWNER_DISPATCHER, VERB_CANCELED, remove.getRequest().logId(), "from replaying");
184220
}
185221
}
186222

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+
187303
void performRetry(BitmapHunter hunter) {
188304
if (hunter.isCancelled()) return;
189305

@@ -350,6 +466,16 @@ public DispatcherHandler(Looper looper, Dispatcher dispatcher) {
350466
dispatcher.performCancel(action);
351467
break;
352468
}
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+
}
353479
case HUNTER_COMPLETE: {
354480
BitmapHunter hunter = (BitmapHunter) msg.obj;
355481
dispatcher.performComplete(hunter);

picasso/src/main/java/com/squareup/picasso/FetchAction.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
import android.graphics.Bitmap;
1919

2020
class FetchAction extends Action<Void> {
21-
FetchAction(Picasso picasso, Request data, boolean skipCache, String key) {
22-
super(picasso, null, data, skipCache, false, 0, null, key);
21+
FetchAction(Picasso picasso, Request data, boolean skipCache, String key, Object tag) {
22+
super(picasso, null, data, skipCache, false, 0, null, key, tag);
2323
}
2424

2525
@Override void complete(Bitmap result, Picasso.LoadedFrom from) {

picasso/src/main/java/com/squareup/picasso/GetAction.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
import android.graphics.Bitmap;
1919

2020
class GetAction extends Action<Void> {
21-
GetAction(Picasso picasso, Request data, boolean skipCache, String key) {
22-
super(picasso, null, data, skipCache, false, 0, null, key);
21+
GetAction(Picasso picasso, Request data, boolean skipCache, String key, Object tag) {
22+
super(picasso, null, data, skipCache, false, 0, null, key, tag);
2323
}
2424

2525
@Override void complete(Bitmap result, Picasso.LoadedFrom from) {

picasso/src/main/java/com/squareup/picasso/ImageViewAction.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ class ImageViewAction extends Action<ImageView> {
2525
Callback callback;
2626

2727
ImageViewAction(Picasso picasso, ImageView imageView, Request data, boolean skipCache,
28-
boolean noFade, int errorResId, Drawable errorDrawable, String key, Callback callback) {
29-
super(picasso, imageView, data, skipCache, noFade, errorResId, errorDrawable, key);
28+
boolean noFade, int errorResId, Drawable errorDrawable, String key, Object tag,
29+
Callback callback) {
30+
super(picasso, imageView, data, skipCache, noFade, errorResId, errorDrawable, key, tag);
3031
this.callback = callback;
3132
}
3233

0 commit comments

Comments
 (0)