Android 图片加载框架Picasso

一:介绍
Picasso 是Square 公司开源的Android 端的图片加载和缓存框架
1.添加依赖:目前github上的版本2.7
image.png
在app下的build.gradle下添加依赖

implementation 'com.squareup.picasso:picasso:2.5.2'

二:使用
1.基本使用

Picasso.with(this).load(url).placeholder(R.mipmap.ic_launcher).into(imageView);
//with下传递的是Context
//load下有4个重载方法,传递分别是url加载一个以Uri路径的图片,File加载File中图片,String ,resourceId加载本地资源的图片
//placeholder是加载一个占位图
//into加载到控件中,也可以是实现了Target j接口的自定义View。

image.png
into方法的一些源码

public void into(Target target) {
    long started = System.nanoTime();
    //检查线程
    //先检查主否在主线程运行,如果不是在主线程就会抛出一个应该在主线程运行的异常
    checkMain();

    if (target == null) {
      throw new IllegalArgumentException("Target must not be null.");
    }
    if (deferred) {
      throw new IllegalStateException("Fit cannot be used with a Target.");
    }
//判断是否加载url
//data.hasImage():这里的data是在之前初始化的Request.Builder对象,它里面包含url地址,resourceId和默认配置,这里是判断uri或resourceId是否为空为0,如果是的话就取消imageview的请求:picasso.cancelRequest(target);
    if (!data.hasImage()) {
      picasso.cancelRequest(target);
      target.onPrepareLoad(setPlaceholder ? getPlaceholderDrawable() : null);
      return;
    }

2.占位符placeholder,error,noPlaceholder,noFade
们的项目中通常最常用的就是加载网络图片,但是由于网络环境的差异,有时侯加载网络图片的过程有点慢,这样界面上就会显示空ImageView什么也看不见,用户体验非常不好
placeholder
提供一张在网络请求还没请求完成时显示的图片,必须是本地图片

 Picasso.with(this).load(url).placeholder(R.mipmap.ic_launcher).into(imageView);

error
error 提供一张在加载图片出错的情况下显示的默认图

Picasso.with(this).load(url).error(R.mipmap.ic_launcher).into(imageView);

noPlaceholder
在调用into的时候明确告诉你没有占位图设置。根据这个方法签名的解释是阻止View被回收的时候Picasso清空target或者设置一个应用的占位图。需要注意的是placeholder和noPlaceholder 不能同时应用在同一个请求上,会抛异常。

 Picasso.with(this).load(url).noPlaceholder().error(R.mipmap.ic_launcher).into(imageView);

noFade
无论你是否设置了占位图,Picasso 从磁盘或者网络加载图片时,into 显示到ImageView 都会有一个简单的渐入过度效果,让你的UI视觉效果更柔顺丝滑一点,如果你不要这个渐入的效果,就调用noFade方法。

Picasso.with(this).load(url).noPlaceholder().error(R.mipmap.ic_launcher).noFade().into(imageView);

3.设置图片的尺寸(resize),缩放(scale),裁剪(crop)
resize(int w,int h)
为了带宽、内存使用和下载速度等考虑,服务端给我们的图片的size 应该和我们View 实际的size一样的,但是实际情况并非如此,服务端可能给我们一些奇怪的尺寸的图片,我们可以使用resize(int w,int hei) 来重新设置尺寸。

 Picasso.with(this).load(url).resize(200,200).noPlaceholder().error(R.mipmap.ic_launcher).noFade().into(imageView);

resize()方法接受的参数的单位是pixels,还有一个可以设置dp单位的方法,将你的尺寸写在dimens.xml文件中,然后用resizeDimen(int targetWidthResId, int targetHeightResId)方法

图片裁剪centerCrop()
Picasso 同样给我们提供了一个方法,centerCrop,充满ImageView 的边界,居中裁剪。

    Picasso.with(this).load(url).resize(400,200).noPlaceholder().error(R.mipmap.ic_launcher).noFade().centerCrop().into(imageView);

centerInside
上面的centerCrop是可能看不到全部图片的,如果你想让View将图片展示完全,可以用centerInside,但是如果图片尺寸小于View尺寸的话,是不能充满View边界的。


   Picasso.with(this).load(url).noPlaceholder().error(R.mipmap.ic_launcher).noFade().resize(400,200).centerInside().into(imageView);

fit
fit 是干什的呢?上面我们需要用resize()来指定我们需要的图片的尺寸,那就是说在程序中需要我们计算我们需要的尺寸(固定大小的除外),这样很麻烦,fit 方法就帮我们解决了这个问题。fit 它会自动测量我们的View的大小,然后内部调用reszie方法把图片裁剪到View的大小,这就帮我们做了计算size和调用resize 这2步

       Picasso.with(this).load(url).noPlaceholder().error(R.mipmap.ic_launcher).noFade().resize(400,200).fit().centerInside().into(imageView);

图片旋转rotate()
在图片显示到ImageView 之前,还可以对图片做一些旋转操作,调用rotate(int degree)方法

  Picasso.with(this).load(url).noPlaceholder().error(R.mipmap.ic_launcher).noFade().rotate(180).into(imageView);

rotate(float degrees, float pivotX, float pivotY) 以(pivotX, pivotY)为原点旋转

 Picasso.with(this).load(url).noPlaceholder().error(R.mipmap.ic_launcher).noFade().rotate(180,0,0).into(imageView);

4.转换器Transformation
Transformation 这就是Picasso的一个非常强大的功能了,它允许你在load图片 -> into ImageView 中间这个过成对图片做一系列的变换。比如你要做图片高斯模糊、添加圆角、做度灰处理、圆形图片等等都可以通过Transformation来完成。
使用

 Picasso.with(this).load(url).noPlaceholder().error(R.mipmap.ic_launcher)
 .transform(new BlurTransformation(this))//这里添加了一个转换器实现高斯模糊
 .into(imageView);

那么我们如何实现这个转换器了BlurTransformation

public class BlurTransformation implements Transformation {

    RenderScript rs;

    public BlurTransformation(Context context) {
        super();
        rs = RenderScript.create(context);
    }
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
    @Override
    public Bitmap transform(Bitmap bitmap) {
        // Create another bitmap that will hold the results of the filter.
        Bitmap blurredBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);

        // Allocate memory for Renderscript to work with
        Allocation input = Allocation.createFromBitmap(rs, blurredBitmap, Allocation.MipmapControl.MIPMAP_FULL, Allocation.USAGE_SHARED);
        Allocation output = Allocation.createTyped(rs, input.getType());

        // Load up an instance of the specific script that we want to use.
        ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
        script.setInput(input);

        // Set the blur radius
        script.setRadius(25);

        // Start the ScriptIntrinisicBlur
        script.forEach(output);

        // Copy the output to the blurred bitmap
        output.copyTo(blurredBitmap);

        bitmap.recycle();

        return blurredBitmap;
    }

    @Override
    public String key() {
        return "blur";
    }
}

