Может ли Пикассо оказаться в очереди для меня?

Здесь критическая точка, которую я не знаю о поведении Пикассо.

Представьте, что вы, скажем, показываете слайд-шоу из десяти предметов. Скажем, они отображаются на экране каждые десять секунд.

Идеальное поведение было бы следующим: в начале слайд-шоу я просто выполняю следующее:

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 делает недействительными свои кеши, поэтому вам может понадобиться немного настроить его