今天开始我将对目前较为流行的开源库以及开源框架的源码进行分析,希望能够通过学习这些源码背后的设计思想,从而让自己的编程和设计能力有所提高。
废话不多说,切入正题。今天给大家介绍的picasso是一个图片缓存库,想必很多人对他都很熟悉了,它支持三级缓存,它的使用极为简单,同时也支持十分灵活的配置方式。大家可以通过如下地址下载到源码 http://square.github.io/picasso/

  1. 分析情景介绍

我们以一个最简单的使用情景来对源码进行分析:

Picasso.with(context).load("http://image.baidu.com/search/detail?z=0&ipn=").into(imageView);

上面的代码是执行从网络上加载地址为http://image.baidu.com/search/detail?z=0&ipn= 的图片到指定的imageView控件中。

  1. 代码组织结构介绍

在分析源码之前我们先看下整个开源库的代码组织,但是大家如果将代码下载到本地后会感到失望,因为个人认为picasso的代码组织是极为不规范的,整个源码只有一个package。下面是我根据自己的理解将其源码进行重新的组织。总共分成5个包,

  • action包包含有:Action抽象类,FetchAction,GetAction,ImageViewAction,RemoteViewsAction,Target,TargetAction。
  • requst包中包含的是:Request,RequestCreator,DeferredRequestCreator
  • requesthandler包中包含的是:AssetRequestHandler,ContactsPhotoRequestHandler,ContentStreamRequestHandler,FileRequestHandler,MediaStoreRequestHandler,NetworkRequestHandler,ResourceRequestHandler,RequestHandler
  • core包中包含有如下几个文件:

为什么这样分包大家可以在源码分析完后对整个流程以及每个类对职责有较为深入了解后进行思考。

3 .详细流程介绍

3.1 with 流程解析:
with阶段的主要任务是创建全局默认的Picasso实例,所创建的实例一般来说能够适合于绝大多数应用场景,如果不满足可以自己通过Builder来创建,也可以通过setSingletonInstance来注入我们自己创建的Picasso,但是这个方法需要在with方法前调用,因为这里创建picasso使用对是单例模式。

picasso默认对配置如下:
(1) 缓存使用LRU 缓存方式,缓存大小占用应用可用存储的15% RAM
(2) 硬盘缓存空间占用手机存储的2%,并且大小不小于5M,最大50MB。
(3) 具有3个线程用于磁盘和网络访问
(4) 缓存位于应用目录下对picasso-cache目录。

我们看下它是如何完成这部分的工作的,首先它使用建造者模式结合单例来创建picasso对象,之所以采用建造者模式是因为这种模式能够给配置参数多的对象的构建带来很大的方便。

public static Picasso with(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("context == null");
}
if (singleton == null) {
synchronized (Picasso.class) {
if (singleton == null) {
singleton = new Builder(context).build();
}
}
}
return singleton;
}

我们能从Builder对象中获取到什么信息呢?一般一个对象的Builder是用于向外面暴露设置内部组件的接口(此接口非彼接口),通过暴露的接口来注入我们自定义的对象。Picasso 的 Builder也是一样的。通过它我们可以注入自定义的下载器,线程池,缓存,请求转换器,请求处理器,内部事件监听。它还有一个builder方法,它的用处就是在全部设置后,检查重要的组件是否有设置了,如果没有设置,那么就创建初始化组件来使用。