5.同步/异步加载图片get(),fetch()
同步get()
很简单,同步加载使用get() 方法,返回一个Bitmap 对象

Bitmap bitmap=Picasso.with(this).load(url).get();
//注意:使用同步方法不能放在主线程来做

异步fetch()
一般直接加载图片通过into显示到ImageView 是异步的方式,除此之外,还提供了2个异步的方法:

fetch() 异步方式加载图片
fetch(Callback callback) 异步方式加载图片并给一个回调接口

  Picasso.with(this).load(url).fetch(new Callback() {
            @Override
            public void onSuccess() {
                
            }

            @Override
            public void onError() {

            }
        });

fetch 方法异步加载图片并没有返回Bitmap,这个方法在请求成功之后,将结果存到了缓存,包括磁盘和内存缓存。所以使用这种方式加载图片适用于这种场景:知道稍后会加载图片,使用fetch 先加载缓存,起到一个预加载的效果。
6.缓存(Disk和Memory)
Picasso 有内存缓存(Memory)和磁盘缓存( Disk)
默认情况下,Picasso 内存缓存和磁盘缓存都开启了的,也就是加载图片的时候,内存和磁盘都缓存了,但是有些时候,我们并不需要缓存,比如说:加载一张大图片的时候,如果再内存中保存一份,很容易造成OOM,这时候我们只希望有磁盘缓存,而不希望缓存到内存,因此就需要我们设置缓存策略了。Picasso 提供了这样的方法
方式一:memoryPolicy 设置内存缓存策略
NO_CACHE:表示处理请求的时候跳过检查内存缓存
NO_STORE: 表示请求成功之后,不将最终的结果存到内存。

Picasso.with(this).load(url).placeholder(R.mipmap.ic_launcher)
.memoryPolicy(MemoryPolicy.NO_CACHE,MemoryPolicy.NO_STORE)//禁止内存缓存
.into(imageView);

