毕加索可以为我排队吗?
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()
更早成功?以上是关于毕加索可以为我排队吗?的主要内容,如果未能解决你的问题,请参考以下文章