public static class Builder {
private final Context context; //上下文
private Downloader downloader; //自定义的下载器
private ExecutorService service; //自定义的线程池
private Cache cache; //自定义的缓存
private Listener listener; //监听器
private RequestTransformer transformer; //请求转换器
private List<RequestHandler> requestHandlers; //请求处理器
private Bitmap.Config defaultBitmapConfig; //图像配置
private boolean indicatorsEnabled; //是否显示指示标志
private boolean loggingEnabled; //是否开启Log

/**Builder构造方法*/
public Builder(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("Context must not be null.");
}
this.context = context.getApplicationContext();
}
/**指定默认用于解码图片的的BitmapConfig*/
public Builder defaultBitmapConfig(@NonNull Bitmap.Config bitmapConfig) {
if (bitmapConfig == null) {
throw new IllegalArgumentException("Bitmap config must not be null.");
}
this.defaultBitmapConfig = bitmapConfig;
return this;
}
/**指定用于下载图片的下载器*/
public Builder downloader(@NonNull Downloader downloader) {
if (downloader == null) {
throw new IllegalArgumentException("Downloader must not be null.");
}
if (this.downloader != null) {
throw new IllegalStateException("Downloader already set.");
}
this.downloader = downloader;
return this;
}
/** 指定用于在后台下载图片的后台线程池 */
public Builder executor(@NonNull ExecutorService executorService) {
if (executorService == null) {
throw new IllegalArgumentException("Executor service must not be null.");
}
if (this.service != null) {
throw new IllegalStateException("Executor service already set.");
}
this.service = executorService;
return this;
}
/** 指定用于存放最近使用的图片的缓存 */
public Builder memoryCache(@NonNull Cache memoryCache) {
if (memoryCache == null) {
throw new IllegalArgumentException("Memory cache must not be null.");
}
if (this.cache != null) {
throw new IllegalStateException("Memory cache already set.");
}
this.cache = memoryCache;
return this;
}
/** 指定一个用于监听事件的监听器 */
public Builder listener(@NonNull Listener listener) {
if (listener == null) {
throw new IllegalArgumentException("Listener must not be null.");
}
if (this.listener != null) {
throw new IllegalStateException("Listener already set.");
}
this.listener = listener;
return this;
}
/**指定一个对所有到来请求进行转换的请求转换器*/
public Builder requestTransformer(@NonNull RequestTransformer transformer) {
if (transformer == null) {
throw new IllegalArgumentException("Transformer must not be null.");
}
if (this.transformer != null) {
throw new IllegalStateException("Transformer already set.");
}
this.transformer = transformer;
return this;
}
/** 添加请求处理器 */
public Builder addRequestHandler(@NonNull RequestHandler requestHandler) {
if (requestHandler == null) {
throw new IllegalArgumentException("RequestHandler must not be null.");
}
if (requestHandlers == null) {
requestHandlers = new ArrayList<RequestHandler>();
}
if (requestHandlers.contains(requestHandler)) {
throw new IllegalStateException("RequestHandler already registered.");
}
requestHandlers.add(requestHandler);
return this;
}
/** 是否显示调试的标志在图片上 */
public Builder indicatorsEnabled(boolean enabled) {
this.indicatorsEnabled = enabled;
return this;
}
/** 是否显示Log*/
public Builder loggingEnabled(boolean enabled) {
this.loggingEnabled = enabled;
return this;
}
/** 配置了诸如默认的下载器,请求转换器,默认的memory cache等: */
public Picasso build() {
//如果某些没有进行自定义那么就在这里使用默认的配置
Context context = this.context;
if (downloader == null) {
downloader = Utils.createDefaultDownloader(context);
}
if (cache == null) {
cache = new LruCache(context);
}
if (service == null) {
service = new PicassoExecutorService();
}
if (transformer == null) {
transformer = RequestTransformer.IDENTITY;
}
Stats stats = new Stats(cache);
Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
}
}

接下来我们重点看下builder方法,如果我们没有注入自定义的下载器,那么就会调用createDefaultDownloader创建出一个下载器:
下面这种写法也是比较值得借鉴的。下面的create方法只是在应用缓存目录下创建一个picasso-cache的缓存目录。后续下载的图片都缓存在这个目录下。
/**

  • 优先选用OKHttp3.如果没有那么就使用OkHttp,再没有那么使用Android 默认的下载方式
    */
    public static Downloader createDefaultDownloader(Context context) {
    if (SDK_INT >= GINGERBREAD) {
    try {
    Class.forName("okhttp3.OkHttpClient");
    //这里会先创建一个缓存目录
    return OkHttp3DownloaderCreator.create(context);
    } catch (ClassNotFoundException ignored) {
    }
    try {
    Class.forName("com.squareup.okhttp.OkHttpClient");
    //这里会先创建一个缓存目录
    return OkHttpDownloaderCreator.create(context);
    } catch (ClassNotFoundException ignored) {
    }
    }
    return new UrlConnectionDownloader(context);
    }
    接着我们看下默认缓存的设置:
public LruCache(@NonNull Context context) {
this(Utils.calculateMemoryCacheSize(context));
}

根据是否是大堆栈类型,如果是则获取大堆栈存储,否则获取标准堆栈存储。然后使用大概15%的存储空间作为缓存。

static int calculateMemoryCacheSize(Context context) {
ActivityManager am = getService(context, ACTIVITY_SERVICE);
boolean largeHeap = (context.getApplicationInfo().flags & FLAG_LARGE_HEAP) != 0;
int memoryClass = am.getMemoryClass();
if (largeHeap && SDK_INT >= HONEYCOMB) {
memoryClass = ActivityManagerHoneycomb.getLargeMemoryClass(am);
}
// Target ~15% of the available heap.
return (int) (1024L * 1024L * memoryClass / 7);
}

接着我们看下默认的线程池的设置:
它是继承自ThreadPoolExcutor

class PicassoExecutorService extends ThreadPoolExecutor
PicassoExecutorService() {
/**
* corePoolSize 线程池中容纳的核心线程数目,即使这些线程是空闲的,它会被保留在线程池中,除非allowCoreThreadTimeOut设置为true
* maximumPoolSize 最大线程池大小
* keepAliveTime 存活时间 当线程池中的线程数大于核心线程数的时候,在达到这个时间的时候等待新任务的空闲线程将会被终止
* unit keepAliveTime 参数的单位
* workQueue 用于在任务执行前保存任务的队列,这个队列将只会保留通过execute方法提交的Runnable任务
* threadFactory 在excutor创建一个线程时候使用的工厂类
*/
super(DEFAULT_THREAD_COUNT, DEFAULT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS,
new PriorityBlockingQueue<Runnable>(), new Utils.PicassoThreadFactory());
}
static class PicassoThreadFactory implements ThreadFactory {
@SuppressWarnings("NullableProblems")
public Thread newThread(Runnable r) {
return new PicassoThread(r);
}
}
private static class PicassoThread extends Thread {
public PicassoThread(Runnable r) {
super(r);
}
@Override public void run() {
Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);
super.run();
}
}

