Здесь критическая точка, которую я не знаю о поведении Пикассо.
Представьте, что вы, скажем, показываете слайд-шоу из десяти предметов. Скажем, они отображаются на экране каждые десять секунд.
Идеальное поведение было бы следующим: в начале слайд-шоу я просто выполняю следующее:
picasso.get( url1 )
picasso.get( url2 )
picasso.get( url3 )
picasso.get( url4 )
picasso.get( url5 )
picasso.get( url6 )
picasso.get( url7 )
picasso.get( url8 )
picasso.get( url9 )
picasso.get( url10 )
И, на самом деле, Пикассо будет делать те один за раз, в очереди.
Каково поведение Пикассо, если я скажу ему предварительно разогреть 10 URL-адресов одновременно?
Возможно ли, чтобы Пикассо делал вещи только по одному, по порядку - есть ли такой вариант?
(Возникают другие вопросы, можете ли вы отменить очередь или??)
Fresco
благодаря удивительному ответу @alicanozkara на этой странице я впервые узнал о
https://github.com/facebook/fresco
(звезды 13k) для лучшего или худшего, я думаю, что эпоха Пикассо, вероятно, закончилась.
Ответ 1
Используя только Picasso
, я думаю, что вы можете достичь:
1) Загрузите все изображения асинхронно в кеш с помощью fetch()
следующим образом:
Picasso.with(context).load(URL).fetch();
Вы также можете добавить приоритет для изображений, которые хотите загрузить раньше: (Возможно, упоминайте высокий приоритет для первых изображений слайда)
Picasso.with(context)
.load(URL)
.priority(Picasso.Priority.HIGH) // Default priority is medium
.fetch();
2). Отменяя очередь, вы можете добавить общий tag()
к своим изображениям, и вы можете приостановить/отменить/возобновить в любое время!
private static final Object TAG_OBJECT = Object();
Picasso.with(context)
.load(URL)
.tag(TAG_OBJECT)
// can be any Java object, must be the same object for all requests you want to control together.
Затем мы можем управлять тегом следующим образом:
Picasso.with(context)
.pauseTag(TAG_OBJECT)
//.resumeTag(TAG_OBJECT)
//.cancelTag(TAG_OBJECT)
3) Еще одна важная вещь, которую я хотел бы предложить, - это когда вы предварительно загружаете свои изображения, сохраняете их только в своем кеш-диске и загружаете их в свой кеш-память только при отображении, Это предотвратит удаление других важных изображений из кеша памяти:
Picasso
.with(context)
.load(URL)
.memoryPolicy(MemoryPolicy.NO_STORE) //Skips storing the final result into memory cache.
.fetch()
4) Для последовательной загрузки ваших изображений в очередь вы можете передать свой собственный ExecutorService
(SingleThreadExecutor
в вашем случае) с помощью метода executor(ExecutorService)
, присутствующего в Picasso.Builder
Вы даже можете изменить размер дискового кэша с помощью метода downloader(Downloader)
и кеша вашей памяти с помощью метода memoryCache(Cache)
, найденного в классе Picasso.Builder
.
Другие удивительные библиотеки:
Glide
Fresco
Ответ 2
Возможно ли, чтобы Пикассо делал вещи только по одному, по порядку - есть ли такой вариант?
Я не уверен, что это можно сделать с самим Пикассо, но по крайней мере RxJava может быть применим к этой проблеме.
Я опубликую фрагмент кода с комментариями:
public class MainActivity extends AppCompatActivity {
public static final List<String> urlList = Arrays.asList(
"http://i.imgur.com/UZFOMzL.jpg",
"http://i.imgur.com/H981AN7.jpg",
"http://i.imgur.com/nwhnRsZ.jpg",
"http://i.imgur.com/MU2dD8E.jpg"
);
List<Target> targetList = new ArrayList<>();
List<Completable> completables = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final long start = System.currentTimeMillis();
// emit each url separately
Observable.fromIterable(urlList)
// flatmap to an Observable<Completable>
.flatMap(url ->
// fromCallable ensures that this stream will emit value as soon as it is subscribed
// Contrary to this, Observable.just() would emit immediately, which we do not want
Observable.fromCallable(() ->
// We need to know whether either download is
// completed or no, thus we need a Completable
Completable.create(e -> {
Target target = new Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
Log.i("vvv", "downloaded " + url + ", " + (System.currentTimeMillis() - start));
e.onComplete();
}
@Override
public void onBitmapFailed(Drawable errorDrawable) {
e.onError(new IllegalArgumentException("error happened"));
}
@Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
// need to keep a strong reference to Target, because Picasso holds weak reference
targetList.add(target);
Picasso.with(MainActivity.this)
.load(url)
.into(target);
})))
// collecting all Completables into a list
.collectInto(completables, List::add)
// flatmap-ing this Observable into a Completable, concatenating each completable
// to next, thus they will be downloaded in order
.flatMapCompletable(Completable::concat)
// clearing the strong reference we retained earlier
.doFinally(() -> {
targetList.clear();
targetList = null;
})
.subscribe(
() -> Log.i("vvv", "done: " + (System.currentTimeMillis() - start)),
throwable -> Log.e("vvv", "err " + throwable.getMessage()
));
}
}
Это будет выход в logcat:
![введите описание изображения здесь]()
Это идеальный сценарий, когда каждое изображение успешно загружается. Этот фрагмент не обрабатывает случай, когда одно из изображений не может быть загружено. Как только Picasso не сможет загрузить один из них - поток будет прерван и будет вызываться onError()
.
Ответ 3
В Picasso.Builder вы можете указать конкретный ExecutorService: https://square.github.io/picasso/2.x/picasso/com/squareup/picasso/Picasso.Builder.html#executor-java.util.concurrent.ExecutorService-
Если вы дадите новый исполнитель одного потока, https://developer.android.com/reference/java/util/concurrent/Executors.html#newSingleThreadExecutor(), Picasso будет загружать все ваши изображения по одному за раз.
Ответ 4
Нет никакого решения для одного вызова метода, связанного с Picasso, но вы могли бы создать помощник, как показано ниже:
public PicassoSlideshow {
private static PicassoSlideshow instance;
private WeakReference<ImageView> view;
private Handler handler;
private int index;
private String[] urls;
private long delay;
private PicassoSlideshow() {
//nothing
}
public static PicassoSlideshow with(ImageView view) {
if (instance == null) {
instance = new PicassoSlideshow();
}
instance.setView(view);
}
private void setView(ImageView view) {
this.view = new WeakReference<>(view);
}
//Note: I'm only suggesting varargs because that what you seem to have in the question
public void startSlideshow(long interval, String... urls) {
if (handler == null) {
handler = new Handler();
}
index = 0;
this.urls = urls;
delay = interval;
displayNextSlide();
}
private void displayNextSlide() {
//display one
ImageView iv = view.get();
if (iv != null) {
Picasso.with(iv.getContext())
.load(urls[index]).into(iv);
index++;
if (index < urls.length) {
//preload next
Picasso.with(iv.getContext()).fetch(urls[index]);
//on timer switch images
handler.postDelayed(PicassoSlideshow::displayNextSlide, delay);
}
}
}
}
Использование:
PicassoSlideshow.with(view).startSlideshow(10000, url1, url2, url3, url9);
Обратите внимание, что я только что написал это с самого начала, пока моя IDE делает недействительными свои кеши, поэтому вам может понадобиться немного настроить его