Picasso源码解析

Picasso模块图


整个库分为 Dispatcher,RequestHandler 及 Downloader,PicassoDrawable 等模块。

  • Dispatcher 负责分发和处理 Action,包括提交、暂停、继续、取消、网络状态变化、重试等等。

  • 简单的讲就是 Picasso 收到加载及显示图片的任务,创建 Request 并将它交给 Dispatcher,Dispatcher 分发任务到具体 RequestHandler,任务通过 MemoryCache 及 Handler(数据获取接口) 获取图片,图片获取成功后通过 PicassoDrawable 显示到 Target 中。

  • 需要注意的是上面 Data 的 File system 部分,Picasso 没有自定义本地缓存的接口,默认使用 http 的本地缓存,API 9 以上使用 okhttp,以下使用 Urlconnection,所以如果需要自定义本地缓存就需要重定义 Downloader。

Picasso优点

(1) 自带统计监控功能
支持图片缓存使用的监控,包括缓存命中率、已使用内存大小、节省的流量等。

(2) 支持优先级处理
每次任务调度前会选择优先级高的任务,比如 App 页面中 Banner 的优先级高于 Icon 时就很适用。

(3) 支持延迟到图片尺寸计算完成加载

(4) 支持飞行模式、并发线程数根据网络类型而变
手机切换到飞行模式或网络类型变换时会自动调整线程池最大并发数,比如 wifi 最大并发为 4, 4g 为 3,3g 为 2。这里 Picasso 根据网络类型来决定最大并发数,而不是 CPU 核数。

(5) “无”本地缓存
无”本地缓存,不是说没有本地缓存,而是 Picasso 自己没有实现,交给了 Square 的另外一个网络库 okhttp 去实现,这样的好处是可以通过请求 Response Header 中的 Cache-Control 及 Expired 控制图片的过期时间。

核心类

* Picasso*
主要是用于初始化、请求的消息的发送、回调结果的处理,即提供了请求发送的接口以及请求回调的接口,属于一个管理类
* RequestCreator,Request,Action*
这三个类是图片请求的抽象,它们之前对图片请求的描述程度是一个递进关系,RequestCreator更多的是一个请求的创建类它包含一些请求的次要元素-占位图片、错误图片的信息、缓存策略、网络策略等,接着Request则是一个真正的请求的抽象,包含了一个请求需要的资源位置、实施的变换等绝大部分信息,从下面的构造函数也可以看出来

 return new Request(uri, resourceId, stableKey, transformations, targetWidth, targetHeight,
       centerCrop, centerInside, onlyScaleDown, rotationDegrees, rotationPivotX, rotationPivotY,
       hasRotationPivot, config, priority);

而最后的Action则进一步描述了请求的需要的信息包括缓存策略、网络策略等,同样还是从构造方法来看

 Action action =
     new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
         errorDrawable, requestKey, tag, callback, noFade);

所以它们之间对于请求的描述是一个递进的关系
* Target接口*
请求加载事件过程的监听
* BitmapHunter*
该类是下载任务的核心管理类,也是Picasso线程池的实际的任务封装了类(实现了Runnable接口),它封装了任务下载、解析图像流、任务回送UI的逻辑
* RequestHandler*
图片下载任务的处理类,该类是图片加载任务处理过程的抽像,使得Picasso扩展加载不同资源的能够友好的扩展,它的的具体实现类NetworkRequestHandler、FileRequestHandler等分别拥有处理网络图片的加载、本地文件加载的能力
* Downloader*
下载图片的行为的抽象,UrlConnectionDownloader其的一个实现该类利用HTTPUrlConnection实现了图片的下载以及磁盘缓存
* Cache接口*
Picasso内存缓存的行为标准,LruCache为实现类
* Dispatcher*
该类的作用如其名-Picasso的事件分发的核心类,它的作用就是一个中转站,利用Handler的消息机制实现了关于请求的发送、请求的暂停、请求的完成等等一系列消息的处理

源码流程图

创建->入队->执行->解码->变换->批处理->完成->分发->显示(可选)