紧接着我们来看下请求转换器:
请求转换器会在每次请求被提交的时候被调用,它主要用于修改某个请求的信息。我们看到默认的请求,并没有对请求做任何事情,只是简单的将原先的请求返回。

public interface RequestTransformer {
Request transformRequest(Request request);
RequestTransformer IDENTITY = new RequestTransformer() {
@Override public Request transformRequest(Request request) {
return request;
}
};
}

Picasso 还有一个统计对象States,用于统计Picasso中的各种事件。它的主要对象只有三个带有消息处理能力的HandlerThread线程,一个Cache引用,一个Handler。

final HandlerThread statsThread;
final Cache cache;
final Handler handler;
它的统计对象数据有:
long cacheHits; //通过缓存获取到图片
long cacheMisses; //通过其他方式获取图片
long totalDownloadSize; //下载的总数据量大小
long totalOriginalBitmapSize; //解码后统计总图片大小
long totalTransformedBitmapSize; //图像转换大小
long averageDownloadSize; //平均下载大小
long averageOriginalBitmapSize; //平均解码后图片大小
long averageTransformedBitmapSize; //平均转换后图片大小
int downloadCount; //下载次数
int originalBitmapCount; //原始图片数量
int transformedBitmapCount; //转换图片数量

整个流程如下所示:
外界通过State对象调用dispatchXXXX方法,在dispatch方法中往Handler中发送事件,然后再调用performXXXX来处理这个事件。

接下来我们继续看下Dispatcher,它负责分发Action的:
我们先看下她的构造方法:

Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,
Downloader downloader, Cache cache, Stats stats) {
//分发线程
this.dispatcherThread = new DispatcherThread();
this.dispatcherThread.start();
//分发Handler
this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
//主线程Handler
this.mainThreadHandler = mainThreadHandler;
//线程池
this.service = service;
this.hunterMap = new LinkedHashMap<String, BitmapHunter>();
this.failedActions = new WeakHashMap<Object, Action>();
this.pausedActions = new WeakHashMap<Object, Action>();
this.pausedTags = new HashSet<Object>();
//下载器
this.downloader = downloader;
this.cache = cache;
this.stats = stats;
this.batch = new ArrayList<BitmapHunter>(4);
this.airplaneMode = Utils.isAirplaneModeOn(this.context);
this.scansNetworkChanges = hasPermission(context, Manifest.permission.ACCESS_NETWORK_STATE);
//网络监听器
this.receiver = new NetworkBroadcastReceiver(this);
receiver.register();
}

整个流程还是和State对象一致,也是由HandlerThread + Handler构成,外部调用dispatchXXX方法传递事件,在Handler中周转下最终调用performXXXX进行处理。这个具体涉及到的时候再重点介绍。
在build最后新建一个Picass对象返回,我们看下Picass对象的构造方法:
/**

  • Picasso 有如下几个重要的对象:
  • RequestTransformer:请求转换器
  • Dispatcher:请求分发器
  • allRequestHandlers:请求处理器
  • Cache 缓存器
  • Stats 事件统计
  • CleanupThread 清除线程
    */
    Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener,
    RequestTransformer requestTransformer, List<RequestHandler> extraRequestHandlers, Stats stats,
    Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled, boolean loggingEnabled) {

    this.context = context;
    this.dispatcher = dispatcher;
    this.cache = cache;
    this.listener = listener;
    this.requestTransformer = requestTransformer;
    this.defaultBitmapConfig = defaultBitmapConfig;

    //内部有7个请求处理器,额外的有一个
    int builtInHandlers = 7; // Adjust this as internal handlers are added or removed.
    int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);
    List<RequestHandler> allRequestHandlers = new ArrayList<RequestHandler>(builtInHandlers + extraCount);

    //将各个RequestHandler添加到allRequestHandlers
    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);

    this.stats = stats;
    //用于存储Traget和Action的Map
    this.targetToAction = new WeakHashMap<Object, Action>();
    this.targetToDeferredRequestCreator = new WeakHashMap<ImageView, DeferredRequestCreator>();
    //软引用队列
    this.referenceQueue = new ReferenceQueue<Object>();
    //清除线程
    this.cleanupThread = new CleanupThread(referenceQueue, HANDLER);
    this.cleanupThread.start();
    //调试相关
    this.indicatorsEnabled = indicatorsEnabled;
    this.loggingEnabled = loggingEnabled;
    }
    我们可以看到在构造方法中添加了很多的requestHandler,用于对请求进行处理,这个在后面介绍流程的时候在进行介绍。
    到目前为止我们知道了整个Picasso的大体结构入下图所示:

    主要包括请求转换器,请求处理类,请求分发器,而请求分发器则负责将请求的分发和执行。

with流程总结:
在with阶段主要是使用建造模式和单例模式,创建Picasso对象。在通过Picasso的Builder接口我们可以看出Picasso可以允许我们注入自定义的下载器,线程池,缓存,请求转换器,请求处理器,内部事件监听。然后通过build方法中检查哪些对象还没创建,如果还没创建就使用默认的方案,在创建Picasso对象的阶段会注册系列的请求处理器为后续的请求处理做准备。
我们可以从这个部分学到什么?

  1. Builder + 单例模式创建对象
  2. 通过Builder注入自定义对象,这里的自定义对象一般是抽象类或者接口,这样可以提供一个模板,用户可以通过这种方式来根据模板创建需要的对象。

