毕加索可以为我排队吗?

Posted

技术标签:

【中文标题】毕加索可以为我排队吗?【英文标题】:Can Picasso queue for me? 【发布时间】:2018-02-18 07:01:47 【问题描述】:

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

假设您正在展示一个包含十个项目的幻灯片。比如说,他们每个人都在屏幕上显示十秒钟。

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

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 个网址,毕加索的行为是什么?

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

(出现的其他问题是,您可以取消队列,还是...?)


壁画

感谢@alicanozkara 在此页面上的惊人回答,我第一次了解到

https://github.com/facebook/fresco

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

【问题讨论】:

我不是毕加索专家,但我在徘徊。计时器不能解决您的问题吗? developer.android.com/reference/java/util/Timer.html 嘿@sabsab,不,这与问题无关 我认为纯粹使用 Picasso 是不可能的,但下面的 RxJava 答案是解决问题的好方法。 【参考方案1】:

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

我不确定Picasso本身是否可以做到,但至少RxJava可能适用于这个问题。

我将发布一段带有 cmets 的 sn-p 代码:

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 中的输出:

这是理想的情况,当每个图像都成功加载时。当无法加载其中一个图像时,此 sn-p 不处理这种情况。一旦 Picasso 无法加载其中一个 - 流将被中断并调用 onError()

【讨论】:

虽然这非常令人钦佩并且值得 1000 票,但我相信下面的 Gut 已经在毕加索内部给出了答案? @Fattie,非常接近真实,期待时间方面,我在comment 中提到过。 .flatMap 替换为.concatMap【参考方案2】:

在 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(),毕加索将一次下载你所有的图像。

【讨论】:

如果我没记错的话,这就是答案! 我测试了this 并得到了this 输出:保留了顺序,但时间很有趣:有4个资源在同一时间下载,这是不可能的.似乎有一个窗口,其中 resourceN 与 resourceN-1 同时下载。 @Fattie 我已经在我的回答中提到了ExecutorService 部分! 这在更大的数据集上看起来甚至很奇怪 - see here,其中 rx 方法给出了有意义的输出 - see here。 @Fattie 谢谢! :D 我在现场项目中使用了几乎所有著名的图像加载库。 Fresco 不错,但它有最多的方法 ~14k,Picasso 是最轻量级的,它的工作非常整洁!我建议您先定义用例,然后再决定使用哪个库! :) 随意讨论任何你想讨论的事情! :)【参考方案3】:

仅使用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) 为了在队列中顺序加载图像,您可以使用executor(ExecutorService) 方法传递自己的ExecutorService(在您的情况下为SingleThreadExecutor),在Picasso.Builder 中提供 p>

您甚至可以使用downloader(Downloader) 方法更改磁盘缓存的大小,使用memoryCache(Cache) 方法更改您的内存缓存,这两种方法都可以在Picasso.Builder 类中找到。

其他很棒的库:

Glide

Fresco

【讨论】:

看起来不错的答案值得一百万个赞 @Fattie btw,你也应该看看 glide 库!大多数在 IO 会议上展示的谷歌应用程序都使用它! :) @Fattie 您能否也将其标记为正确以帮助未来的读者。【参考方案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'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.with(iv.getContext()).load(urls[index]).into(iv) 会比下一个 displayNextSlide() 更早成功?

以上是关于毕加索可以为我排队吗?的主要内容,如果未能解决你的问题,请参考以下文章

使毕加索中的缓存无效

有了RecyclerView,毕加索还需要吗?

毕加索加载横向图像但不将纵向加载到标记的信息窗口中

该应用程序崩溃使用毕加索和发送空白图片

如何清理毕加索的内存缓存?

MVC电影毕加索的艺术探讨