毕加索可以帮我排队吗?
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.
其他很棒的库:
没有与 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 将一次下载你所有的图像。
这是关于毕加索行为的一个关键点,我不知道。
假设您正在放映包含十个项目的幻灯片。比如说,他们每人在屏幕上停留十秒钟。
理想的行为是这样的:在幻灯片放映开始时,我只需执行以下操作:
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.
其他很棒的库:
没有与 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 将一次下载你所有的图像。