3.2 load流程:

public RequestCreator load(@Nullable Uri uri) {
return new RequestCreator(this, uri, 0);
}

在load阶段传递的是用于标示图片的Uri. path resourceId等。返回的是RequestCreator

RequestCreator(Picasso picasso, Uri uri, int resourceId) {
if (picasso.shutdown) {
throw new IllegalStateException(
"Picasso instance already shut down. Cannot submit new requests.");
}
this.picasso = picasso;
this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
}

这里实际上只是创建了一个Request.Builder为创建请求对象做准备。load阶段很简单吧。 这个阶段重点记住load提供的接口有如下几种:

public RequestCreator load(@Nullable Uri uri) 
public RequestCreator load(@Nullable String path) //android.resource: file: content:
public RequestCreator load(@NonNull File file)
public RequestCreator load(@DrawableRes int resourceId)

load阶段总结:
load阶段就完成两个任务,一个是创建一个RequestCreator,另一个是创建Requst.Builder.为into阶段做准备。

3.3 into流程分析:
下面代码是整个流程的代码,每个步骤我们将会在后面一一展开。

public void into(ImageView target, Callback callback) {
//获取创建请求的时间
long started = System.nanoTime();
//注意into阶段必须在主线程,所以在这个阶段需要检查下当前是否是在主线程
checkMain();
if (target == null) {
throw new IllegalArgumentException("Target must not be null.");
}
//对输入的Uri进行检查,如果没有Uri或者resourceId那么就取消请求,根据要求看下是否显示占位图
if (!data.hasImage()) {
picasso.cancelRequest(target);
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
return;
}

if (deferred) {
if (data.hasSize()) {
throw new IllegalStateException("Fit cannot be used with resize.");
}
int width = target.getWidth();
int height = target.getHeight();
if (width == 0 || height == 0 || target.isLayoutRequested()) {
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
picasso.defer(target, new DeferredRequestCreator(this, target, callback));
return;
}
data.resize(width, height);
}
//创建请求,请求有两个重要的元素id以及创建时间,创建完后需要通过请求转换器先进行初步的转换
Request request = createRequest(started);
//使用请求信息,生成一个请求的key
String requestKey = createKey(request);
//查看请求策略,是否需要从缓存中获取数据
if (shouldReadFromMemoryCache(memoryPolicy)) {
//使用请求的key在cache中尝试获取图像,并统计获取结果
Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
//如果获取到了就取消请求,并设置图片,回调返回
if (bitmap != null) {
picasso.cancelRequest(target);
setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
if (picasso.loggingEnabled) {
log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);
}
if (callback != null) {
callback.onSuccess();
}
return;
}
}

//如果需要从其他地方获取,那么就先显示占位图
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
//初始化一个Action并提交
Action action = new ImageViewAction(picasso, target, request, memoryPolicy,networkPolicy, errorResId, errorDrawable, requestKey, tag, callback, noFade);
picasso.enqueueAndSubmit(action);
}

3.1.1 Request的创建过程

into阶段会先对传入的Uri进行判断,如果为空就取消请求。并根据需要决定是否显示占位符
然后使用load阶段创建的Request.Builder来创建请求。每个请求都有两个关键的成员,一个id一个创建时间。创建完Request对象后会通过请求转换器对其进行转换。

private Request createRequest(long started) {
int id = nextId.getAndIncrement(); //获得id
Request request = data.build(); //调用RequestBuidler 创建请求对象Requst
request.id = id; //给请求设置id以及创建时间
request.started = started; //请求是通过这两个来标示一个请求对象的
Request transformed = picasso.transformRequest(request); //对请求进行转换,默认的请求转换器为空操作,直接返回原来的请求
return transformed;
}

我们先来看下Request.Builder 的 build方法:

public Request build() {
if (centerInside && centerCrop) {
throw new IllegalStateException("Center crop and center inside can not be used together.");
}
if (centerCrop && (targetWidth == 0 && targetHeight == 0)) {
throw new IllegalStateException(
"Center crop requires calling resize with positive width and height.");
}
if (centerInside && (targetWidth == 0 && targetHeight == 0)) {
throw new IllegalStateException(
"Center inside requires calling resize with positive width and height.");
}
if (priority == null) {
priority = Priority.NORMAL;
}
return new Request(uri, resourceId, stableKey, transformations, targetWidth, targetHeight,
centerCrop, centerInside, centerCropGravity, onlyScaleDown, rotationDegrees,
rotationPivotX, rotationPivotY, hasRotationPivot, purgeable, config, priority);
}
}

上述的build过程首先对参数进行检查,通过上述的Requst对象中注入了包括uri在内的众多参数。
紧接着就是根据request对象来生成用于标示Requst的RequestKey。

