Picasso

  Picasso是一个加载图片的库。

dependencies {
    implementation 'com.squareup.picasso:picasso:2.71828'
}

基本用法

  使用的是最新的Picasso版本,api用法和先前的一些版本不太一致。
  Picasso加载图片的方式很简单,代码如下:

Picasso.get()
    .load(R.mipmap.avatar)
    .into(mImageView1);

加载的图片来源:

  Picasso加载图片资源的来源如下四种:
1 . 从assets中加载:

Picasso.get()
    .load("file:///android_asset/header.jpeg")
    .into(mImageView2);

2 . 从res资源id中加载

Picasso.get()
    .load(R.mipmap.avatar)
    .into(mImageView1);

//也可以用下面的方式来加载res文件夹中的图片资源;    
Picasso.get()
    .load("android.resource://com.example.kang/mipmap/avatar")
    .into(mImageView1);

3 . 从本地存储中加载

String path = "file://" + Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/image.jpg";
Picasso.get()
    .load(path)
    .into(mImageView3);

4 . 从网络资源中加载

Picasso.get()
    .load("http://dmimg.5054399.com/allimg/pkm/pk/13.jpg")
    .into(mImageView4);

  从上面简单的代码中,其实Picasso调用api是有一定的顺序的,比如需要是Picasso.get().load()这样的开头,以into()方法结尾。在load()和into()方法之间可以添加一些api方法对图片进行一定的处理。其实这里面涉及到Picasso源码中的两个类:Picasso和RequestCreator。
  在Picasso这个类中,我们常用的方法有:

方法名 解释
get() 构造Picasso对象
load() 加载图片资源

  在RequestCreator类中,常用的方法有:

方法名 解释
placeholder() 设置加载中显示的占位图;
error() 设置加载错误时显示的图片(如果加载发生错误会重复三次请求,三次都失败才会显示预设的错误图片);
resize() 设置指定大小的图片(指的是图片宽高的像素点的值,而不是图片的宽高);
resizeDimen() 设置指定大小的图片,指的是图片宽高占用的
centerCrop() 按比例裁减图片,使其居中显示,充满View,会造成图片显示不全,必须与resize()方法同时使用;
centerInside() 按比例裁减图片,图片可以完全显示,但如果图片比View小,则无法充满整个View,必须与resize()方法同时使用;
fit() 设置图片的宽高等于控件的宽高,属于非等比拉伸填满控件,不能和resize()方法一起使用;
rotate() 旋转,比如设置rotate(90),就是顺时针旋转90度;
noFade() 取消图片的过渡显示效果;
config() 设置图片质量,默认使用ARGB_8888,参数有ALPHA_8(每个像素占用1byte内存)、RGB_565(每个像素占用2byte内存)、ARGB_4444(每个像素占用2byte内存,已经被弃用)、ARGB_8888(每个像素占用4byte内存);
priority()
into() 设置填充的View的对象;
  • resize()方法的注意事项:
      例如一张图片分辨率为400 * 486,Bitmap.config为ARGB_8888,即每个像素占4个字节。如果不使用resize()方法,图片所占内存大小为400 * 486 * 4个字节。如果使用resize(200,200)以后,图片所占内存大小就变为了200 * 200 * 4个字节。
      但是也需要注意,resize()不仅可以缩小,同时也会放大图片。如果使用resize(3500,3500)方法加载了3500 * 3500分辨率的图片到内存中,那么所占用内存为3500 * 3500 * 4 /(1024 * 1024) = 46.7M。因此,使用resize()方法的时候,最好加上onlyScaleDown()方法,这个方法设置只缩小不放大。当resize尺寸大于Bitmap尺寸的时候,不放大图片;当resize尺寸小于Bitmap尺寸的时候,缩小照片。
      当resize()的两个参数中有一个为0时,表示按照另一个非0的参数来保持宽高比。比如resize(300,0),就是图片宽度为300个像素,高度等比例拉伸。两个参数不可以同时为0,否则会报"At least one dimension has to be positive number."的错误。
      fit()方法不能和resize()一起使用。

  • centerCrop()和centerInside():
      centerCrop()和centerInside()单独使用的时候,必须和resize()方法一起使用,而且centerCrop()和centerInside()这两个方法不可以同时使用。
      使用Picasso设置图片的ScaleType,只有centerCrop()和centerInside()这两种类型。

  • 取消加载图片
      当我们退出页面Activity/Fragment的时候,不想再加载图片,我们就需要取消请求。可以使用cancelRequest(mImageView)或Picasso.get().cancelTag(tag);
      需要注意的是:如果正在下载一张图片,那么及时取消了加载图片,网络请求不会中断,仍会占用网络继续下载,但是ImageView不会收到回调。这里正在下载的意思是线程正在执行Runnable方法。如果请求在线程池的等待队列中,那么不会下载。如果是正在执行,则不会中断。

  • 暂停加载图片
      Picasso的暂停加载图片,实际上是取消下载图片,并把当前请求添加到暂停列表中。使用Picasso.get().pauseTag(tag);

  • 继续加载图片
      Picasso的继续加载图片,是重新把请求交给Picasso来下载,并不是断点下载。使用Picasso.get().resumeTag(tag);
      暂停和继续加载图片,一般是在ListView和RecyclerView中使用的,当快速滑动的时候暂停下载图片,当滑动停止的时候继续加载图片。

  • 跳过内存缓存加载图片
      有时候,我们的业务需求不需要从内存缓存中加载图片,而是想直接从磁盘或网络中加载图片,就可以设置内存策略为MemoryPolicy.NO_CACHE。使用Picasso.get().load("xxx").memoryPolicy(MemoryPolicy.NO_CACHE).into(mImageView);
      如果不想把加载的图片放在内存缓存中,就使用内存策略MemoryPolicy.NO_STORE。使用Picasso.get().load("xxx").memoryPolicy(MemoryPolicy.NO_STORE).into(mImageView);
      Picasso默认会使用设备的15%的内存作为内存图片缓存,且没有api可以清空内存缓存。我们一般在查看大图时放弃使用内存缓存,图片从网络下载完成后会缓存到磁盘中,加载会从磁盘中加载,这样可以加速内存的回收。
    Picasso.get().load("xxx").memoryPolicy(MemoryPolicy.NO_CACHE,MemoryPolicy.NO_STORE).into(mImageView);NO_CACHE指图片加载时放弃在内存缓存中查找,NO_STORE指图片加载不缓存在内存中。

