前言

前面介绍了比较重要的Picasso对象的使用,以及他的一些重要方法,其实还有一个对象也很重要 ---- RequestCreator,在第一篇文章中其实我已经介绍了,RequestCreator的最终是为了生成一个Request对象。

public方法

太多了,但是这些方法其实实现都非常的简单,举一个例子。

 public RequestCreator centerCrop() {data.centerCrop(Gravity.CENTER);return this;}//--------Request.java--------public Builder centerCrop(int alignGravity) {if (centerInside) {throw new IllegalStateException("Center crop can not be used after calling centerInside");}centerCrop = true;centerCropGravity = alignGravity;return this;}
复制代码

其实RequestCreator的方法每个都很简单就是用来给Request.Builder对象设置参数的。Request.Builder是用来生成Request的,所以还是Request这个对象,然后来看里面的一些配置参数,以及参数的作用。

一个个来介绍下,public方法和属性

  1. buildUpon
 public Builder buildUpon() {return new Builder(this);}
复制代码

根据request重新构建出一个新的Request.Builder对象,可以在原来的基础之上再调用链式方法创建新的一个Request.Builder对象。

  1. hasSize
 public boolean hasSize() {return targetWidth != 0 || targetHeight != 0;}
复制代码

很简单,就是看你有没有resize 宽高

