前一篇文章讲了Picasso的详细用法,Picasso 是一个强大的图片加载缓存框架,一个非常优秀的开源库,学习一个优秀的开源库,,我们不仅仅是学习它的用法,停留在使用API层面,我们也要试着去阅读源码,有两个方面的原因,第一,熟悉了源码我们才能更好的驾驭,项目中做我们需要的定制。第二,学习它的设计思想、编码风格、代码的架构,然后在项目中对这些好的思想和架构加以实践,变成自己的知识,这样才会对我们有更多的提升和帮助。这也是我们学习的目的,因此这篇文章对Picasso 的源码和流程做一个分析。

一、Picasso 加载图片流程图

图片加载框架Picasso - 源码分析_java

上面就是Picasso加载图片的流程,图画的丑,各位见谅。

二、重要的类介绍

(0)Picasso: 图片加载、转换、缓存的管理类。单列模式 ,通过with方法获取实例,也是加载图片的入口。
(1)RequestCreator: Request构建类,Builder 模式,采用链式设置该Request的属性(如占位图、缓存策略、裁剪规则、显示大小、优先级等等)。最后调用build()方法生成一个请求(Request)。
(2)DeferredRequestCreator:RequestCreator的包装类,当创建请求的时候还不能获取ImageView的宽和高的时候,则创建一个DeferredRequestCreator,DeferredRequestCreator里对 target 设置监听,直到可以获取到宽和高的时候重新执行请求创建。
 (3) Action: 请求包装类,存储了该请求和RequestCreator设置的这些属性,最终提交给线程执行下载。
(4)Dispatcher:分发器,分发执行各种请求、分发结果等等。
(5)PicassoExecutorService:Picasso使用的线程池,默认池大小为3。
(6)LruCache:一个使用最近最少使用策略的内存缓存。
(7)BitmapHunter:这是Picasso的一个核心的类,开启线程执行下载,获取结果后解码成Bitmap,然后做一些转换操作如图片旋转、裁剪等,如果请求设置了转换器Transformation,也会在BitmapHunter里执行这些转换操作。
(8)NetworkRequestHandler:网络请求处理器,如果图片需要从网络下载,则用这个处理器处理。
(9)FileRequestHandler:文件请求处理器,如果请求的是一张存在文件中的图片,则用这个处理器处理。
(10)AssetRequestHandler: Asset 资源图片处理器,如果是加载asset目录下的图片,则用这个处理器处理。
(11)ResourceRequestHandler:Resource资源图片处理器,如果是加载res下的图片,则用这个处理器处理。
(12)ContentStreamRequestHandler: ContentProvider 处理器,如果是ContentProvider提供的图片,则用这个处理器处理
(13)MediaStoreRequestHandler: MediaStore 请求处理器,如果图片是存在MediaStore上的则用这个处理器处理。
(14)ContactsPhotoRequestHandler:ContactsPhoto 请求处理器,如果加载com.android.contacts/ 下的tu图片用这个处理器处理。如:

上面8-14 是默认的提供的几个处理器,分别处理不同来源的请求。

(15)Response: 返回的结果信息,Stream流或者Bitmap。
(16)Request: 请求实体类,存储了应用在图片上的信息。
(17)Target:图片加载的监听器接口,有3个回调方法,onPrepareLoad 在请求提交前回调,onBitmapLoaded 请求成功回调,并返回Bitmap,onBitmapFailed请求失败回调。
(18)PicassoDrawable:继承BitmapDrawable,实现了过渡动画和图片来源的标识(就是图片来源的指示器,要调用 setIndicatorsEnabled(true)方法才生效),请求成功后都会包装成BitmapDrawable显示到ImageView 上。
(19)OkHttpDownloader:用OkHttp实现的图片下载器,默认就是用的这个下载器。
(20)UrlConnectionDownloader:使用HttpURLConnection 实现的下载器。
(21)MemoryPolicy: 内存缓存策略,一个枚举类型。
(22)NetworkPolicy: 磁盘缓存策略,一个枚举类型。
 (23)  Stats: 这个类相当于日志记录,会记录如:内存缓存的命中次数,丢失次数,下载次数,转换次数等等,我们可以通过StatsSnapshot类将日志打印出来,看一下整个项目的图片加载情况。
(24)StatsSnapshot :状态快照,和上面的Stats对应,打印Stats纪录的信息。

以上就是Picasso 的一些关键的类的介绍(还有一些简单的没有列举)。

三、流程分析

上一节介绍了Picasso 的一些关键类,接下来就以加载网络图片为例,分析Picasso加载图片的整个流程

Picasso.with(this).load(URL)
                .placeholder(R.drawable.default_bg)
                .error(R.drawable.error_iamge)
                .into(mBlurImage);

1, 获取Picasso instance

首先要获取一个Picasso对象,采用的单例模式

图片加载框架Picasso - 源码分析_java_02

2, 通过load方法生成一个RequestCreator

通过load方法生成一个RequestCreator,用链式api 来构建一个图片下载请求

RequestCreator提供了很多的API 来构建请求,如展位图、大小、转换器、裁剪等等,这些API其实是为对应的属性赋值,最终会在into方法中构建请求。

3,into 添加显示的View,并且提交下载请求

into方法里面干了3件事情:

1, 判断是否设置了fit 属性,如果设置了,再看是否能够获取ImageView 的宽高,如果获取不到,生成一个DeferredRequestCreator(延迟的请求管理器),然后直接return,在DeferredRequestCreator中当监听到可以获取ImageView 的宽高的时候,再执行into方法。

2, 判断是否从内存缓存获取图片,如果没有设置NO_CACHE,则从内存获取,命中直接回调CallBack 并且显示图片。

