Picasso
背景:picasso是Square公司出的一款图片加载框架,能够解决我们在Android开发中加载图片时遇到的诸多问题,比如OOM,图片错位等,问题主要集中在加载图片列表时,因为单张图片加载谁都会写。如果我们想在ListView或者GridView或者RecyclerView中加载图片墙,那么这个时候对原图片的二次处理就显得非常重要了,否则就会出现我们上文说的OOM或者图片错位等。不过,如果你使用了Picasso来加载图片的话,那么所有问题都会变得很简单。
基本使用
- 加载网络图片
Picasso.with(this).load("http://n.sinaimg.cn/translate/20160819/9BpA-fxvcsrn8627957.jpg").into(iv);
- 对网络图片裁剪
Picasso.with(this).load("http://n.sinaimg.cn/translate/20160819/9BpA-fxvcsrn8627957.jpg")
.resize(200,200)
.into(iv);
//注意这里的200表示200px,如果你想在resize时指定dp,可以使用如下方法
Picasso.with(this).load("http://n.sinaimg.cn/translate/20160819/9BpA-fxvcsrn8627957.jpg")
.resizeDimen(R.dimen.iv_width,R.dimen.iv_height)
.into(iv);
- 缩放模式
Picasso.with(this).load("http://n.sinaimg.cn/translate/20160819/9BpA-fxvcsrn8627957.jpg")
.resizeDimen(R.dimen.iv_width,R.dimen.iv_height)
.centerCrop()
.into(iv);
- 默认图
Picasso.with(this).load("http://n.sinaimg.cn/translate/20160819/9BpA-fxvcsrn8627957.jpg")
//占位图,图片加载出来之前显示的默认图片
.placeholder(R.mipmap.ic_launcher)
//错误图,图片加载出错时显示的图片
.error(R.mipmap.ic_launcher)
.into(iv);
- 裁剪,高斯模糊
Picasso.with(this).load("http://n.sinaimg.cn/translate/20160819/9BpA-fxvcsrn8627957.jpg")
.transform(transformation)
.into(iv);
- 查看图片从哪里加载
Picasso picasso = Picasso.with(this);
//开启指示器
picasso.setIndicatorsEnabled(true);
picasso.load("http://n.sinaimg.cn/translate/20160819/9BpA-fxvcsrn8627957.jpg")
.into(iv);
缓存
默认缓存位置:data/data/packagename/cache
缓存方式:Disk 和 Memory
缓存策略:
NO_CACHE:表示处理请求的时候跳过检查内存缓存
NO_STORE: 表示请求成功之后,不将最终的结果存到内存
with(this).load(URL)
.placeholder(R.drawable.default_bg)
.error(R.drawable.error_iamge)
.memoryPolicy(MemoryPolicy.NO_CACHE,MemoryPolicy.NO_STORE) //禁止内存缓存
.into(mBlurImage);
磁盘缓存:
NO_CACHE: 表示处理请求的时候跳过处理磁盘缓存
NO_STORE: 表示请求成功后,不将结果缓存到Disk,但是这个只对OkHttp有效。
OFFLINE: 这个就跟 上面两个不一样了,如果networkPolicy方法用的是这个参数,那么
Picasso会强制这次请求从缓存中获取结果,不会发起网络请求,不管缓存中能否获取到结果。
with(this).load(URL)
.placeholder(R.drawable.default_bg)
.error(R.drawable.error_iamge)
.memoryPolicy(MemoryPolicy.NO_CACHE,MemoryPolicy.NO_STORE)//跳过内存缓存
.networkPolicy(NetworkPolicy.NO_CACHE)//跳过磁盘缓存
.into(mBlurImage);
默认缓存位置:
static Downloader createDefaultDownloader(Context context) {
try {
Class.forName("com.squareup.okhttp.OkHttpClient");
return OkHttpLoaderCreator.create(context);
} catch (ClassNotFoundException ignored) {
}
return new UrlConnectionDownloader(context);
}
自定义下载器
Picasso picasso = new Picasso.Builder(this)
.downloader(new OkHttp3Downloader(this.getExternalCacheDir()))
.build();
Picasso.setSingletonInstance(picasso);
picasso.load("http://n.sinaimg.cn/translate/20160819/9BpA-fxvcsrn8627957.jpg").into(iv);
Tag管理请求
cancelTag(Object tag) 取消设置了给定tag的所有请求
pauseTag(Object tag) 暂停设置了给定tag 的所有请求
resumeTag(Object tag) resume 被暂停的给定tag的所有请求
Adapter中添加如下代码:
Picasso.with(this).load(mData.get(position))
.placeholder(R.drawable.default_bg)
.error(R.drawable.error_iamge)
.tag("PhotoTag")
.into(holder.mImageView);
Activity中为RecyclerView添加滑动监听
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
final Picasso picasso = Picasso.with(MainActivity.this);
if (newState == SCROLL_STATE_IDLE) {
picasso.resumeTag("PhotoTag");
} else {
picasso.pauseTag("PhotoTag");
}
}
});
Activity结束的时候,清除tag
@Override
protected void onDestroy() {
super.onDestroy();
Picasso.with(this).cancelTag("PhotoTag");
}
线程池
PicassoExecutorService:默认3个线程
也可以自定义线程池
源码分析
通过Builder模式创建实例
1.Picasso.Builder build
/** Start building a new {@link Picasso} instance. */
public Builder(Context context) {
if (context == null) {
throw new IllegalArgumentException("Context must not be null.");
}
this.context = context.getApplicationContext();
}
private Downloader downloader;
private ExecutorService service;
private Cache cache;
private Listener listener;
private RequestTransformer transformer;
private List<RequestHandler> requestHandlers;
private Bitmap.Config defaultBitmapConfig;
/** Create the {@link Picasso} instance. */
public Picasso build() {
Context context = this.context;
// 为这一系列变量进行默认初始化
// Downloader执行实际的下载业务,返回Response
if (downloader == null) {
downloader = Utils.createDefaultDownloader(context);
}
// 内存缓存,可以看到默认的是LruCache
if (cache == null) {
cache = new LruCache(context);
}
// 线程池,执行网络请求的地方
if (service == null) {
service = new PicassoExecutorService();
}
// request发送前进行处理
if (transformer == null) {
transformer = RequestTransformer.IDENTITY;
}
// 用以统计
Stats stats = new Stats(cache);
// 进行Request以及Response的转发
Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
// 创建一个Picasso
return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
}
2. Picasso#load
public RequestCreator load(String path) {
if (path == null) {
return new RequestCreator(this, null, 0);
}
if (path.trim().length() == 0) {
throw new IllegalArgumentException("Path must not be empty.");
}
return load(Uri.parse(path));
}
public RequestCreator load(Uri uri) {
return new RequestCreator(this, uri, 0);
}
这里创建一个RequestCreator
private final Picasso picasso;
private final Request.Builder data;
RequestCreator(Picasso picasso, Uri uri, int resourceId) {
if (picasso.shutdown) {
throw new IllegalStateException(
"Picasso instance already shut down. Cannot submit new requests.");
}
this.picasso = picasso;
this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
}
data对应创建一个Request的Builder,这个Request中封装了了相应的请求信息,传入了请求加载图片的URI,已经resourceId,以及默认显示图片的配置信息。
进而一般调用into将图片加载到相应的控件中。
RequestCreator#into
// RequestCreator.java
public void into(ImageView target) {
into(target, null);
}
public void into(ImageView target, Callback callback) {
long started = System.nanoTime();
// 盘算是否是在主线程,如果不是则会抛出异常
checkMain();
if (target == null) {
throw new IllegalArgumentException("Target must not be null.");
}
// 判断reqeust是否合法,即存在URI或者对应的resId,否则会取消该请求
if (!data.hasImage()) {
picasso.cancelRequest(target);
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
return;
}
// 判断是否需要延期执行
if (deferred) {
// 判断是否已经设置了宽高大小
if (data.hasSize()) {
throw new IllegalStateException("Fit cannot be used with resize.");
}
// 获取目标控件的宽高参数
int width = target.getWidth();
int height = target.getHeight();
// 表示当前控件并未加载到界面上(宽或高为0)
if (width == 0 || height == 0) {
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
// 生成DeferredRequestCreator,加入相应队列进行处理
picasso.defer(target, new DeferredRequestCreator(this, target, callback));
return;
}
// 设置Request.Builder中的宽高参数大小
data.resize(width, height);
}
// 创建Request
Request request = createRequest(started);
// 获取request对应的key
String requestKey = createKey(request);
// 根据策略判断是够需要跳过读取MemoryCache
if (shouldReadFromMemoryCache(memoryPolicy)) { // 尝试从MemoryCache中获取Bitmap
// 根据Requeskey来获取相应的Bitmap
Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
if (bitmap != null) {
// 如果从MemoryCache中获取到相应的bitmap,则取消request
picasso.cancelRequest(target);
// 设置图片
setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
if (picasso.loggingEnabled) {
log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);
}
// 调用回调callback的onSuccess函数
if (callback != null) {
callback.onSuccess();
}
return;
}
}
// 如果从MemoryCache获取图片失败,或者根据缓存策略直接跳过读取MemoryCache,则设置默认图片
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
// 创建ImageViewAction,Action里面包含了一次请求所需要的所有信息
Action action =
new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
errorDrawable, requestKey, tag, callback, noFade);
// 将action入队列
picasso.enqueueAndSubmit(action);
}
附一张流程图:
相关文章:
http://blog.csdn.net/woliuyunyicai/article/details/51417839