前段时间简单的分析了一下ImageLoader的源码,所以就想看看使用很火的一些其他的图片加载库的实现,跟ImageLoader对比起来有什么优缺点。所以本系列的几篇博文会时不时跟ImageLoader来个简单的对比来说明问题。闲言少叙,开始Picasso分析之旅吧。
本篇只是简单的对流程进行梳理,因为Picasso自己分析起来篇幅只多不少,所以本篇就简单的说明。具体的某个知识点的细节会另外开篇博客说明之。
其实既然是缓存,肯定核心也就是那么几点:memory cache,disk cache等等,总体上来说只是不同的库对它们处理的方式不同而已。
当年老毛同志怎么说的来着:”要从战略上藐视对方,从战术上重视对方“。所以为了减少对阅读源码的恐惧感,让我先从战略上藐视一下picasso吧。刚把这玩意的源码下载来一瞅,第一个感觉就是:我滴个乖乖,居然就一个包!神马各种Action、各种RequestHandler统统特么的一股脑的放在了一个包下面,感觉就像一个洁白的床上堆满了臭袜子、毛巾、衣服和拖鞋一样乱糟糟的。就算你这个作者是技术大神,也不能组织类的时候这么不修边幅啊!
好了,战略藐视完毕,还是从战术上来仔细的分析吧!
Picasso的总体流程:
总的来说Picasso的流程很简单,当Picasso通过load方法获取图片的时候,需要经过如下步骤才能完成显示图片的流程:
1)将请求封装为Request对象,然后将Request对象进一步封装为Action(ImageAction)对象。
2)将Action(ImageAction)对象交给Dispather进行分发
3)最终将action交给BitmapHunter这个Runnable作为在线程池中线程的工作的单元(具体的是讲action持有的当前Reqeuest对象)
4)由RequestHandler来处理当前request,调用其load方法将加载完成后的图片交给PicassoDrawable显示图片。
代码流程如下:Picasso->load->创建request->创建action->Dispatcher分发action->RequestHandler的load方法处理具体的请求->PicassoDrawable显示图片。
上面去掉了许多的枝枝蔓蔓简单的说了一些流程,算是一个总纲吧,下面就围绕着这个总纲来说明Picasso的具体细节。
Picasso对象的初始化
跟ImageLoaderConfiguration一样,Picasso也是利用了Builder模式来组建Picasso,用Builder模式的好处之一就是可以通过Builder来清晰的知道Picasso可都可以提供哪些对外的配置接口供我们使用,同时我们自己在客户端配置这些组件的话,Builder也可以提供默认的组件来使用。就让我们看看Picasso的Builder都提供了什么组件让客户端自由配置:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
先不说这些Builder怎么使用它们,事实上Picasso的with方法来初始化一个Picasso单例对象,事实上就是用的Builder默认的配置来build一个Picasso对象出来:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
那么这个默认的Builder在build的时候都做了些神马呢,其实不用多想也应该知道了,就是配置了诸如默认的下载器,请求转换器,默认的memory cache等:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
我们知道Picasso中通过with方法其实是用默认的builder配置来初始化Picasso的单例对象。其实在你调用with方法之前(注意是调用with方法之前),还可以调用setSingletonInstance(Picasso)结合Builder配置自己的单例Picasso对象:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
这其实也算是单例模式+Builder模式的灵活应用吧!挺值得学习、借鉴!
load+into的简单说明
跟ImageLoader相同,Picasso也提供了对不同 图片来源(比如assets里面的图片,Filie里面的图片,或者Drawable)的加载处理。不同之处就是ImageLoader对外提供了统一的显示方法,然后由Imageloader自己判断来源从而提供不同的流来获取图片,而Picasso提供了多个重载load方法来处理对应的情况:
我们就挑拣其中的一个load(Uri)来进行分析吧。
其实,Picasso的源码分析起来也很简单,层层跟进load方法调用路径就可以了。
在调用load的时候实际上返回的是一个RequestCreator:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
也就是说在调用load方法的时候实际上并没有对图片资源进行加载,只是简单返回了一个RequestCreator对象,该对象在初始化的时候初始化了Request的Builder对象(好吧,又是Builder模式的应用)。
使用过Picasso的都知道,我们调用RequestCreator的into方法来完成工作的,那么就先简单的分析RequestCreator的into系列重载方法之一进行说明:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
上面的代码为了本篇博文的说明也是去掉了一些枝枝蔓蔓,甚至里面不乏重要的代码,在本篇不做论述,后面博文会讲到。into的主要逻辑就是把request+target封装交给action,然后由Picasso对此action进行提交。
Action的简要说明
为什么既然有了Request对象,又会需要一个Action呢?其实仔细分析源码可以发现Request侧重点在于请求本身:比如请求图片资源的uri,对请求的图片做什么处理等等一系列请求,主要是对Image做什么样的展示效果处理等。而Action的所含有的属性如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
很显然Action的主要职责就是:对图片进行加载,配置图片的文件缓存和内存缓存策略以及是否重新加载等逻辑。使得责任分明,调理清晰。
Action是一个抽象的泛型类,提供了complete和error两个抽象方法、它的子类又如下几个:
GetAction、FetchAction、ImageViewAction、TargetAction在此处我们提交的是ImageViewAction.
让我们简单的看一下ImageViewAction的complete方法:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
可以发现最终是由PicassoDrawable来完成图片的显示,所以继续跟进:
PicasDrawable是BitmapDrawable的子类:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
RequestHandler
:
该类用来处理不同来源的图片,是个抽象类,功能类似于Imageloader对不同图片来源返回不同的输入流,在picasso只是用面向对象的方式处理来自网络(NetworkRequestHandler),Resoure资源图片(ResourceRequestHandler),asset文件中的图片(AssetRequestHandler)等等若干个Handler。在初始化Picasso的时候,Picasso默认实现的RequestHandler的上述子类是预加载到一个集合中去的!:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
可以发现Picasso自己实现的handler在构造器初始化的时候就把自己实现的RequestHander的子类预先初始化好(姑且称之为Picasso内置的ReqeustHandler对象),当然用户也可以实现自己的RequestHandler通过Picasso.Builder.addRequestHandler来添加进去,当然大多数情况不需要自己提供ReqeuestHandler的实现。这种处理方式或者预先初始化的对象放入集合的方式其实在Gson源码中也有所体现。
那Picasso怎么知道是来自于哪个种类的图片呢?这就是ReqeuestHandler的功能了!RequestHandler时候一个抽象类,从名字上就可以知道它是处理具体请求的,它提供了两个重要的抽象方法来完成对不同种类图片的处理工作:
abstract canHandleRequest(Request data):判断某子类是否有能力处理当前请求
abstract load(Request request, int networkPolicy):在canHandleRequest方法返回true的时候,就用该RequestHandler的实现类的load方法来加载图片!
就让我们先简单分析Picasso怎么处理网络Reqeust的:NetworkRequestHandler
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
逻辑很简单,就是在判断当前Reqeuest为网络请求的时候调用load方法从网络中下载图片等处理。
那么ReqeuestHandler什么时候才真正起作用呢?沿着Picasso的脉络分析也很容易发现,在Picasso的Dispatcher在分发请求(从代码上来说应该是分发action)的时候会调用performSubmit方法,在此方法里面会来检测当前请求图片的种类来源,同样砍掉一些枝枝蔓蔓核心代码如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
该方法里面调用了forRequest方法,那么这个方法就是我们要找的方法了!!!
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
上面forRequest方法也很简单:主要执行了一下逻辑
1、获取当前请求Reqeuest对象
2、获取Picasso内置的RequestHandler以及自定义的RequestHandler集合。
3、判断集合中哪一个RequestHandler对象可以对当前Reqeuest进行处理(canHanlderReqest返回true)
4、把集合中能处理当前Request的RequestHandler对象交给BitMapHunter。
注意此时Picasso并没有立即调用RequestHandler对象的load方法进行处理,而是继续调用 service.submit(hunter);方法来在线程池中对BitmapHunter这个Runnable进行处理,所以我们不难猜测出来在run方法中必然调用了RequestHandler的load方法!
那么就继续追踪BimapHunter的run方法发现其调用了hunt()方法,那么hunt()方法是由做了什么呢?:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
看到了吧!在hunt()方法中正式调用了forReqeuset过滤的ReqesutHandler对象的load方法完成了核心业务功能!
http://blog.csdn.net/chunqiuwei/article/details/52040064
上面简单的介绍了一下Picasso的工作流程,而且故意省略了很多重要的地方因为篇幅需要暂时没做说明,会继续写博客慢慢抽丝拨茧分析,其实阅读源码很枯燥,有好多自己能体会到的地方有时候因为语言组织能力有限,只能自己体会而没办法写出来,Picasso的代码思路也很清晰,建议在业余的时间读读加深体会也是好的,好多东西自己钻研了才会真正的获得属于自己的收获。