3, 如果缓存未命中,则生成一个Action,并提交Action。

4, 提交、分发、执行请求。

会经过下面这一系列的操作,最重将Action 交给BitmapHunter 执行。
enqueueAndSubmit -> submit -> dispatchSubmit -> performSubmit:

图片加载框架Picasso - 源码分析_java_03

5,指定对应的处理器(RequestHandler)

在上面执行的请求的performSubmit 方法里,调用了forRequest 方法为对应的Action 生成一个BitmapHunter,里面有一个重要的步骤,指定请求处理器(在上面一节介绍Picasso有7种请求处理器,看一下对应的代码:
图片加载框架Picasso - 源码分析_java_04

从Picasso里获取一个处理器列表,然后循环列表,看是否有能处理该请求的处理器,如果有,则生成BitmapHunter,那么这个请求处理器的列表在哪儿初始化的呢?请看源码:
图片加载框架Picasso - 源码分析_java_05

小结: 在Picasso 的构造函数里 初始化了内置的7中请求处理器,然后在生成BitmapHunter的时候,循环列表,找到可以处理对应请求的处理器。

6, 重点:BitmapHunter (图片捕获器)

上一节重要类介绍的时候介绍过BitmapHunter,BitmapHunter继承Runnable,其实就是开启一个线程执行最终的下载。看一下源码:
1, run() 方法

当将一个bitmapHunter submit 给一个线程池执行的时候,就会执行run() 方法,run里面调用的是hunt方法来获取结果,看一下hunt方法:


7,Downloader 下载器下载图片

上面的hunt方法获取结果的时候,最终调用的是配置的处理器的load方法,如下:

RequestHandler.Result result = requestHandler.load(data, networkPolicy);

加载网络图片用的是NetworkRequestHandler,匹配处理器,有个canHandleRequest 方法:

判断的条件是,Uri带有”http://“ 或者 https://   前缀则可以处理

我们接下来看一下NetworkRequestHandler的load方法:

NetworkRequestHandler最终是调用的downloader 的load方法下载图片。内置了2个Downloader,OkhttpDownloader和UrlConnectionDownloader 。我们以UrlConnectionDownloader为例,来看一下load方法:
图片加载框架Picasso - 源码分析_java_06
图片加载框架Picasso - 源码分析_java_07

小结:梳理一下调用链, BitmapHunter -> NetworkRequestHandler -> UrlConnectionDownloader(也有可能是OkHttpDownloader),经过这一系列的调用,最后在BitmapHunter 的run 方法中就可以获取到我们最终要的Bitmap。

8,返回结果并显示在Target上

在BitmapHunter获取结果后,分发器分发结果,通过Hander处理后,执行performComplete方法:
图片加载框架Picasso - 源码分析_java_08
图片加载框架Picasso - 源码分析_java_09
图片加载框架Picasso - 源码分析_java_10

小结:通过上面一系列的方法调用, performComplete -> batch —> performBatchComplete -> handleMessage -> complete 把BitmapHunter中获取到的结果回调到主线程,并且显示在Target上。

通过以上的8个步骤,就把图片从加载到显示的整个过程分析完了。

四,缓存特别说明

内存缓存很简单,用的是LRUCache,大小为 手机内存的15% ,上面代码中已经分析过了,这里不过多说明,这里重点说一下Disk Cahce。Picasso内存了2个默认的下载器,UrlConnectionDownloader和OkHttpDownloader,它们的磁盘缓存实现还是有一些差异的,看一下代码:
图片加载框架Picasso - 源码分析_java_11

在OkHttpDownloader 的构造方法里设置了磁盘缓存,使用的okHttp 的 DiskLruCache 实现的。

然后看一下UrlConnectionDownloader的磁盘缓存实现,代码:

图片加载框架Picasso - 源码分析_java_12

UrlConnectionDownloader 的磁盘缓存是用HttpResponseCache实现的

尽管2种磁盘缓存实现的方式不一样,但是它们的最后结果都是一样的:

1,磁盘缓存的地址: 磁盘缓存的地址在:data/data/your package name/cache/picasso-cache /
2,磁盘缓存的大小:磁盘缓存的大小为 手机磁盘大小的2% ,不超过50M不小于5M。
3, 缓存的控制方式一样:都是在请求的header设置Cache-Control的值来控制是否缓存。

缓存清除:
有同学在前一篇文章(图片加载框架-Picasso最详细的使用指南)下面留言问怎么清除缓存,这里统一说一下:
1, 清除内存缓存:调用invalidate方法,如:

 Picasso.with(this)
                .invalidate("http://ww3.sinaimg.cn/large/610dc034jw1fasakfvqe1j20u00mhgn2.jpg");

清除指定url 的内存缓存。

但是Picasso没有提供清除全部内存缓存的方法,那就没有办法了吗?办法还是有的,LRUCahce 提供了clear方法的,只是Picasso没有向外部提供这个接口,因此可以通过反射获取到Picasso的cache字段,然后调用clear方法清除。

2, 清除磁盘缓存
很遗憾Picasso没有提供清除磁盘缓存的方法。它没有提供方法我们就自己想办法呗。

思路:很简单,既然我们知道磁盘缓存是存在:data/data/your package name/cache/picasso-cache 这个路径下的,那我们把这个文件夹下面的所有文件清除不就行了。

实现:
图片加载框架Picasso - 源码分析_java_13

好了,就用上面一段代码就可以实现删除磁盘缓存了。

最后

以上就是对Picasso的源码分析,代码中的关键部分也有添加注释,到此,Picasso的使用和源码分析就讲完了,还没有看前一篇文章(图片加载框架-Picasso最详细的使用指南)的可以去看一下,如有问题,欢迎留言交流。