图片加载库 Picasso 的使用与部分源码分析

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);
}

附一张流程图:
Picasso构造流程图

相关文章:
http://blog.csdn.net/woliuyunyicai/article/details/51417839

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值