static String createKey(Request data, StringBuilder builder) {
if (data.stableKey != null) {
builder.ensureCapacity(data.stableKey.length() + KEY_PADDING);
builder.append(data.stableKey);
} else if (data.uri != null) {
String path = data.uri.toString();
builder.ensureCapacity(path.length() + KEY_PADDING);
builder.append(path);
} else {
builder.ensureCapacity(KEY_PADDING);
builder.append(data.resourceId);
}
builder.append(KEY_SEPARATOR);

if (data.rotationDegrees != 0) {
builder.append("rotation:").append(data.rotationDegrees);
if (data.hasRotationPivot) {
builder.append('@').append(data.rotationPivotX).append('x').append(data.rotationPivotY);
}
builder.append(KEY_SEPARATOR);
}
if (data.hasSize()) {
builder.append("resize:").append(data.targetWidth).append('x').append(data.targetHeight);
builder.append(KEY_SEPARATOR);
}
if (data.centerCrop) {
builder.append("centerCrop:").append(data.centerCropGravity).append(KEY_SEPARATOR);
} else if (data.centerInside) {
builder.append("centerInside").append(KEY_SEPARATOR);
}

if (data.transformations != null) {
//noinspection ForLoopReplaceableByForEach
for (int i = 0, count = data.transformations.size(); i < count; i++) {
builder.append(data.transformations.get(i).key());
builder.append(KEY_SEPARATOR);
}
}
return builder.toString();
}

3.1.2 发起请求

( 1 ) 从缓存预取
在发起请求的时候首先查看下缓存策略,看下是否优先从缓存中获取图片数据,如果是的话就调用picasso.quickMemoryCacheCheck来检查缓存中是否已经有需要的数据了。如果有就返回,并且将结果传递给统计对象。然后取消现有的请求,并将图像设置到ImageView上。

Bitmap quickMemoryCacheCheck(String key) {
Bitmap cached = cache.get(key);
if (cached != null) {
stats.dispatchCacheHit();
} else {
stats.dispatchCacheMiss();
}
return cached;
}

(2) 创建Action 通过其他途径获取
紧接着创建一个ImageViewAction然后调用picasso.enqueueAndSubmit将Action发出。接下来这部分是非常重要的,所以在进行这部分介绍之前进行一下总结:

在into阶段首先会使用load阶段创建的RequstCreator来创建一个Request,并设置Requst的id以及启动时间这两个关键,参数,在创建Request这个过程中会先检查一系列的参数,然后再new出一个Requst对象出来。然后再使用创建出的Requst对象的参数创建出作为Request标示的requestKey。
再根据缓存策略,查看是否优先使用缓存中的图像,如果是的话那么就使用上面创建出来的requstKey从缓存中获取,并将结果反馈给stats对象进行统计,如果不优先使用缓存中的图像的话那么就创建出一个Action,然后调用picasso.enqueueAndSubmit将Action发出。
Action 是Target 以及Request的封装,它有个重要的方法abstract void complete(Bitmap result, Picasso.LoadedFrom from)在请求结束的时候会调用这个进行处理,这个后续会进行介绍,Action的子类目前有GetAction FetchAction TargetAction ImageViewAction RmoteViewAction这些。

我们继续看接下来的流程:
在提交到分发器之前需要先检查当前Action的对象是否绑定了另外的Action,如果是的话那么取消原先的请求,将当前的Action和Target放到targetToAction。然后提交到分发器上。

void enqueueAndSubmit(Action action) {
//获取Action中的target
Object target = action.getTarget();
//如果当前targetToAction 中记录的 Target中指定的action不是当前的action 说明之前已经指定了,那么取消原先的
if (target != null && targetToAction.get(target) != action) {
cancelExistingRequest(target);
//提交到targetToAction
targetToAction.put(target, action);
}
//提交到分发器
submit(action);
}

分发器分发请求:

void submit(Action action) {
//分发器分发请求
dispatcher.dispatchSubmit(action);
}
void dispatchSubmit(Action action) {
handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
}
@Override 
public void handleMessage(final Message msg) {
switch (msg.what) {
case REQUEST_SUBMIT: {
Action action = (Action) msg.obj;
dispatcher.performSubmit(action);
break;
}
//...............
}
}
}
void performSubmit(Action action) {
performSubmit(action, true);
}

上面的流程在之前已经介绍了,由外部调用dispatchXXXX方法,然后传递给Hander,然后再由performXXXXX进行处理。

匹配requestHandler封装到BitmapHunter:
在performSubmit中根据Action来判断那个requstHandler可以处理这个请求,然后将这个requstHandler传递到新创建的BitmapHunter中。

void performSubmit(Action action, boolean dismissFailed) {

if (pausedTags.contains(action.getTag())) {
pausedActions.put(action.getTarget(), action);
//查看是否在停止的列表中
return;
}
//从hunterMap通过RequestKey查找是否有已经存在的BitmapHunter(搜索者)
BitmapHunter hunter = hunterMap.get(action.getKey());
if (hunter != null) {
hunter.attach(action);
return;
}
//查找能够处理请求的RequestHandler,并将其封装到BitmapHunter
hunter = forRequest(action.getPicasso(), this, cache, stats, action);
//BitmapHunter 实际上是一个线程,在这里将其丢到线程池进行执行,进入线程池后会调用其中的run方法
hunter.future = service.submit(hunter);
//添加到hunterMap
hunterMap.put(action.getKey(), hunter);
if (dismissFailed) {
failedActions.remove(action.getTarget());
}
}