逻辑梳理

  • Picasso类初始化必须组件(任务处理类RequestHandler、任务下载类Downloader、缓存实现类Cache、事件分发类Dispatcher、线程池PicassoExecutorService等
  • 请求的预处理(RequestCreator),这里存在缓存检查的逻辑(以及其他),没有则继续准备创建请求类Request、Action
  • Picasso调用Dispatcher开始发送请求提交的消息,同时提交时还有一个请求检查的逻辑,如果有相同的请求存在则取消之前的
  • BitmapHunter上场,请求消息的处理是将BitmapHunter、请求Action、请求处理类RequestHandler绑定,然后送到线程池中执行
  • BitmapHunter开始加载图片,缓存图片,图片加载完成后由Dispatcher发送加载完成消息,同时这里加载图片之前还会先检查一次缓存
  • Picasso接收结果,利用其主线程HANDLER(Picasso利用了类似Volley的方法,使用Looper.mainLooper来创建Handler从而利用该Handler将事件调度到主线程中)来将结果发送到主线程中并回调监听

关键点

【1】RequestHandlers

List<RequestHandler> allRequestHandlers =
    new ArrayList<RequestHandler>(builtInHandlers + extraCount);

// ResourceRequestHandler needs to be the first in the list to avoid
// forcing other RequestHandlers to perform null checks on request.uri
// to cover the (request.resourceId != 0) case.
allRequestHandlers.add(new ResourceRequestHandler(context));
if (extraRequestHandlers != null) {
  allRequestHandlers.addAll(extraRequestHandlers);
}
allRequestHandlers.add(new ContactsPhotoRequestHandler(context));
allRequestHandlers.add(new MediaStoreRequestHandler(context));
allRequestHandlers.add(new ContentStreamRequestHandler(context));
allRequestHandlers.add(new AssetRequestHandler(context));
allRequestHandlers.add(new FileRequestHandler(context));
allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));
requestHandlers = Collections.unmodifiableList(allRequestHandlers);

可以看到除了添加我们可以自定义的extraRequestHandlers,另外添加了7个RequestHandler分别用来处理加载不同来源的资源,可能是Resource里的,也可能是File也可能是来源于网络的资源.这里使用了一个ArrayList来存放这些RequestHandler.

其中NetworkRequestHandler被传入了一个Downloader接口,接口的实现有2种:

  • OKHttpDownloader
  • UrlConnectionDownloader

有okhttp就反射用okhttp来下载,没有的话就用原生的UrlConnection。

自定义的extraRequestHandlers需要返回一个内部类Result,作为资源获取结果。

从这里也可以看到,Picasso对图片的处理支持类型还不是很多。gif、webp都不支持,如果要弄得话,估计要自己做个类似实现了。

参考Picasso解析(二)RequestHandler

【2】延迟加载(fit())

fit()会去测量ImageView的宽高,并且在内部使用resize(),从而缩小图片的尺寸去适配ImageView。

  1. 调用fit()会延时图片请求,因为Picasso需要等待直到ImageView的尺寸被测量完毕
  2. 只可以使用一个ImageView做完fit的目标

好处是图片使用的是最低的分辨率,不会受到图片质量的影响。更低的分辨率意味着需要占用更少的缓存。我们也可以调用resize方法手动对图片进行裁剪。

【3】DispatcherHandler

Dispatcher类主要功能就是利用handler进行消息通信,我们先看这个handler创建在哪个线程

Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,
    Downloader downloader, Cache cache, Stats stats) {
  this.dispatcherThread = new DispatcherThread();
  this.dispatcherThread.start();    
  this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
  this.mainThreadHandler = mainThreadHandler;
}

  static class DispatcherThread extends HandlerThread {
  DispatcherThread() {
    super(Utils.THREAD_PREFIX + DISPATCHER_THREAD_NAME, THREAD_PRIORITY_BACKGROUND);
  }
}

可以看到handler创建于名为DispatcherThread的子线程里。这里一共给handler发了两次消息,分别执行performSubmit()和performComplete()方法。

【4】mainThreadHandler

最后调用了mainThreadHandler(这是主线程的Handler)执行UI方法。

Picasso会开几条线程?
一条Dispatcher的Handler使用的线程+数条图片加载线程,后者由网络情况决定且Picasso注册了一个网络变化的广播接收器—连接wifi等4条线程、4G三条线程,3G,2G依次递减一条。

参考资料

猜你喜欢

转载自blog.csdn.net/xiang_freedom/article/details/67638675