picasso使用方法与原理分析(一)

  • 前言

       安卓开源的图片加载框架有很多。最初的imageloader(目前已经不再维护),目前的picasso, glide。picasso和glide在加载展示图片这个模块其实相似度蛮高,谷歌也在很多项目中使用了glide。但这并不是说glide就在任何项目都是最适合的,我们必须在理解每个框架的原理的基础上选择适合我们的框架。接下来的几篇文章会通过分析picasso与glide的使用方式以及源码解析来比较他们两个的差异。首先我们先比较简单的看picasso的使用方法。具体源码分析在:使用方法与原理(二)

  • 添加依赖

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

       直接在模块build.gradle文件添加依赖就可以 ,目前的版本是18.03.08发布的2.71828.如果想通过自己编译源码获取jar包的方式需要注意,现在的源码和828版本有了一些改变。在使用方式上面还是有一些差距的。本篇博客就以828版本为准。 

  • 加载图片

      加载图片最常用的就是请求网络图片,代码如下

//firstUrl 是一个图片的网络地址,second是一个imageview
 Picasso.get().load(firstUrl).into(second);

     其中get方法是获得一个picasso的实例,load方法是进行加载,into是指定要加载图片的容器。本例是一个imageview。我们通过查看Picasso的类可以看到如下所示的load方法的重载:

    由此我们可以看出不只是支持网络请求,本地文件,已经本地资源文件都可以支持。

    比如获取显示资源文件:

  Picasso.get().load(R.mipmap.ic_launcher).into(second);

   Load(String)的方法参数不止是url。本地图片路径也可以识别。就不一一举例。加载的时候需要异常处理或者占位图。都是可以的:

Picasso.get().load(firstUrl).placeholder(R.mipmap.ic_launcher).error(R.mipmap.ic_launcher).into(second);

placeholder和error参数也可以是drawable类型的。使用picasso加载图片,在显示的时候都有一个渐进渐出效果,如果需要关闭调用nofade函数:

Picasso.get().load(firstUrl).noPlaceholder()
                .noFade().error(R.mipmap.ic_launcher).into(second);

 通过get方法获取的都是默认的picasso,我们来看看picasso的构造函数:

 Picasso(Context context, Dispatcher dispatcher, Call.Factory callFactory,
            @Nullable okhttp3.Cache closeableCache, PlatformLruCache cache, Listener listener,
            List<RequestTransformer> requestTransformers, List<RequestHandler> extraRequestHandlers,
            Stats stats, Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled,
            boolean loggingEnabled) {
    }

缓存,还有图片的config等等都是默认的,这个时候如果需要定制的还,可以通过builder的方式来创建。