forRequst 方法中取出请求,以及请求处理器列表。依次调用请求处理器的canHandleRequst来判断当前requstHandler是否能够处理当前的请求。

static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action) {
//获取Action中封装的请求
Request request = action.getRequest();
//获取Picasso注册的RequestHandlers
List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
//遍历每个RequestHandler 看下那个Handler能够处理这个请求
for (int i = 0, count = requestHandlers.size(); i < count; i++) {
RequestHandler requestHandler = requestHandlers.get(i);
//使用请求的Uri来判断那个Handler进行处理
if (requestHandler.canHandleRequest(request)) { //如果可以处理当前请求那么就将Action传递给新建的BitmapHunter
return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);
}
}
return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);
}

每个requestHandler都有一个canHandleRequst的方法,将request进去,在这个方法中将根据请求的Uri来进行匹配。当前是否可以处理这个类型的请求。所以在new Picasso 对象的时候各个requestHandler的请求对象的添加顺序很关键。
下面是网络请求处理器的canHandleRequest的实现。

public boolean canHandleRequest(Request data) {
String scheme = data.uri.getScheme();
return (SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme));
}

在添加到线程池后将会调用BitmapHunter的run方法。在run方法中主要是获取最终的Bitmap,然后再通过分发器来返回结果。

public void run() {
//修改线程名字
updateThreadName(data);
//获取图片并完成转换,首先会先从缓存中获取,然后调用RequestHandler load方法进行获取,最后再进行内部转换以及自定义的图片转换
result = hunt();
//向分发器返回结果
if (result == null) {
dispatcher.dispatchFailed(this);
} else {
dispatcher.dispatchComplete(this);
}
}

通过requestHandler中的load方法加载图片数据:
获取图片主要是通过hunt方法,在这个方法中会先尝试从缓存中获取,如果缓存中没有,那么通过requstHandler中的load方法,这时候会有两种可能,一种是直接返回Bitmap。另一种是返回一个InputStream。如果是Bitmap那么就直接进入下一步,如果是输入流的话需要将从流中对其进行解码。

Bitmap hunt() throws IOException {
Bitmap bitmap = null;
//先尝试从缓存中获取图片
if (shouldReadFromMemoryCache(memoryPolicy)) {
bitmap = cache.get(key);
if (bitmap != null) {
stats.dispatchCacheHit();
loadedFrom = MEMORY;
return bitmap;
}
}
data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
//调用requestHandler的load方法进行加载图片
RequestHandler.Result result = requestHandler.load(data, networkPolicy);
if (result != null) {
loadedFrom = result.getLoadedFrom();
exifOrientation = result.getExifOrientation();
bitmap = result.getBitmap();
//首先会尝试直接获取Bitmap
if (bitmap == null) {
//如果不行的话尝试获取输入流
InputStream is = result.getStream();
try {
//使用输入流来创建Bitmap
bitmap = decodeStream(is, data);
} finally {
Utils.closeQuietly(is);
}
}
}
if (bitmap != null) {
//统计解码
stats.dispatchBitmapDecoded(bitmap);
//如果需要进行图片转换那么就进行转换
if (data.needsTransformation() || exifOrientation != 0) {
synchronized (DECODE_LOCK) {
//调用transformResult进行图片转换
if (data.needsMatrixTransform() || exifOrientation != 0) {
bitmap = transformResult(data, bitmap, exifOrientation);
}
//如果有自定义的转换效果那么就调用applyCustomTransformations 应用图片转换
if (data.hasCustomTransformations()) {
bitmap = applyCustomTransformations(data.transformations, bitmap);
}
}
//统计转换
if (bitmap != null) {
stats.dispatchBitmapTransformed(bitmap);
}
}
}
return bitmap;
}
public Result load(Request request, int networkPolicy) throws IOException {
//调用Downloader进行下载
Response response = downloader.load(request.uri, request.networkPolicy);
if (response == null) {
return null;
}
//查看是从哪里下载的?
Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK;
//获取返回的图片
Bitmap bitmap = response.getBitmap();
//如果图片数据不为空那么返回图片
if (bitmap != null) {
return new Result(bitmap, loadedFrom);
}

InputStream is = response.getInputStream();
if (is == null) {
return null;
}
if (loadedFrom == DISK && response.getContentLength() == 0) {
Utils.closeQuietly(is);
throw new ContentLengthException("Received response with 0 content-length header.");
}
if (loadedFrom == NETWORK && response.getContentLength() > 0) {
stats.dispatchDownloadFinished(response.getContentLength());
}
return new Result(is, loadedFrom);
}

图像执行内部以及自定义变换处理:
获取到Bitmap数据之后接着就进行图像的转换,首先进行内部的转换,这个是通过调用transformRequst进行的。