优化的地方

  • 重写ImageView:
      重写ImageView的onDetachedFromWindow方法,在ImageView从屏幕中消失的时候回调,去掉drawable引用,能加快内存的回收。
public class RecyclerImageView extends ImageView{

    @Override
    protected void onDetachedFromWindow(){
        super.onDetachedFromWindow();
        setImageDrawable(null);
    }
    
}
  • 在新进程中查看大图:
      如果要查看的大图占用内存达到了几十M,再加上现有进程中的内存,就容易产生OOM。在展示图片的Activity,我们可以在Androidmanifest.xml文件中把那个展示图片的Activity设置属性android:process=":xxx",就相当于在新进程中打开这个Activity。

  • 在ListView或者RecyclerView中滑动优化:
      Picasso可以对多个加载请求设置相同的tag,如下:

Object tag = new Object()

Picasso.get()
    .load("xxx")
    .tag(tag)
    .into(mImageView);
    
//在RecyclerView滑动时监听,处理不同的表现;
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener(){
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView,int newState){
        if(newState == RecyclerView.SCROLL_STATE_IDLE){
            Picasso.get().resumeTag(tag);
        }else{
            Picasso.get().pauseTag(tag);
        }
    }
});
  • 对于不透明的图片使用RGB_565来优化内存:
       java Picasso.get()
    .load("xxx")
    .config(Bitmap.Config.RGB_565)
    .into(mImageView);

源码分析

Picasso默认包含七个内置RequestHandler分别用来处理七种不同类型的请求:

  • ResourceRequestHandler:
      用于处理加载图片资源id的情况;

  • ContactsPhotoRequestHandler:
      用于处理手机联系人图片;

  • MediaStoreRequestHandler:
      用于处理content://media开头的URI;

  • ContentStreamRequestHandler:
      用于处理scheme为content的URI;

  • AssetRequestHandler:
      用于处理file:///android_asset/开头的URI;

  • FileRequestHandler:
      用于处理scheme为file的URI;

  • NetworkRequestHandler:
      用于处理http或https图片url;

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
---------------------last line for now---------------------