之前我们也有使用skipMemoryCache来禁止内存缓存,但是好像现在新的没有用了

 Picasso.with(this).load(url).placeholder(R.mipmap.ic_launcher).skipMemoryCache().into(imageView);

那我们就需要去看一下源码:

//看源码标注废弃
//其实他也是调用memoryPolicy,传递默认的NO_CACHE,NO_STORE
  /**
   * @deprecated Use {@link #memoryPolicy(MemoryPolicy, MemoryPolicy...)} instead.
   */
  @Deprecated public RequestCreator skipMemoryCache() {
    return memoryPolicy(NO_CACHE, NO_STORE);
  }

  /**
   * Specifies the {@link MemoryPolicy} to use for this request. You may specify additional policy
   * options using the varargs parameter.
   */
  public RequestCreator memoryPolicy(MemoryPolicy policy, MemoryPolicy... additional) {
  //不能为空,空异常
    if (policy == null) {
      throw new IllegalArgumentException("Memory policy cannot be null.");
    }
    this.memoryPolicy |= policy.index;
    //不能为空,异常,故2个参数不能为空
    if (additional == null) {
      throw new IllegalArgumentException("Memory policy cannot be null.");
    }
    if (additional.length > 0) {
      for (MemoryPolicy memoryPolicy : additional) {
        if (memoryPolicy == null) {
          throw new IllegalArgumentException("Memory policy cannot be null.");
        }
        this.memoryPolicy |= memoryPolicy.index;
      }
    }
    return this;
  }
//根据源码查看memoryPolicy方法的参数不能为空

那么我们看一下硬盘缓存
方式二:networkPolicy 设置磁盘缓存策略
NO_CACHE: 表示处理请求的时候跳过处理磁盘缓存
NO_STORE:表示请求成功后,不将结果缓存到Disk,但是这个只对OkHttp有效。
OFFLINE: 这个就跟 上面两个不一样了,如果networkPolicy方法用的是这个参数,那么Picasso会强制这次请求从缓存中获取结果,不会发起网络请求,不管缓存中能否获取到结果。

Picasso.with(this).load(url).placeholder(R.mipmap.ic_launcher).networkPolicy(NetworkPolicy.NO_CACHE).into(imageView);

7.Debug和日志
Picasso 有内存缓存和磁盘缓存,先从内存获取,没有再去磁盘缓存获取,都没有就从网络加载,网络加载是比较昂贵和耗时的。因此,作为一个开发者,我们往往需要加载的图片是从哪儿来的(内存、Disk还是网络),Picasso让我们很容易就实现了。只需要调用一个方法setIndicatorsEnabled(boolean)就可以了,它会在图片的左上角出现一个带色块的三角形标示,有3种颜色,绿色表示从内存加载、蓝色表示从磁盘加载、红色表示从网络加载。
image.png
日志
Picasso,可以打印一些日志,比如一些关键方法的执行时间等等,我们只需要调用setLoggingEnabled(true)方法,然后App在加载图片的过程中,我们就可以从logcat 看到一些关键的日志信息。
image.png

三:Picasso总结
1.Picasso 是下载图片然后缓存完整的大小到本地,比如说图片的大小是1080p的,之后如果我需要同一张图片,就会返回这张 full size 的,如果我需要resize,也是对这种 full size 的做 resize。
2.Picasso 则是用的 ARGB _8888的设定。Glide 默认是用的 RGB_565 的设定
3.加载时间问题(当从网络加载Picasso快一些,当从内存加载Glide快一些)
当尝试加载一个图片的时候,两个库都会采用先从缓存中读取,如果缓存中没有,再去下载的做法。
实际试验中,Picasso 会比 Glide 快一点。猜测可能的原因还是因为之前讲到的缓存机制导致,因为Picasso 是直接把图加载到内存中,而 Glide 则需要改变图片大小再加载到内存中去。这个应该是会耗费一定的时间。

但是,当加载图片从内存中的时候,Glide 则比 Picasso 要快。其原理还是因为缓存机制的区别。因为Picasso 从缓存中拿到的图片,还要先去 resize 后,然后设定给 imageView,但是 Glide 则不需要这样。
4.Picasso.with(这里只能传入上下文) .
Glide.with,后面可以传入上下文,activity实例,FragmentActivity实例,Fragement.传入的对象要比前者多.

结尾:如果我看得更远一点的话,是因为我站在巨人的肩膀上


Rocky_ruan
57 声望5 粉丝

不积跬步,无以至千里