上面是2个方法,下面说属性。

  1. centerCrop
 public Request build() {if (centerInside && centerCrop) {throw new IllegalStateException("Center crop and center inside can not be used together.");}if (centerCrop && (targetWidth == 0 && targetHeight == 0)) {throw new IllegalStateException("Center crop requires calling resize with positive width and height.");}if (centerInside && (targetWidth == 0 && targetHeight == 0)) {throw new IllegalStateException("Center inside requires calling resize with positive width and height.");}if (priority == null) {priority = Priority.NORMAL;}
...
复制代码

在添加centerCrop的时候必须设置targetWidthtargetHeight

 Picasso.get().load("http://i.imgur.com/DvpvklR.png").centerCrop().into(ivTest)
复制代码

这样会报错, 应该这样

Picasso.get().load("http://i.imgur.com/DvpvklR.png").centerCrop().resize(100,100).into(ivTest)
复制代码

真正实现centerCrop是在图片加载后进行处理。

 if (data.centerCrop) {//获取图片要缩放的宽和图片真正的宽的比例float widthRatio =targetWidth != 0 ? targetWidth / (float) inWidth : targetHeight / (float) inHeight;//获取图片要缩放的高和图片真正的高的比例float heightRatio =targetHeight != 0 ? targetHeight / (float) inHeight : targetWidth / (float) inWidth;float scaleX, scaleY;//如果宽的比例大于高的比例,就说明图片在y方向上需要被裁减if (widthRatio > heightRatio) {int newSize = (int) Math.ceil(inHeight * (heightRatio / widthRatio));//裁剪后有个对齐的方向,是上对齐,还是下对齐,默认是中对齐if ((data.centerCropGravity & Gravity.TOP) == Gravity.TOP) {drawY = 0;} else if ((data.centerCropGravity & Gravity.BOTTOM) == Gravity.BOTTOM) {drawY = inHeight - newSize;} else {drawY = (inHeight - newSize) / 2;}drawHeight = newSize;scaleX = widthRatio;scaleY = targetHeight / (float) drawHeight;} else if (widthRatio < heightRatio) {//这里跟前面是一样的逻辑,只不过是x轴方向上需要被裁减,以及左对齐,还是右对齐int newSize = (int) Math.ceil(inWidth * (widthRatio / heightRatio));if ((data.centerCropGravity & Gravity.LEFT) == Gravity.LEFT) {drawX = 0;} else if ((data.centerCropGravity & Gravity.RIGHT) == Gravity.RIGHT) {drawX = inWidth - newSize;} else {drawX = (inWidth - newSize) / 2;}drawWidth = newSize;scaleX = targetWidth / (float) drawWidth;scaleY = heightRatio;} else {drawX = 0;drawWidth = inWidth;scaleX = scaleY = heightRatio;}if (shouldResize(onlyScaleDown, inWidth, inHeight, targetWidth, targetHeight)) {matrix.preScale(scaleX, scaleY);}}
复制代码

其实这里的代码很多,但是我们可以从效果上去看,就是对图片进行了 裁剪和缩放,然后以及对齐。这样就很清楚了,上面代码只是为了获取到。

  • scaleX 图片宽的缩放
  • scaleY 图片高的缩放
  • drawX 根据对齐操作,判断从图片的哪个点开始绘制
  • drawY 根据对齐操作,判断从图片的哪个点开始绘制
  1. centerCropGravity 上面已经介绍了,就是centerCrop的对齐的属性

  2. centerInside 其实跟centerCrop差不多,就是对图片的一个适配。

else if (data.centerInside) {// Keep aspect ratio if one dimension is set to 0float widthRatio =targetWidth != 0 ? targetWidth / (float) inWidth : targetHeight / (float) inHeight;float heightRatio =targetHeight != 0 ? targetHeight / (float) inHeight : targetWidth / (float) inWidth;float scale = widthRatio < heightRatio ? widthRatio : heightRatio;if (shouldResize(onlyScaleDown, inWidth, inHeight, targetWidth, targetHeight)) {matrix.preScale(scale, scale);}}
复制代码

代码其实就非常的简单,分别获取到宽高的缩放比例,然后取最小的,这样保证图片在imageview里面都看得到,并且不变形。

  1. config 配置图片的格式,可以是ARGB_8888,RGB_565等
//---------RequestCreator.java---------
//调用链式方法,可配置,最终其实是传到`Request`对象中public RequestCreator config(@NonNull Bitmap.Config config) {data.config(config);return this;}//-------RequestHandler.java--------static BitmapFactory.Options createBitmapOptions(Request data) {final boolean justBounds = data.hasSize();final boolean hasConfig = data.config != null;BitmapFactory.Options options = null;if (justBounds || hasConfig || data.purgeable) {options = new BitmapFactory.Options();options.inJustDecodeBounds = justBounds;options.inInputShareable = data.purgeable;options.inPurgeable = data.purgeable;if (hasConfig) {//最终其实是在创建 BitmapFactory.Options的时候当作参数传入,这样子生成的图片就会按照配置,如果不对图片质量有很高要求的话可以选择RGB_565,节省了一半的内存options.inPreferredConfig = data.config;}}return options;}//---------BitmapFactory.java----------/*** If this is non-null, the decoder will try to decode into this* internal configuration. If it is null, or the request cannot be met,* the decoder will try to pick the best matching config based on the* system's screen depth, and characteristics of the original image such* as if it has per-pixel alpha (requiring a config that also does).* * Image are loaded with the {@link Bitmap.Config#ARGB_8888} config by* default.*/public Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;
复制代码

其实呢这里已经说的很清楚了,如果配置config,那么就按你配置的来,如果没有配置,默认是Bitmap.Config.ARGB_8888

  1. hasRotationPivot,rotationDegrees,rotationPivotX,rotationPivotY
//----------Request.java------------public Builder rotate(float degrees, float pivotX, float pivotY) {rotationDegrees = degrees;rotationPivotX = pivotX;rotationPivotY = pivotY;hasRotationPivot = true;return this;}
复制代码

其实这里就可以看出4个参数的作用 hasRotationPivot:是否设置了旋转参数 rotationDegrees:旋转角度 rotationPivotX,rotationPivotY:旋转中心点x,y

6.onlyScaleDown

 if (shouldResize(onlyScaleDown, inWidth, inHeight, targetWidth, targetHeight)) {matrix.preScale(scaleX, scaleY);}....private static boolean shouldResize(boolean onlyScaleDown, int inWidth, int inHeight,int targetWidth, int targetHeight) {return !onlyScaleDown || (targetWidth != 0 && inWidth > targetWidth)|| (targetHeight != 0 && inHeight > targetHeight);}
复制代码

onlyScaleDown,顾名思义,只允许缩小,所以每次在缩放的时候,先判断下。

  1. priority 这个其实就是优先级的意思
//-------Picasso.java--------public enum Priority {LOW,NORMAL,HIGH}
复制代码

从定义上看是3种优先级,从高到低。在构建Request对象的时候会给默认值

//--------Request.java--------
public Request build() {...if (priority == null) {priority = Priority.NORMAL;}...
复制代码

那么设置这个优先级到底有什么用呢,其实还要看PicassoExecutorService这个关键类

PicassoExecutorService() {super(DEFAULT_THREAD_COUNT, DEFAULT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS,new PriorityBlockingQueue<Runnable>(), new Utils.PicassoThreadFactory());}
复制代码

这里非常有意思,在构建一个线程池的时候,创建了一个PriorityBlockingQueue有优先级的一个阻塞队列。

@Overridepublic Future<?> submit(Runnable task) {//每次执行的是一个PicassoFutureTask任务PicassoFutureTask ftask = new PicassoFutureTask((BitmapHunter) task);execute(ftask);return ftask;}private static final class PicassoFutureTask extends FutureTask<BitmapHunter>implements Comparable<PicassoFutureTask> {private final BitmapHunter hunter;PicassoFutureTask(BitmapHunter hunter) {super(hunter, null);this.hunter = hunter;}//任务提交之后,线程池会一个个进行处理,会对加入的对象做下对比,看哪个优先级高,就先执行哪个@Overridepublic int compareTo(PicassoFutureTask other) {Picasso.Priority p1 = hunter.getPriority();Picasso.Priority p2 = other.hunter.getPriority();return (p1 == p2 ? hunter.sequence - other.hunter.sequence : p2.ordinal() - p1.ordinal());}}
}
复制代码
  1. purgeable
//----------Request.java---------
public Builder purgeable() {purgeable = true;return this;}
复制代码

其实在第5点的config里面是有涉及到purgeable这个的,其实也是BitmapFactory.Options的一个参数。 这里我把另外一个参数合起来一起说,就是inInputShareable,这里我把源码的注释贴出,然后解释下。

 /*** @deprecated As of {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this is* ignored.** In {@link android.os.Build.VERSION_CODES#KITKAT} and below, if this* is set to true, then the resulting bitmap will allocate its* pixels such that they can be purged if the system needs to reclaim* memory. In that instance, when the pixels need to be accessed again* (e.g. the bitmap is drawn, getPixels() is called), they will be* automatically re-decoded.** <p>For the re-decode to happen, the bitmap must have access to the* encoded data, either by sharing a reference to the input* or by making a copy of it. This distinction is controlled by* inInputShareable. If this is true, then the bitmap may keep a shallow* reference to the input. If this is false, then the bitmap will* explicitly make a copy of the input data, and keep that. Even if* sharing is allowed, the implementation may still decide to make a* deep copy of the input data.</p>** <p>While inPurgeable can help avoid big Dalvik heap allocations (from* API level 11 onward), it sacrifices performance predictability since any* image that the view system tries to draw may incur a decode delay which* can lead to dropped frames. Therefore, most apps should avoid using* inPurgeable to allow for a fast and fluid UI. To minimize Dalvik heap* allocations use the {@link #inBitmap} flag instead.</p>** <p class="note"><strong>Note:</strong> This flag is ignored when used* with {@link #decodeResource(Resources, int,* android.graphics.BitmapFactory.Options)} or {@link #decodeFile(String,* android.graphics.BitmapFactory.Options)}.</p>*/@Deprecatedpublic boolean inPurgeable;
复制代码

首先这是弃用的一个属性,注释里也说的很清楚,LOLLIPOP及以上版本就无效了,在KITKAT及以下版本,有效。 翻译下: 如果设置为true,那么生成bitmap而去申请的一块内存,会在系统需要内存的时候,被回收。如果说这个bitmap又被调用拿去使用了,那么就跟inInputShareable这个属性有关,如果这个属性设置了false,那么,就会对inputdata做一个深拷贝,如果是true的话,一开始就会先使用input的一个引用,但是最后真正要使用到bitmap的时候,还是会对inputdata做一次深拷贝。

当然,后面还有最后一句比较关键的话Therefore, most apps should avoid using inPurgeable to allow for a fast and fluid UI. 意思就是说,如果你想你的app比较流畅,比较快,那么你就不要去使用,因为重新去解码一张图片是要时间的,这样很可能就会造成你加载图片的时候白了几秒。

当然,在内存紧张的时候是可以使用的。

static BitmapFactory.Options createBitmapOptions(Request data) {...options.inInputShareable = data.purgeable;options.inPurgeable = data.purgeable;...return options;}
复制代码

很显然PicassoinInputShareable,inPurgeable绑在了一起,要么都true,要么都false

  1. resourceId 如果要直接加载R.mipmap.**,R.drawable.**这种资源的话,这个参数就!=0

  2. stableKey

//key-value存入LruCache中,这里就是key的生成规则static String createKey(Request data, StringBuilder builder) {//先看有没有设置stableKey,有设置的话,就直接生成跟stableKey有关的一段字符串,后面可以直接使用stableKey,获取到需要的value。if (data.stableKey != null) {builder.ensureCapacity(data.stableKey.length() + KEY_PADDING);builder.append(data.stableKey);} else if (data.uri != null) {//如果有说没有上面的stableKey,但是有uri,那么就直接根据uri生成对应的keyString path = data.uri.toString();builder.ensureCapacity(path.length() + KEY_PADDING);builder.append(path);} else {builder.ensureCapacity(KEY_PADDING);builder.append(data.resourceId);}builder.append(KEY_SEPARATOR);复制代码

正常情况下,其实你的图片的uri不变的话,直接就uri就够用了,但是如果你图片的uri可能会发生改变,然后本身其实是一张图片的话,其实是可以使用stableKey。

比如说,你图片存放了三方服务器,然后可以使用http://****/x100/y100这种裁减的方式的话,那么就可以使用stableKey,因为你本身原图是一张,然后呢正常情况下你可能会下载了很多不通尺寸的图片,然后根据uri保存在LruCache中。

但是这里有一个优化的方式了,设置一个stableKey,只保存一份图片,然后再对图片进行缩放或者裁减,这样就防止存放了很多份不同尺寸的图片。

  1. targetHeight,targetWidth 当调用了resize方法,重新定义宽高的话,targetHeight,targetWidth!=0 如:
 Picasso.get().load("http://i.imgur.com/DvpvklR.png").resize(100,100).into(ivTest)
复制代码
  1. transformations 用来进行图片的一个变换的列表。 比如我要切成一个圆图
 Picasso.get().load("http://i.imgur.com/DvpvklR.png").transform(object : Transformation {override fun transform(source: Bitmap?): Bitmap {//重新创建一个新的bitmapval bitmap = Bitmap.createBitmap(source?.width!!, source?.height!!, Bitmap.Config.ARGB_8888)val canvas = Canvas(bitmap)val p = Paint(Paint.ANTI_ALIAS_FLAG)canvas.drawCircle(100f, 100f, 100f, p)p.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)canvas.drawBitmap(source, 0f, 0f, p)//这里必须要对原来的bitmap做recycle,不然会报错,后面source?.recycle()return bitmap}override fun key() = "test111"}).resize(200, 200).into(ivTest)//-------BitmapHunter.java---------
static Bitmap applyCustomTransformations(List<Transformation> transformations, Bitmap result) {for (int i = 0, count = transformations.size(); i < count; i++) {final Transformation transformation = transformations.get(i);Bitmap newResult;try {newResult = transformation.transform(result);} catch (final RuntimeException e) {Picasso.HANDLER.post(new Runnable() {@Override public void run() {throw new RuntimeException("Transformation " + transformation.key() + " crashed with exception.", e);}});return null;}if (newResult == null) {final StringBuilder builder = new StringBuilder() //.append("Transformation ").append(transformation.key()).append(" returned null after ").append(i).append(" previous transformation(s).\n\nTransformation list:\n");for (Transformation t : transformations) {builder.append(t.key()).append('\n');}Picasso.HANDLER.post(new Runnable() {@Override public void run() {throw new NullPointerException(builder.toString());}});return null;}//如果有转换,必须要对原来的bitmap做recycleif (newResult == result && result.isRecycled()) {Picasso.HANDLER.post(new Runnable() {@Override public void run() {throw new IllegalStateException("Transformation "+ transformation.key()+ " returned input Bitmap but recycled it.");}});return null;}
...
复制代码
  1. uri 加载图片的uri,没有什么好介绍的。

总结

相对来说图片框架中,Picasso是比较简单,比较容易看得懂的,所以,如果想看图片框架源码的话,建议可以从Picasso源码入手,先看到一些图片框架的基本的一些功能,然后可以尝试GlideFresco,那一定会受益匪浅的。最终可以自己上手写一份,我就是朝着这个目标前进的。fighting!!!

转载于:https://juejin.im/post/5c73640051882502900d0051

Picasso-源码解析(三)相关推荐

  1. Android之图片加载框架Picasso源码解析

    转载请标明出处: http://blog.csdn.net/hai_qing_xu_kong/article/details/76645535 本文出自:[顾林海的博客] 个人开发的微信小程序,目前功 ...

  2. Disruptor源码解析三 RingBuffer解析

    目录 系列索引 前言 主要内容 RingBuffer的要点 源码解析 系列索引 Disruptor源码解析一 Disruptor高性能之道 Disruptor源码解析二 Sequence相关类解析 D ...

  3. OkHttp3源码解析(三)——连接池复用

    OKHttp3源码解析系列 OkHttp3源码解析(一)之请求流程 OkHttp3源码解析(二)--拦截器链和缓存策略 本文基于OkHttp3的3.11.0版本 implementation 'com ...

  4. ReactiveSwift源码解析(三) Signal代码的基本实现

    上篇博客我们详细的聊了ReactiveSwift源码中的Bag容器,详情请参见<ReactiveSwift源码解析之Bag容器>.本篇博客我们就来聊一下信号量,也就是Signal的的几种状 ...

  5. 并发编程与源码解析 (三)

    并发编程 (三) 1 Fork/Join分解合并框架 1.1 什么是fork/join ​ Fork/Join框架是JDK1.7提供的一个用于并行执行任务的框架,开发者可以在不去了解如Thread.R ...

  6. 前端入门之(vuex源码解析三)

    上两节前端入门之(vuex源码解析二)我们把vuex的源码大概的撸了一遍,还剩下(插件.getters跟module),我们继续哈~ 插件童鞋们可以去看看vuex在各个浏览器的状态显示插件,小伙伴可以 ...

  7. 拆轮子-RxDownload2源码解析(三)

    本文为博主原创文章,未经允许不得转载 造轮子者:Season_zlc 轮子用法请戳作者链接 ↑ 前言 本文主要讲述 RxDownload2 的多线程断点下载技术. 断点下载技术前提 服务器必须支持按 ...

  8. Tomcat源码解析三:tomcat的启动过程

    Tomcat组件生命周期管理 在Tomcat总体结构 (Tomcat源代码解析之二)中,我们列出了Tomcat中Server,Service,Connector,Engine,Host,Context ...

  9. 【Vue.js源码解析 三】-- 模板编译和组件化

    前言 笔记来源:拉勾教育 大前端高薪训练营 阅读建议:建议通过左侧导航栏进行阅读 模板编译 模板编译的主要目的是将模板 (template) 转换为渲染函数 (render) <div> ...

  10. Cesium源码解析三(metadata元数据拓展中行列号的分块规则解析)

    目录 1.前言 2.layer.json中available参数意义 3.EPSG:4626切片及terrain分块原理 4.Cesium的terrain分块规则 5.自定义terrain分块规则 6 ...

最新文章

  1. Keep裁员、程序员被抓,看了下行情,我选择投奔AI
  2. 【C语言】09-字符串
  3. C#常用集合总结-2
  4. AI Frontiers | 微软首席 AI 科学家邓力演讲:口语对话系统的分类及三代演变
  5. 【Flask】数据的CRUD操作之聚合函数
  6. matlab求借带参数的方程组
  7. 狗窝里的小日子- 3 ...
  8. 前端白屏问题_深入理解前端性能监控
  9. python做线性回归统计推断提取参数_概率分析方法与推断统计(来自我写的python书)...
  10. windows环境下 安装gcc
  11. 小齐读者拿到快手、百度、网易等 offer 的独门秘籍!
  12. Warning: The TensorFlow library wasn't compiled to use SSE,SSE2,SSE3,SSE4.1 instructions
  13. [渝粤教育] 西南科技大学 文学概论 在线考试复习资料
  14. 应届生web前端面试题_Web前端初学者(应届生)面试攻略
  15. 郑大计算机课程表,郑州大学研究生课程表.doc
  16. 王家林老师人工智能AI 第10节课:用神经网络识别手写数字内幕解密 老师微信13928463918
  17. vue日历加法定假假日
  18. vscode输入英文时字体之间的间隔突然变大
  19. 计算机网络论文 考试吧,2012年11月计算机网络学习心得体会
  20. 如何优化SEO的网站结构

热门文章

  1. python新手入门-python新手入门方法
  2. python实现文件下载-Python实现http文件下载
  3. python列表切片口诀-python学习之“切片操作从入门到精通”
  4. python多线程爬虫实例-python多线程爬虫实例讲解
  5. python输入语句-Python中的模块导入和读取键盘输入的方法
  6. python编程基础是什么-Python面向对象编程基础解析(一)
  7. linux下载哪个python版本-Linux安装多个Python版本
  8. python菜鸟教程h-python菜鸟教程,python好玩又简单的代码
  9. 安装TensorRT,然后导入uff库包的时候报错:ImportError: ERROR: Failed to import module(cannot import name ‘GraphDef`)
  10. 使用conda报错:from conda.cli import main ModuleNotFoundError: No module named conda