Picasso - 使用(踩坑)

前言

本文基于 Picasso 2.71828 版本 。本文不仅讲用法,更主要讲的是错误的用法。而具体的源码分析在其他文章中。

implementation 'com.squareup.picasso:picasso:2.71828'

基本用法

从 assets 中加载

 Picasso.get()
        .load("file:///android_asset/test_img")
        .into(imageView);

从资源 Id 中加载图片
方法一 :

 Picasso.get()
        .load(R.drawable.test_img)
        .into(imageView);

方法二 :

 Picasso.get()
         .load("android.resource://" + getPackageName() + "/" + R.drawable.test_img)
         .into(imageView);

从 SD 中加载文件

File externalFile = getExternalFilesDir(null).getAbsoluteFile();
File targetFile = new File(externalFile, "test.jpg");
Picasso.get()
       .load(targetFile)
       .into(imageView);

从网络中加载图片

Picasso.get()
       .load("http://i.imgur.com/DvpvklR.png")
       .into(imageView);

设置占位图

Picasso.get()
       .load("http://i.imgur.com/DvpvklR.png")
       .placeholder(R.drawable.placeHolder)
       .into(imageView);

设置加载失败图片

   Picasso.get()
          .load("http://i.imgur.com/DvpvklR.png")
          .error(R.drawable.error_image)
          .into(imageView);

加载指定大小的图片 :

例如 : http://i.imgur.com/DvpvklR.png 图片分辨率为 400* 486 ,Bitmap.config 为 ARGB_8888 , 即每个像素占 4 个字节。

Picasso.get()
       .load("http://i.imgur.com/DvpvklR.png")
       .resize(200,200)
       .into(imageView);

请务必注意 , 千万不要以为有了图片加载框架,就可以为所欲为 !!!
resize() 不仅可以缩小,同时也会放大图片。下面代码 resize()之后 加载了 3500 * 3500 分辨率的图片到内存中。
所占用内存为 3500 * 3500 * 4 /(1024*1024) = 46.7 M 。如果你的页面存在内存泄露 , 嘿嘿 ,等着加班修 Bug 吧 !

Picasso.get()
       .load("http://i.imgur.com/DvpvklR.png")
       .resize(3500,3500)
       .into(imageView);

因此使用 resize 的时候,最好加上 onlyScaleDown() 方法。
只缩小不放大, 当 resize 尺寸大于 Bitmap 尺寸的时候 ,不放大图片。
当 resize 尺寸小于 Bitmap 尺寸的时候,缩小图片。
下面代码最终加载了 400 * 486 分辨率的图片到内存中 。

Picasso.get()
       .load("http://i.imgur.com/DvpvklR.png")
       .resize(3500,3500)
       .onlyScaleDown()
       .into(imageView);

设置图片 ScaleType
以下写法全是错误写法 :

        Picasso.get().load("http://i.imgur.com/DvpvklR.png").centerCrop().into(imageView);
        Picasso.get().load("http://i.imgur.com/DvpvklR.png").centerInside().into(imageView);
        Picasso.get().load("http://i.imgur.com/DvpvklR.png").centerInside().centerCrop().into(imageView);

centerCrop() 和 centerInside() 分别使用的时候, 必须调用 resize()方法。
centerCrop() 和 centerInside() 不能同时使用。


图片加载回调

注意 ,下面的写法会造成内存泄露 !

        Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(imageView, new Callback() {
            @Override
            public void onSuccess() {

            }
            @Override
            public void onError(Exception e) {

            }
        });

Callback 匿名内部类持有外部类的引用 。因此在 Activity/Fragment 销毁的时候注意取消请求。

  Picasso.get().cancelRequest(imageView);

同时注意到, 上边的回调,并没有 Bitmap 对象。 有时候我们需要拿到 Bitmap 对象来做些额外的处理 ,见下:


图片加载 Bitmap 回调

注意,下面的写法也是错误的写法

     Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(new Target() {
            @Override
            public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {

            }

            @Override
            public void onBitmapFailed(Exception e, Drawable errorDrawable) {

            }

            @Override
            public void onPrepareLoad(Drawable placeHolderDrawable) {

            }
        });

在 Picasso 中持有的 ImageView / Target 都是弱引用 , 上面的代码意味着 , Target 随时可能被回收 , 这些回调可能不会被调用。
正确写法如下 : 将 Target 声明为成员变量, Activity/fragent 销毁的时候要取消请求。

 Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(target);
...
    private Target target = new Target() {

        @Override
        public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {

        }

        @Override
        public void onBitmapFailed(Exception e, Drawable errorDrawable) {

        }

        @Override
        public void onPrepareLoad(Drawable placeHolderDrawable) {

        }
    };

....
   @Override
    protected void onDestroy() {
        super.onDestroy();
        Picasso.get().cancelRequest(target);
    }

加载图片到内存当中

有的时候,为了更快的拿到图片,我们需要提前从网络或磁盘中加载图片到内存中。
例如有两个页面 A、B ,在 A 页面的时候就去下载一张图片,跳转到 B 页面的时候立即显示图片,这样体验更好。
因此在 A 页面的时候,我们就可以调用。

        Picasso.get().load("http://i.imgur.com/DvpvklR.png").fetch();

如果你需要回调, : 下面代码同样这是错误的写法,理由同上,匿名内部类持有外部类造成的内存泄露 。
但是这里又不能取消,因此我建议使用 静态内部类 , 如果需要引用 Acttivity /fragment ,请使用弱引用来避免内存泄露。
或者使用上述的 into(Target) 方法 。

      Picasso.get().load("http://i.imgur.com/DvpvklR.png").fetch(new Callback() {
            @Override
            public void onSuccess() {

            }

            @Override
            public void onError(Exception e) {

            }
        });

上面获取 Bitmap 对象的方法都是异步方法 ,如果需要同步方法 :

    try {
            Bitmap bitmap = Picasso.get().load("http://i.imgur.com/DvpvklR.png").get();
        } catch (IOException e) {
            e.printStackTrace();
        }

使用时请务必注意一下几点
1. 该方法获取的 Bitmap 对象不可能从 内存缓存中获取 。 原因是 Picasso 内存相关操作不是线程安全的。
2. 该方法不能在是主线程调用。只能从磁盘或者网络获取 Bitmap ,是耗时操作,因此不能在主线程调用 , 必须在异步线程中调用 。


总结

使用第三方框架稍不留神,就会有很多的隐患。
本文的这些坑,一些已经在 Picasso 的注释中写的很明白。而另外一些,是我看了源码后忽然想到的。
写完本篇文章后一身冷汗, 不能仅仅满足于 API 的调用, 还是要知其然知其所以然 !

猜你喜欢

转载自blog.csdn.net/stupid56862/article/details/80944029