我们来看看buidler的构造函数

  public static class Builder {
        private final Context context;
        private Call.Factory callFactory;
        private ExecutorService service;
        private PlatformLruCache cache;
        private Listener listener;
        private final List<RequestTransformer> requestTransformers = new ArrayList<>();
        private final List<RequestHandler> requestHandlers = new ArrayList<>();
        private Bitmap.Config defaultBitmapConfig;

这些变量都是可以通过get/set方法来设置。所以我们自己可以定制缓存方式,ExecutorService等。

  • 修改图片的尺寸

有时候我们需要的大小和服务器返回的图片大小尺寸不符合,这个时候可以通过接口重置大小:

Picasso.get().load(firstUrl).resize(220,200).into(second);
Picasso.get().load(firstUrl).resizeDimen(R.dimen.widget_margin,R.dimen.widget_margin).into(second);

第一个resize的参数单位是像素。这样可以修改图片的大小。其实这种方法需要我们自己计算图片的大小,而picasso中的fit()方法会自己计算imagview的大小,然后自己对图片进行设置。

Picasso.get().load(firstUrl).fit().into(second);

需要注意的是fit作用对象是imageview,并且因为要计算大小,imageview长宽不能使用wrap_content的属性。

改变图片大小我们都有过图片会变形的经历, 而这个时候需要使用centerCrop()这个函数实现了拉伸截取中间部分。因此centerCrop()一般会配合resize和fit使用:

 Picasso.get().load(firstUrl).resize(220,200).centerCrop().into(second);
 Picasso.get().load(firstUrl).fit().centerCrop().into(second);

这样做会铺满全屏,但是也许会将图片四周截去一部分。如果想看清楚全貌,使用centerinside()方法,但是这样做有可能出现图片无法充满整个view的情况

比如这样,imagview是全屏窗宽,但是无法铺满全屏。

  • 操作bitmap的方法包括旋转,灰度处理。

  1  上面的方法都是我们直接将下载的bitmap直接显示到imagview中,如果我们想自己处理的话。可以通过方法拿到bitmap,

/**
   * Synchronously fulfill this request. Must not be called from the main thread.
   */
  @Nullable // TODO make non-null and always throw?
  public Bitmap get() throws IOException {
    long started = System.nanoTime();
    //检测是否是主线程,如果是在主线程执行则抛出异常
    checkNotMain();

    if (deferred) {
      throw new IllegalStateException("Fit cannot be used with get.");
    }

    //url不为空或者默认图不为空,表示需要有要展示的图片
    if (!data.hasImage()) {
      return null;
    }

    //创建请求对象,request描述了需要请求的最终图片的属性。
    Request request = createRequest(started);

    //action是一个抽象类,表示了请求的一个实体
    Action action = new GetAction(picasso, request);
    //请求结果
    RequestHandler.Result result =
        forRequest(picasso, picasso.dispatcher, picasso.cache, picasso.stats, action).hunt();

    //判断是否需要写入缓存。如果需要则保存。
    if (result.hasBitmap() && shouldWriteToMemoryCache(request.memoryPolicy)) {
      //保存的时候如果需要保存的bitamp太大,超过缓存大小。则直接放弃
      picasso.cache.set(request.key, result.getBitmap());
    }
    return result.getBitmap();
  }

这个get接口,将获取的bitmap返回,需要注意的是不能在主线程进行这个操作。

  2   picasso还提供了图片的旋转功能

//旋转90度,默认以(0,0)为原点旋转
picasso.load(firstUrl).centerCrop().rotate(90).into(iv);
//以(20,30)为原点旋转90度
picasso.load(firstUrl).centerCrop().rotate(90,20,30).into(iv);

3  在项目中经常会有对图片进行处理的需求,比较常见的是圆角,灰度处理,高斯模糊,倒影。这些都可以自定义实现 。

picasso.load(firstUrl).transform(new BlurTransformation(this)).into(iv);

 picasso通过transform(Transformation)来实现,而Transformation是一个接口,我们要自己自己实现这个接口来对bitmap进行处理。

/** Image transformation. */
public interface Transformation {
  /**
   * Transform the source bitmap into a new bitmap. If you create a new bitmap instance, you must
   * call {@link android.graphics.Bitmap#recycle()} on {@code source}. You may return the original
   * if no transformation is required.
   */
  Bitmap transform(Bitmap source);

  /**
   * Returns a unique key for the transformation, used for caching purposes. If the transformation
   * has parameters (e.g. size, scale factor, etc) then these should be part of the key.
   */
  String key();

在这里bitmap就是我们要处理的主要的东西。我们来实现模糊处理

public class BlurTransformation implements Transformation {

  private static int MAX_RADIUS = 25;
  private static int DEFAULT_DOWN_SAMPLING = 1;

  private Context mContext;

  private int mRadius;
  private int mSampling;

  public BlurTransformation(Context context) {
    this(context, MAX_RADIUS, DEFAULT_DOWN_SAMPLING);
  }

  public BlurTransformation(Context context, int radius) {
    this(context, radius, DEFAULT_DOWN_SAMPLING);
  }

  public BlurTransformation(Context context, int radius, int sampling) {
    mContext = context.getApplicationContext();
    mRadius = radius;
    mSampling = sampling;
  }

  @Override public Bitmap transform(Bitmap source) {

    int scaledWidth = source.getWidth() / mSampling;
    int scaledHeight = source.getHeight() / mSampling;

    Bitmap bitmap = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888);

    Canvas canvas = new Canvas(bitmap);
    canvas.scale(1 / (float) mSampling, 1 / (float) mSampling);
    Paint paint = new Paint();
    paint.setFlags(Paint.FILTER_BITMAP_FLAG);
    canvas.drawBitmap(source, 0, 0, paint);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
      try {
        bitmap = RSBlur.blur(mContext, bitmap, mRadius);
      } catch (RSRuntimeException e) {
        bitmap = FastBlur.blur(bitmap, mRadius, true);
      }
    } else {
      bitmap = FastBlur.blur(bitmap, mRadius, true);
    }

    source.recycle();

    return bitmap;
  }

  @Override public String key() {
    return "BlurTransformation(radius=" + mRadius + ", sampling=" + mSampling + ")";
  }

然后通过如下代码,可以实现图片的模糊处理。

Picasso.get().load(firstUrl).transform(new BlurTransformation(this)).into(four);

通过看源码,transfrom也是一个重载函数:

 /**
   * Add a list of custom transformations to be applied to the image.
   * <p>
   * Custom transformations will always be run after the built-in transformations.
   */
  @NonNull
  public RequestCreator transform(@NonNull List<? extends Transformation> transformations) {
    data.transform(transformations);
    return this;
  }

也可以自定义几个效果,放到列表里面,然后会对图片进行一系列的操作。比如在定义一个灰度处理:

 List<Transformation> ts = new ArrayList<>();
        ts.add(new BlurTransformation(this));
        ts.add(new ColorFilterTransformation(Color.RED));
        Picasso.get().load(firstUrl).transform(ts).into(four);

这样就可以对图片进行2次处理。使用这个方法的时候要注意顺序。

在这里提供一个开源的Transformation项目,针对当前版本可以用,以后因为picasso修改了Transformation这个接口的回调方法,所以这个就不再适用了。但是本质上还是可以借鉴的。

Transformation项目

  • 有意思的预加载

/**
   * Asynchronously fulfills the request without a {@link ImageView} or {@link Target},
   * and invokes the target {@link Callback} with the result. This is useful when you want to warm
   * up the cache with an image.
   * <p>
   * <em>Note:</em> The {@link Callback} param is a strong reference and will prevent your
   * {@link android.app.Activity} or {@link android.app.Fragment} from being garbage collected
   * until the request is completed.
   */
  public void fetch(@Nullable Callback callback) {
    long started = System.nanoTime();

    if (deferred) {
      throw new IllegalStateException("Fit cannot be used with fetch.");
    }
    if (data.hasImage()) {
      // Fetch requests have lower priority by default.
      if (!data.hasPriority()) {
        data.priority(Priority.LOW);
      }

      Request request = createRequest(started);
      String key = createKey(request, new StringBuilder());

      if (shouldReadFromMemoryCache(memoryPolicy)) {
        Bitmap bitmap = picasso.quickMemoryCacheCheck(key);
        if (bitmap != null) {
          if (picasso.loggingEnabled) {
            log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);
          }
          if (callback != null) {
            callback.onSuccess();
          }
          return;
        }
      }

      Action action =
          new FetchAction(picasso, request, memoryPolicy, networkPolicy, tag, key, callback);
      picasso.submit(action);
    }
  }

