Picasso是SquareUp公司开源的专门为Android平台量身制作的图片加载库。通过Picasso,用户可以方便的将图片加载到特定的ImageView中,而不用关心图片是在一个文件夹里,还是在一个服务器上。那么,Picasso是如何何实现图片的快速加载了?
- 利用两级缓存机制对图片进行缓存: 加载一个图片时,首先从内存中查看是否存在;如果不存在,则查看外部存储是否有该图片。这时,如果还没有找到,则通过网络端下载图片;
- 利用OkHttp库进行图片下载,下载后保存到缓存,下次请求时无需从网络端下载;如果出现网络错误,会自动重试下载;
使用示例
Picasso提供了好几个调用接口,可以从文件或资源文件,也可以从网络下载图片.比如,现在要从网络上下载一张图片,并将其加载到一个ImageView上去,可以这么来调用接口:
1 |
|
那么,如果想要监听图片加载完成这一事件,则需要自己手动实现`Target这个接口:
1 |
|
然后将其作为参数传入:
1 |
|
具体代码可参考: https://github.com/runningforlife/AndroidExamples 中ImageLoader部分代码
源码解析
下图是Picasso的简单框图,用户调用into(ImageView iv)
接口后,Picasso将其该下载图片的请求包装成一个ImageViewAction
,接着由Picasso
负责发送到Dispatcher
中;Dispatcher
接着会把该Action继续封装成一个可执行对象BitmapHunter
,接着将其提交到线程池PicassoExecutorService
中执行;如果此时发现内存中已经有图片,则直接返回,否则BitmapHunter
将通过一个Downloader
下载图片,下载图片完成后,BitmapHunter
会将结果返回给Dispatcher
,最后由Dispatcher
告知Picasso
图片加载完成。
这里就来看一看Picasso库的几个核心类:
** Picasso.java
**
从构造函数来看,Picasso
类构造时需要初始化整个库,通过RequestCreator
来产生图片加载请求Request
,发送到Dispatcher
中准备执行; 等图片加载完成后,还需要通过主线程告知用户图片已经加载完成。
1 |
|
几个重要函数:
load(...)
: 加载图片,资源可是一个URL,也可以是一个文件路径,可以是一个资源文件ID;cancelRequest(ImageView)
: 取消某个ImageView
的图片加载请求;invalidate(...)
: 清除缓存;shutdown
: 关闭Picasso,取消所有已存在的Request
;enqueueAnsSubmite(Action)
: 将图片下载动作ImageViewAction
保存到一个Map,并发送给Dispatcher
处理;
**ImageViewAction.java
**
每一个图片下载请求都封装成一个ImageViewAction
,当图片下载完成后,有错误,则调用error(Exception)
接口,若用户设置了错误时对应的图片,则直接显示该图片;没有错误,则调用complete(Bitmap, LoadedFrom)
,将下载完成的Bitmap
显示出来。
complete(Bitmap,LoadedFrom)
: 图片加载完成
**Dispatcher.java
**
接收来自Picasso.java
的请求动作,产生一个BitmapHunter
可执行对象用于下载图片;Dispatcher
并不是在一个请求完成之后就发送给Picasso
,而是采用批处理的方式,等到完成的动作达到4个时,才一起发送出去。同时,有错误发生网络错误时,需要重新尝试下载;网络状态变化(网络切换)时,Dispatcher
会调整线程池中线程的个数,并且重新提交失败的下载请求。
performSubmit(Action)
: 将请求动作提交到线程执行服务PicassoExecutorService
;performRetry(BitmapHunter)
: 重新执行BitmapHunter
对象;
BitmapHunter.java
用于加载图片的可执行对象,其首先尝试从缓存中读取图片,如果没有则从外部存储或者网络中进行加载。加载完成后,解码生成一个Bitmap
返回给Dispatcher
。
hunt()
: 加载图片,优先从cache中读取,若没有,则从网络端下载;完成后,返回一个Bitmap对象;getResult()
: 返回加载的图片Bitmap;
** PicassoExecutorService.java
**
继承自ThreadPoolExecutor
,采用优先级阻塞队列PriorityBlockingQueue
对执行请求BitmapHunter
进行优先级排序,优先级高的始终位于队列的前端。另外,根据当前网络的状态来调整线程池的线程数目: 如果是WIFI连接,则线程数设置为4;4G网络下的线程数设置为3;而3G的线程数只有2个。
submit(Runnable)
: 将可执行对象加入到线程池中,并返回一个FutureTask<?>
对象;adjustThreadCount(NetworkInfo)
: 根据网络状态调整线程池线程数;