static Bitmap transformResult(Request data, Bitmap result, int exifOrientation) {
int inWidth = result.getWidth();
int inHeight = result.getHeight();
boolean onlyScaleDown = data.onlyScaleDown;
int drawX = 0;
int drawY = 0;
int drawWidth = inWidth;
int drawHeight = inHeight;
Matrix matrix = new Matrix();
if (data.needsMatrixTransform() || exifOrientation != 0) {
int targetWidth = data.targetWidth;
int targetHeight = data.targetHeight;
float targetRotation = data.rotationDegrees;
if (targetRotation != 0) {
double cosR = Math.cos(Math.toRadians(targetRotation));
double sinR = Math.sin(Math.toRadians(targetRotation));
if (data.hasRotationPivot) {
matrix.setRotate(targetRotation, data.rotationPivotX, data.rotationPivotY);
// Recalculate dimensions after rotation around pivot point
double x1T = data.rotationPivotX * (1.0 - cosR) + (data.rotationPivotY * sinR);
double y1T = data.rotationPivotY * (1.0 - cosR) - (data.rotationPivotX * sinR);
double x2T = x1T + (data.targetWidth * cosR);
double y2T = y1T + (data.targetWidth * sinR);
double x3T = x1T + (data.targetWidth * cosR) - (data.targetHeight * sinR);
double y3T = y1T + (data.targetWidth * sinR) + (data.targetHeight * cosR);
double x4T = x1T - (data.targetHeight * sinR);
double y4T = y1T + (data.targetHeight * cosR);
double maxX = Math.max(x4T, Math.max(x3T, Math.max(x1T, x2T)));
double minX = Math.min(x4T, Math.min(x3T, Math.min(x1T, x2T)));
double maxY = Math.max(y4T, Math.max(y3T, Math.max(y1T, y2T)));
double minY = Math.min(y4T, Math.min(y3T, Math.min(y1T, y2T)));
targetWidth = (int) Math.floor(maxX - minX);
targetHeight = (int) Math.floor(maxY - minY);
} else {
matrix.setRotate(targetRotation);
// Recalculate dimensions after rotation (around origin)
double x1T = 0.0;
double y1T = 0.0;
double x2T = (data.targetWidth * cosR);
double y2T = (data.targetWidth * sinR);
double x3T = (data.targetWidth * cosR) - (data.targetHeight * sinR);
double y3T = (data.targetWidth * sinR) + (data.targetHeight * cosR);
double x4T = -(data.targetHeight * sinR);
double y4T = (data.targetHeight * cosR);
double maxX = Math.max(x4T, Math.max(x3T, Math.max(x1T, x2T)));
double minX = Math.min(x4T, Math.min(x3T, Math.min(x1T, x2T)));
double maxY = Math.max(y4T, Math.max(y3T, Math.max(y1T, y2T)));
double minY = Math.min(y4T, Math.min(y3T, Math.min(y1T, y2T)));
targetWidth = (int) Math.floor(maxX - minX);
targetHeight = (int) Math.floor(maxY - minY);
}
}
if (exifOrientation != 0) {
int exifRotation = getExifRotation(exifOrientation);
int exifTranslation = getExifTranslation(exifOrientation);
if (exifRotation != 0) {
matrix.preRotate(exifRotation);
if (exifRotation == 90 || exifRotation == 270) {
// Recalculate dimensions after exif rotation
int tmpHeight = targetHeight;
targetHeight = targetWidth;
targetWidth = tmpHeight;
}
}
if (exifTranslation != 1) {
matrix.postScale(exifTranslation, 1);
}
}
if (data.centerCrop) {
// Keep aspect ratio if one dimension is set to 0
float widthRatio =
targetWidth != 0 ? targetWidth / (float) inWidth : targetHeight / (float) inHeight;
float heightRatio =
targetHeight != 0 ? targetHeight / (float) inHeight : targetWidth / (float) inWidth;
float scaleX, scaleY;
if (widthRatio > heightRatio) {
int newSize = (int) Math.ceil(inHeight * (heightRatio / widthRatio));
if ((data.centerCropGravity & Gravity.TOP) == Gravity.TOP) {
drawY = 0;
} else if ((data.centerCropGravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
drawY = inHeight - newSize;
} else {
drawY = (inHeight - newSize) / 2;
}
drawHeight = newSize;
scaleX = widthRatio;
scaleY = targetHeight / (float) drawHeight;
} else if (widthRatio < heightRatio) {
int newSize = (int) Math.ceil(inWidth * (widthRatio / heightRatio));
if ((data.centerCropGravity & Gravity.LEFT) == Gravity.LEFT) {
drawX = 0;
} else if ((data.centerCropGravity & Gravity.RIGHT) == Gravity.RIGHT) {
drawX = inWidth - newSize;
} else {
drawX = (inWidth - newSize) / 2;
}
drawWidth = newSize;
scaleX = targetWidth / (float) drawWidth;
scaleY = heightRatio;
} else {
drawX = 0;
drawWidth = inWidth;
scaleX = scaleY = heightRatio;
}
if (shouldResize(onlyScaleDown, inWidth, inHeight, targetWidth, targetHeight)) {
matrix.preScale(scaleX, scaleY);
}
} else if (data.centerInside) {
// Keep aspect ratio if one dimension is set to 0
float widthRatio =
targetWidth != 0 ? targetWidth / (float) inWidth : targetHeight / (float) inHeight;
float heightRatio =
targetHeight != 0 ? targetHeight / (float) inHeight : targetWidth / (float) inWidth;
float scale = widthRatio < heightRatio ? widthRatio : heightRatio;
if (shouldResize(onlyScaleDown, inWidth, inHeight, targetWidth, targetHeight)) {
matrix.preScale(scale, scale);
}
} else if ((targetWidth != 0 || targetHeight != 0) //
&& (targetWidth != inWidth || targetHeight != inHeight)) {
// If an explicit target size has been specified and they do not match the results bounds,
// pre-scale the existing matrix appropriately.
// Keep aspect ratio if one dimension is set to 0.
float sx =
targetWidth != 0 ? targetWidth / (float) inWidth : targetHeight / (float) inHeight;
float sy =
targetHeight != 0 ? targetHeight / (float) inHeight : targetWidth / (float) inWidth;
if (shouldResize(onlyScaleDown, inWidth, inHeight, targetWidth, targetHeight)) {
matrix.preScale(sx, sy);
}
}
}
Bitmap newResult = Bitmap.createBitmap(result, drawX, drawY, drawWidth, drawHeight, matrix, true);
if (newResult != result) {
result.recycle();
result = newResult;
}
return result;
}

接下来就是进行自定义的处理,这个是我们可以进行自己设置的。

static Bitmap applyCustomTransformations(List<Transformation> transformations, Bitmap result) {
for (int i = 0, count = transformations.size(); i < count; i++) {
final Transformation transformation = transformations.get(i);
Bitmap newResult;
try {
newResult = transformation.transform(result);
} catch (final RuntimeException e) {
//.................
return null;
}
result = newResult;
}
return result;
}

往主线程中返回处理后的Bitmap:

void dispatchComplete(BitmapHunter hunter) {
handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));
}
case HUNTER_COMPLETE: {
BitmapHunter hunter = (BitmapHunter) msg.obj;
dispatcher.performComplete(hunter);
break;
}
void performComplete(BitmapHunter hunter) {
//是否需要写入到缓存中
if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
cache.set(hunter.getKey(), hunter.getResult());
}
//移除hunterMap中注册的信息
hunterMap.remove(hunter.getKey());
//将图片展现出来
batch(hunter);
}
private void batch(BitmapHunter hunter) {
if (hunter.isCancelled()) {
return;
}
batch.add(hunter);
if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
}
}
case HUNTER_DELAY_NEXT_BATCH: {
dispatcher.performBatchComplete();
break;
}

