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---------------------