Android开发

Picasso 源码 commit 日记(3):图片缓存的 key 和 value 分别是什么?

本文主要是介绍Picasso 源码 commit 日记(3):图片缓存的 key 和 value 分别是什么?,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

以下代码笔记基于 commitId:a07bd18608800555f7ba6a4dea038be63188b831,commit 时间:2013/3/19 9:18 AM。点击上面的 commitId 可以跳转到 github 看代码,配合本文阅读。

本系列的文章结构包括以下 5 个部分。重构 是同样功能的代码的变动。feature 是对比上次提交,这次提交的新功能。设计 是我觉得可以提一下的代码设计,这部分可能不同的程序员会做不同的设计。疑惑 是我看代码过程中觉得有问题或者不懂的地方。知识点 是关于 Java 或者安卓的一些通用知识。

feature

缓存

上个版本,每一次请求都会直接从网络下载。这次新增了缓存,这样已经下载的图片就不需要重新下载。缓存分成了两部分,一是硬盘缓存,二是内存缓存。硬盘缓存可以让缓存数据在 app 退出后也存在,内存缓存可以让缓存数据加载更快。缓存策略采用的 LRU 算法,固定大小,如果满了,优先淘汰下载早的和不常使用的图片。具体实现可以看 DiskLruCache.java 和 LruMemoryCache.java 这两个类,本文先不展开。

增加了缓存后的工作逻辑

上个版本,工作逻辑是放在 Request 的 run 方法里,可能由于工作逻辑涉及到的很多变量都在 Picasso.java 里面,所以把工作逻辑放到了 Picasso.java 里。下面来看 Picasso.java 里面的 void run(Request request) 方法。

这里我简单地把主要的代码逻辑阐述一下:

  1. 先从内存缓存里面拿,如果拿到则返回。
  2. 再从硬盘缓存里面拿,如果拿到则返回,并且放到内存缓存里。
  3. 如果缓存没有,再通过网络下载,下载完返回,再放到硬盘缓存和内存缓存里。

图片变换

这个版本支持图片变换,内置了四种图片变换,用户还可以自定义图片变换,只需要实现 Transformation 接口里面的 Bitmap transform(Bitmap source) 方法。图片变换通过 Builder 配置。具体的变换算法,这里先不展开,主要看一下图片变换是在哪里应用的。可以看 Picasso.java 的 transformResult 方法,这个方法会把网络下载后的图片通过一系列的变换,再返回变换后的图片给到外面使用。

失败重试

上面一直没有提到如果任务失败会怎么办,这个版本对失败情况也做了处理。在 Picasso.java 的 void loadFromStream(Request request) 方法里,可以看到 catch (IOException e) 后,会通过 handler 发送一个 RETRY_REQUEST 消息(成功时发送的是 PROCESS_RESULT 消息)。

失败重试没有涉及到 UI 线程操作,这里为什么要通过 handler 切换到主线程呢? handler 除了切换线程的功能,还有一个消息队列的功能。仔细看发送重试消息的代码 handler.sendMessageDelayed(handler.obtainMessage(RETRY_REQUEST, request), RETRY_DELAY); ,这里用的 sendMessageDelayed ,Delayed 代表不会马上执行,RETRY_DELAY = 500 会延时 500 毫秒。因为这时候失败了,如果马上重试,大概率也会失败,所以需要延时。

失败重试还有一个最多重试次数的设计,这可以跟踪 retryCount 变量来看相关逻辑。最后注意的是,重试前也要判断请求是否已经被取消。

可以配置 bitmapOptions

可以通过 Biuder 来配置 bitmapOptions ,图片解码从 BitmapFactory.decodeStream(stream) 变成了 BitmapFactory.decodeStream(stream, null, request.bitmapOptions)

疑惑

为什么工作逻辑要放到 Picasso.java 里面

按道理工作逻辑应该要放到 worker 也就是 Request.java 里面,但是这次放到了 Picasso.java 里面。为什么会提出这个问题呢?可以看到在 handler 里出现的 request.picasso.complete(request)request.setFuture(request.picasso.service.submit(request)),这种代码读起来很绕。

缓存的 key 和 value 设置不合理

要做缓存,首先要想的是缓存的 key 和 value 要存什么。从 Picasso.java 的 void loadFromStream(Request request) 方法里的 saveToCaches(path, result) 可以知道, 缓存的 key 是图片的 url,value 是变换后 bitmap 。下一个请求如果用同一个 url ,所需的变换可能不一样,但是却拿到上一次变换后的图片,这样是有问题的。应该 key 改成和变换相关,或者 value 保存原图,从缓存拿出来再做变换。后面我们可以关注 Picasso 是怎么修复这个问题的。

设计

提交任务之前,先判断内存缓存有没有

在 Builder 的 into 方法里,可以看到先查询了内存缓存里有没有这个图片,如果有就不会把 request 提交到线程池里。为什么呢?因为内存缓存读取很快,如果提交到线程池里还需要等待线程池分配,所以就没必要提交到线程池了。这个小的设计,让请求既可以快速响应,又减少了线程池的负担。

方便测试

假设我们把缓存功能加上了,我们怎样快速地知道哪些图片是从网络下载的,哪些图片是从缓存加载的?Picasso 会给每个请求增加一个字段来标明图片来源,最后在图片渲染的时候,根据不同的来源,通过 setBackgroundColor() 来给图片设置不同的背景颜色,这样在 app 上面就可以一目了然了。这部分代码可以看 RequestMetrics.java 类和它的使用。

知识点

LRU 缓存如何实现

这里可以看 external 文件夹里面的文件。本文先不展开。

图片变换如何实现

这里可以看 transformations 文件夹里面的文件。本文先不展开。

这篇关于Picasso 源码 commit 日记(3):图片缓存的 key 和 value 分别是什么?的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!