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

 

相关推荐

相关文章