毕加索可以帮我排队吗?

Can Picasso queue for me?

这是关于毕加索行为的一个关键点,我不知道。

假设您正在放映包含十个项目的幻灯片。比如说,他们每人在屏幕上停留十秒钟。

理想的行为是这样的:在幻灯片放映开始时,我只需执行以下操作:

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 )

而且,事实上,毕加索会在队列中一次一个

如果我告诉 Picasso 一次预热 10 个 url,它的行为是什么?

是否可以让毕加索按顺序一次只做一件事情 - 有这样的选择吗?

(另外一个问题是,你能不能取消排队,或者...?)


壁画

多亏了@alicanozkara 在此页面上的精彩回答,我才第一次了解到

https://github.com/facebook/fresco

(13k 星)无论好坏我认为毕加索时代可能已经结束了。

Is it possible to have Picasso do things only one at a time, in order - is there such an option?

我不确定 Picasso 本身是否可以完成,但至少 RxJava 可能适用于此问题。

我将 post 一段带有注释的代码:

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()

仅使用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(),你可以随时pause/cancel/resume!

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) 要在队列中顺序加载图像,您可以使用 [=20 传递自己的 ExecutorService(在您的情况下为 SingleThreadExecutor) =] 方法,存在于 Picasso.Builder

您甚至可以使用 downloader(Downloader) 方法更改磁盘缓存的大小,使用 memoryCache(Cache) 方法更改内存缓存的大小,两者都可以在 Picasso.Builder class.

其他很棒的库:

Glide

Fresco

没有与 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's 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 使它的缓存无效时从头开始写的,所以您可能需要稍微调整一下

在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 将一次下载你所有的图像。