根据注释我们可以看出。这个函数,没有target目标,也不会返回获取的数据,所以你觉得执行了一点意思没用 。但是他会把获取的图片存入内存。因此可以算是预加载,我们可以再使用图片之前偷偷的先预加载,然后加载的时候就会显得比较快。但是不要迷信这个方法 ,内存存储这个不是我们能准确控制的,所以慎用。

  • Tag管理

Picasso本质上也是进行很多网络请求,像Volley一样,它也提供了tag的管理方法。

 Picasso.get().load(firstUrl).fit().centerCrop().tag("alvin").into(second);

在我们请求的时候添加tag,如果页面销毁的时候,还没有请求成个,那么我们可以通过tag取消这个请求。当然如果只是一个请求,它的效果不足以体现,但是我们经常会用到listview,recyleview,这个时候如果有十几二十多个甚至更多,那么这个取消就比较有用了。

 @Override
    protected void onStop(){
        super.onStop();
        Picasso.get().cancelTag("alvin");
    }

除了这个还有PauseTag()和ResumeTag()这个方法。与activity的对应方法意思类似。

  • 缓存:内存,本地,网络三种方式

1  首先是内存缓存,通过源码可以看出是一个app所允许的15%

static int calculateMemoryCacheSize(Context context) {
    ActivityManager am = ContextCompat.getSystemService(context, ActivityManager.class);
    boolean largeHeap = (context.getApplicationInfo().flags & FLAG_LARGE_HEAP) != 0;
    int memoryClass = largeHeap ? am.getLargeMemoryClass() : am.getMemoryClass();
    // Target ~15% of the available heap.
    return (int) (1024L * 1024L * memoryClass / 7);
}

2 硬盘的缓存,picasso网络请求用的okhttp,而本地缓存直接使用的ok的缓存方式。一般大小也不超过50m,一个请求进入的时候,会进行缓存检查,顺序是:memory->disk->network

在开发过程中,我们可以通过接口设置本次请求是否需要缓存已经使用哪种缓存。默认是内存和磁盘缓存都开启。

Picasso.get().load(firstUrl).centerCrop().memoryPolicy(MemoryPolicy.NO_CACHE).into(second);

memoryPolicy()函数可以进行设置,参数大家可以通过源码学习,很简单的东西就不写了。

在我上面手机截屏的图片可以看到左上角有一个蓝色三角,这个是picasso的一个特色,通过颜色来表示图片的来源。memory,disk,network分别由不同的颜色保存,绿色表示来自memroy,蓝色来自disk,红色来自network

/**
     * Describes where the image was loaded from.
     */
    public enum LoadedFrom {
        MEMORY(Color.GREEN),
        DISK(Color.BLUE),
        NETWORK(Color.RED);

        final int debugColor;

        LoadedFrom(int debugColor) {
            this.debugColor = debugColor;
        }
    }

这个颜色显示默认是关闭的,需要通过setIndicatorsEnabled(true)来打开。

Picasso picasso = PicassoProvider.get();
picasso.setIndicatorsEnabled(true);
//旋转90度,默认以(0,0)为原点旋转
picasso.load(firstUrl).centerCrop().rotate(90).into(iv);
  • 总结

   以上就是picasso的基本用法,主旨是为了介绍使用方法,很多细节没有详解,稍后会在讲解源码的时候进行解释。如果有遗漏或者错误之处希望大伙指出,不胜感激。

 

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值