这里将上面的BitmapHandler数组返回给主线程。

void performBatchComplete() {
List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);
batch.clear();
//这里是将其转送到Picasso类中进行处理,mainThreadHandler是在初始化DisPatcher的时候传入的HANDLER
mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
}

显示图片处理:
在complete阶段每个BitmapHunter都会将自己传递给Picasso complete中进行处理。

case HUNTER_BATCH_COMPLETE: {
//获取BitmapHunter列表
List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;
//对BitmapHandler列表中的每个handler调用complete方法进行处理。
for (int i = 0, n = batch.size(); i < n; i++) {
//调用complete进行处理
BitmapHunter hunter = batch.get(i);
hunter.picasso.complete(hunter);
}
break;
}

在Picasso的complete方法中将会从BitmapHunter中获取到图片数据。通过deliverAction进行图片的显示。

void complete(BitmapHunter hunter) {
Action single = hunter.getAction();
List<Action> joined = hunter.getActions();

boolean hasMultiple = joined != null && !joined.isEmpty();
boolean shouldDeliver = single != null || hasMultiple;
if (!shouldDeliver) {
return;
}
//从hunter中获取Uri Exception result以及图片的来源
Uri uri = hunter.getData().uri;
Exception exception = hunter.getException();
Bitmap result = hunter.getResult();
LoadedFrom from = hunter.getLoadedFrom();

if (single != null) {
//显示图片
deliverAction(result, from, single);
}
if (hasMultiple) {
for (int i = 0, n = joined.size(); i < n; i++) {
Action join = joined.get(i);
deliverAction(result, from, join);
}
}
//通过回调返回结果
if (listener != null && exception != null) {
listener.onImageLoadFailed(this, uri, exception);
}
}

在deliverAction中将会调用Action中的complete方法进行将图像显示到控件上。

private void deliverAction(Bitmap result, LoadedFrom from, Action action) {

if (result != null) {
//调用Action的complete方法
action.complete(result, from);
} else {
action.error();
}
}

最终是调用PicassoDrawable的setBitmap方法显示到ImageView上的。

@Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
if (result == null) {
throw new AssertionError(
String.format("Attempted to complete action with no result!\n%s", this));
}

//取出要显示图片的ImageView
ImageView target = this.target.get();
if (target == null) {
return;
}

Context context = picasso.context;
//使用PicassoDrawable setBitmap 方法将图片显示到ImageView上
PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);

//回调放回结果
if (callback != null) {
callback.onSuccess();
}
}
static void setBitmap(ImageView target, Context context, Bitmap bitmap,
Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
Drawable placeholder = target.getDrawable();
if (placeholder instanceof AnimationDrawable) {
((AnimationDrawable) placeholder).stop();
}
PicassoDrawable drawable = new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
//最终显示图片
target.setImageDrawable(drawable);
}

整个流程大致如下:

Contents