Android图片加载框架Picasso源码分析(基于Picasso 2.71828)

Picasso 是 Android 开发中最受欢迎的图片请求加载框架之一 ,它诞生于 2013 年,距今已有五年的生命。在这五年间 Picasso 发布过 21 个版本更新,而最近的一次更新为今年的 3 月 8 日,更新的版本号为 2.71828(文中统称为新版),该版本离上一次发布更新相隔了三年。本文主要分析新版 Picasso 的源码实现和它的一些 API 变化。

1. 新版 Picasso 的使用

新版 Picasso 最直观的变化就是在 App 中的调用方式为:

Picasso.get().load(url).into(imageView);

该调用跟原来版本的调用区别是,没有了需要传 Context 的 with 方法,取而代之是一个不需要传参的 get 方法来获取全局唯一 Picasso 实例。

本文也主要通过分析 Picasso.get().load(url).into(imageView) 该句调用的来龙去脉来理清新版 Picasso 框架的实现原理。

2. Picasso 实例的获取

Picasso 实例是通过调用 Picasso 类中的静态方法 get 获取的,该方法也是新版 Picasso 的入口,我们从该方法开始看起:

static volatile Picasso singleton = null;

public static Picasso get() {
  if (singleton == null) {
    synchronized (Picasso.class) {
      if (singleton == null) {
        if (PicassoProvider.context == null) {
          throw new IllegalStateException("context == null");
        }
        singleton = new Builder(PicassoProvider.context).build();
      }
    }
  }
  return singleton;
}

上面这段代码是一个非常经典的双重检查锁模式 (Double Checked Locking Pattern)。

首先进入 get 方法即检查一次 Picasso 实例 singleton 是否为 null,不为 null 就可以直接返回该实例。

接着进入到同步块,因为可能会一个线程进入同步块后创建完对象后退出,另一个线程又紧接着进入同步块,因此进入同步块后需要再检查一次 singleton 是否为 null,如果还为 null,这时候可以开始初始化该实例。

最后静态变量 singleton 需要用 volatile 关键字来修饰,目的是为了防止重排序。

2.1 使用 ContentProvider 获取 Context

新版 Picasso 提供给我们的入口方法 get 不需要传 Context,因为它使用的是 PicassoProvider 类中的 context,我们看一下 PicassoProvider 类:

public final class PicassoProvider extends ContentProvider {

  @SuppressLint("StaticFieldLeak") static Context context;

  @Override public boolean onCreate() {
    context = getContext();
    return true;
  }
    
}

PicassoProvider 类继承自 ContentProvider,除了 onCreate 方法,其他方法都是默认实现 (为了节省篇幅,省略了该部分代码),而 onCreate 方法也只是调用 getContext 方法并赋值给静态变量 context,然后返回 true 表示成功加载了该 ContentProvider。

Picasso 这么做的理由是,只要将 PicassoProvider 在 AndroidManifest 文件中注册,那么 App 在启动的时候,系统就会自动回调 PicassoProvider 的 onCreate 方法,因此也就自动获取到了 Context。

2.2 Picasso 实例的创建

接着回到 Picasso.get 方法中,Picasso 实例通过该句代码创建:

singleton = new Builder(PicassoProvider.context).build();

这里使用到了常用的 Builder 设计模式。当一个类的属性过多,通过构造函数构造一个对象过于复杂时,可以选择使用 Builder 设计模式来简化对象的构造过程。

看下 Picasso 类中静态内部类 Builder 的构造函数:

public Builder(@NonNull Context context) {
  if (context == null) {
    throw new IllegalArgumentException("Context must not be null.");
  }
  this.context = context.getApplicationContext();
}

该构造函数确保传进来的 Context 实例不为 null,然后获取全局的 Application Context。

接着是 Picasso.Builder 类中的 build 方法:

private final Context context;
private Downloader downloader;
private ExecutorService service;
private Cache cache;
private Listener listener;
private RequestTransformer transformer;
private List requestHandlers;
private Bitmap.Config defaultBitmapConfig;

public Picasso build() {
  Context context = this.context;
  // 配置下载器 Downloader,用于从网络下载图片资源,默认为 OkHttp3Downloader
  if (downloader == null) {
    downloader = new OkHttp3Downloader(context);
  }
  // 配置缓存 Cache,用来保存最近查看使用的图片,默认为 LruCache
  if (cache == null) {
    cache = new LruCache(context);
  }
  // 配置 ExecutorService,默认为 PicassoExecutorService
  // 后面 Bitmap 的获取任务就在该线程池中完成
  if (service == null) {
    service = new PicassoExecutorService();
  }
  // 配置 RequestTransformer 实例
  if (transformer == null) {
    transformer = RequestTransformer.IDENTITY;
  }
  // 创建 Stats 实例,Stats 类用来进行一些统计,如缓存命中数,图片下载数等
  Stats stats = new Stats(cache);
  // 创建 Dispatcher 实例,Dispatcher 类顾名思义,它的作用就是用来分发处理
  // 各种图片操作事件的如提交图片请求事件,图片获取完成事件等;
  // 传入前面配置好的对象和 HANDLER 实例给 Dispatcher 类构造函数
  // 该 HANDLER 在主线程接收处理事件,后面获取到 Bitmap 后需要回调到
  // 该 HANDLER 的 handleMessage 方法中以便将 Bitmap 切换回主线程显示
  Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
  // 传入前面配置好的一系列参数,创建 Picasso 实例
  return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
      defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
}

该方法的逻辑与相关类作用已在注释中进行了说明。

接下来看 Picasso 类的构造函数:

private final Listener listener;
private final RequestTransformer requestTransformer;
private final CleanupThread cleanupThread;
private final List requestHandlers;

final Context context;
final Dispatcher dispatcher;
final Cache cache;
final Stats stats;
final Map targetToAction;
final Map targetToDeferredRequestCreator;
final ReferenceQueue referenceQueue;
final Bitmap.Config defaultBitmapConfig;

boolean indicatorsEnabled;
volatile boolean loggingEnabled;

boolean shutdown;

Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener, RequestTransformer requestTransformer, List 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;
  // Picasso 默认包含七个内置 RequestHandler 分别用来处理七种不同类型的请求
  // 你也可以自己继承 RequestHandler 类来处理你的自定义请求
  // 自定义请求放在 extraRequestHandlers 中
  int builtInHandlers = 7;
  int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);
  List allRequestHandlers = new ArrayList<>(builtInHandlers + extraCount);
  // 添加 ResourceRequestHandler,用于处理加载图片资源 id 的情况
  // ResourceRequestHandler 需要第一个进行添加
  // 避免其他的 RequestHandler 检查 (request.resourceId != 0) 的情况
  allRequestHandlers.add(new ResourceRequestHandler(context));
  // 然后添加自定义的 RequestHandler (如果有的话)
  if (extraRequestHandlers != null) {
    allRequestHandlers.addAll(extraRequestHandlers);
  }
  // 添加 ContactsPhotoRequestHandler,用于处理手机联系人图片
  allRequestHandlers.add(new ContactsPhotoRequestHandler(context));
  // 添加 MediaStoreRequestHandler,用于处理 content://media/ 开头的 URI
  allRequestHandlers.add(new MediaStoreRequestHandler(context));
  // 添加 ContentStreamRequestHandler,用于处理 scheme 为 content 的 URI
  allRequestHandlers.add(new ContentStreamRequestHandler(context));
  // 添加 AssetRequestHandler,用于处理 file:///android_asset/ 开头的 URI
  allRequestHandlers.add(new AssetRequestHandler(context));
  // 添加 FileRequestHandler,用于处理 scheme 为 file 的 URI
  allRequestHandlers.add(new FileRequestHandler(context));
  // 添加 NetworkRequestHandler,用于处理 http 或 https 图片 url
  allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));
  // 调用 Collections 的静态方法 unmodifiableList 
  // 返回一个不能进行修改操作的 List 实例,防止 requestHandlers 被修改
  requestHandlers = Collections.unmodifiableList(allRequestHandlers);
  this.stats = stats;
  this.targetToAction = new WeakHashMap<>();
  this.targetToDeferredRequestCreator = new WeakHashMap<>();
  this.indicatorsEnabled = indicatorsEnabled;
  this.loggingEnabled = loggingEnabled;
  this.referenceQueue = new ReferenceQueue<>();
  this.cleanupThread = new CleanupThread(referenceQueue, HANDLER);
  this.cleanupThread.start();
}

到这里 Picasso 实例就创建完毕了。

2.3 Dispatcher 实例的创建

Picasso.Builder 类的 build 方法在创建 Picasso 实例前先创建了 Dispatcher 类的实例,Dispatcher 类对后面分发处理图片事件至关重要,这里先看一下它的构造函数:

final DispatcherThread dispatcherThread;
final Context context;
final ExecutorService service;
final Downloader downloader;
final Map hunterMap;
final Map failedActions;
final Map pausedActions;
final Set pausedTags;
final Handler handler;
final Handler mainThreadHandler;
final Cache cache;
final Stats stats;
final List batch;
final NetworkBroadcastReceiver receiver;
final boolean scansNetworkChanges;

boolean airplaneMode;

Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,
    Downloader downloader, Cache cache, Stats stats) {
  // 创建静态内部类 DispatcherThread 的实例并启动
  this.dispatcherThread = new DispatcherThread();
  this.dispatcherThread.start();
  this.context = context;
  this.service = service;
  this.hunterMap = new LinkedHashMap<>();
  this.failedActions = new WeakHashMap<>();
  this.pausedActions = new WeakHashMap<>();
  this.pausedTags = new LinkedHashSet<>();
  // 创建静态内部类 DispatcherHandler 的实例
  this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
  this.downloader = downloader;
  // 保存前面 Picasso 类传进来的主线程 HANDLER
  this.mainThreadHandler = mainThreadHandler;
  this.cache = cache;
  this.stats = stats;
  this.batch = new ArrayList<>(4);
}

该构造函数首先创建了 DispatcherThread 实例,而后面 DispatcherHandler 实例的创建用到了 DispatcherThread 中的 Looper。

DispatcherThread 和 DispatcherHandler 都是 Dispatcher 中的静态内部类:

static class DispatcherThread extends HandlerThread {
  DispatcherThread() {
    super(Utils.THREAD_PREFIX + DISPATCHER_THREAD_NAME, THREAD_PRIORITY_BACKGROUND);
  }
}
private static class DispatcherHandler extends Handler {
  private final Dispatcher dispatcher;

  DispatcherHandler(Looper looper, Dispatcher dispatcher) {
    super(looper);
    this.dispatcher = dispatcher;
  }

  @Override public void handleMessage(final Message msg) {
    switch (msg.what) {
      case REQUEST_SUBMIT: {
        Action action = (Action) msg.obj;
        dispatcher.performSubmit(action);
        break;
      }
      case HUNTER_COMPLETE: {
        BitmapHunter hunter = (BitmapHunter) msg.obj;
        dispatcher.performComplete(hunter);
        break;
      }
      default:
        Picasso.HANDLER.post(new Runnable() {
          @Override public void run() {
            throw new AssertionError("Unknown handler message received: " + msg.what);
          }
        });
    }
  }
}

可以看到,DispatcherThread 继承自 HandlerThread,而 DispatcherHandler 实例是通过 DispatcherThread 的 Looper 创建的,因此 DispatcherHandler 发送的消息将切换到工作线程 (即 DispatcherThread) 中处理,即 DispatcherHandler 的 handleMessage 方法会在工作线程中执行。

3. Picasso 类中的 load 方法

获取到 Picasso 实例后,紧接着调用 Picasso 类的 load 方法,该方法主要作用就是创建并返回一个 RequestCreator 实例。

RequestCreator 类的主要作用就是创建 Request 对象,并提供了一系列的 into 方法来开始图片请求。

先来看一下 Picasso 类中的 load 方法:

public RequestCreator load(@Nullable String path) {
  // 如果传进来的 path 为 null,创建并返回一个
  // Uri 为 null 的 RequestCreator 对象。
  if (path == null) {
    return new RequestCreator(this, null, 0);
  }
  // 如果 path 为空字符串,抛出异常。
  if (path.trim().length() == 0) {
    throw new IllegalArgumentException("Path must not be empty.");
  }
  return load(Uri.parse(path));
}

最后 path 不为 null 也不为空字符串,则调用 Uri.parse(path) 方法对 path 进行解析并返回 一个 Uri 对象传给 load(@Nullable Uri uri) 方法。

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

该方法创建并返回一个 RequestCreator 实例,看一下 RequestCreator 类的构造函数:

private final Picasso picasso;
private final Request.Builder data;

RequestCreator(Picasso picasso, Uri uri, int resourceId) {
  this.picasso = picasso;
  this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
}

可以看到 RequestCreator 对象持有了 Picasso 的一份引用,然后创建了 Request.Builder 类的实例 data,这里又用到了 Builder 设计模式。

Request.Builder 的构造函数为:

Builder(Uri uri, int resourceId, Bitmap.Config bitmapConfig) {
  this.uri = uri;
  this.resourceId = resourceId;
  this.config = bitmapConfig;
}

到这里 RequestCreator 就创建完成了,接下来就可以调用 RequestCreator 类中的许多方法,如 placeholder 方法,centerCrop 方法等,我们这里直接前往 into 方法。

4. RequestCreator 类的 into 方法

经过 load 方法后,紧接着就来到了 into 方法,该方法是整个 API 调用流程的最后一步,可以说是整个调用流程的重头戏,因此篇幅也比较大。

我们往 into 方法传的是 ImageView 实例,看一下该方法源码:

public void into(ImageView target) {
  into(target, null);
}
public void into(ImageView target, Callback callback) {
  // 记录开始处理的时间戳
  long started = System.nanoTime();
  // 检查当前方法是否在主线程进行调用,如果不是抛出异常
  checkMain();
  // ImageView 实例 target 不能为 null,否则抛异常
  if (target == null) {
    throw new IllegalArgumentException("Target must not be null.");
  }
  // data 即前面的 Request.Builder 实例
  // 如果 data 中没有图片(例如传入的 path 为 null)
  // 直接对该 target 取消请求,并设置占位图如果有设置 placeholder
  if (!data.hasImage()) {
    picasso.cancelRequest(target);
    if (setPlaceholder) {
      setPlaceholder(target, getPlaceholderDrawable());
    }
    return;
  }
  // 创建 Request 实例
  Request request = createRequest(started);
  // 为当前 Request 生成一个 requestKey,用来标记 Request
  String requestKey = createKey(request);
  // 如果当前的 memoryPolicy 允许从缓存中读取图片
  // 从 Cache 中获取 requestKey 对应的 Bitmap,如果该 Bitmap 存在
  // 则取消当前请求,直接为 target 设置该 Bitmap
  if (shouldReadFromMemoryCache(memoryPolicy)) {
    Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
    if (bitmap != null) {
      picasso.cancelRequest(target);
      setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
      if (callback != null) {
        callback.onSuccess();
      }
      return;
    }
  }

  // 前面缓存中没有查找到图片,从这里开始请求
  // 先设置 placeholder 如果有配置的话 
  if (setPlaceholder) {
    setPlaceholder(target, getPlaceholderDrawable());
  }

  // 创建一个 ImageViewAction 的实例
  Action action = new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
          errorDrawable, requestKey, tag, callback, noFade);
  // 向 picasso 提交该 Action 实例
  picasso.enqueueAndSubmit(action);
}

这里省略了部分与 Picasso.get().load(uri).into(imageView) 调用不相关的代码,代码中的注释只是大致逻辑流程,接下来按 into 方法的代码逻辑对一些细节进行分析。

4.1 设置占位图 (placeholder) 的实现

从 into 方法中可以看到,有两处需要设置占位图,设置占位图的逻辑是:

if (setPlaceholder) {
  setPlaceholder(target, getPlaceholderDrawable());
}

看一下 getPlaceholderDrawable 方法:

private Drawable getPlaceholderDrawable() {
  if (placeholderResId != 0) {
    if (Build.VERSION.SDK_INT >= 21) {
      return picasso.context.getDrawable(placeholderResId);
    } else if (Build.VERSION.SDK_INT >= 16) {
      return picasso.context.getResources().getDrawable(placeholderResId);
    } else {
      TypedValue value = new TypedValue();
      picasso.context.getResources().getValue(placeholderResId, value, true);
      return picasso.context.getResources().getDrawable(value.resourceId);
    }
  } else {
    return placeholderDrawable;
  }
}

该方法根据当前运行的 Android SDK 版本进行不同的方法调用来通过 placeholderResId 获取到一个 Drawable 实例。

获取到占位图 Drawable 后接下来就可以进行设置了,setPlaceholder 方法为 PicassoDrawable 类中的静态方法,调用该方法后 ImageView 就可以显示占位图了。

static void setPlaceholder(ImageView target, Drawable placeholderDrawable) {
  target.setImageDrawable(placeholderDrawable);
  if (target.getDrawable() instanceof Animatable) {
    ((Animatable) target.getDrawable()).start();
  }
}

4.2 Request 实例的创建

Request 对象是通过 createRequest 方法创建的:

private static final AtomicInteger nextId = new AtomicInteger();

private Request createRequest(long started) {
  // 为 Request 实例分配下一个 id
  int id = nextId.getAndIncrement();
  // 创建 Request 实例
  Request request = data.build();
  request.id = id;
  request.started = started;
  return request;
}

Request 实例通过调用 Request.Builder 类的 build 方法创建:

public Request build() {
  // 先验证当前的配置参数是否合法
  // centerInside 和 centerCrop 方法不能同时用
  if (centerInside && centerCrop) {
    throw new IllegalStateException("Center crop and center inside can not be used together.");
  }
  // centerCrop 方法需要与 resize 方法同用
  if (centerCrop && (targetWidth == 0 && targetHeight == 0)) {
    throw new IllegalStateException(
        "Center crop requires calling resize with positive width and height.");
  }
  // centerInside 方法需要与 resize 方法同用
  if (centerInside && (targetWidth == 0 && targetHeight == 0)) {
    throw new IllegalStateException(
        "Center inside requires calling resize with positive width and height.");
  }
  // 设置 priority
  if (priority == null) {
    priority = Priority.NORMAL;
  }
  // 创建 Request 实例
  return new Request(uri, resourceId, stableKey, transformations, targetWidth, targetHeight,
      centerCrop, centerInside, centerCropGravity, onlyScaleDown, rotationDegrees,
      rotationPivotX, rotationPivotY, hasRotationPivot, purgeable, config, priority);
}

该方法在最后一步创建了 Request 实例,看一下 Request 类的构造函数:

public final Uri uri;
public final int resourceId;
public final String stableKey;
public final List transformations;
public final int targetWidth;
public final int targetHeight;
public final boolean centerCrop;
public final int centerCropGravity;
public final boolean centerInside;
public final boolean onlyScaleDown;
public final float rotationDegrees;
public final float rotationPivotX;
public final float rotationPivotY;
public final boolean hasRotationPivot;
public final boolean purgeable;
public final Bitmap.Config config;
public final Priority priority;

private Request(Uri uri, int resourceId, String stableKey, List transformations,
    int targetWidth, int targetHeight, boolean centerCrop, boolean centerInside,
    int centerCropGravity, boolean onlyScaleDown, float rotationDegrees,
    float rotationPivotX, float rotationPivotY, boolean hasRotationPivot,
    boolean purgeable, Bitmap.Config config, Priority priority) {
  // 图片的 Uri,与 resourceId 不能共存
  this.uri = uri;
  // 图片的 resourceId,与 Uri 不能共存
  this.resourceId = resourceId;
  this.stableKey = stableKey;
  // 用来对 Bitmap 进行转换的一系列 Transformation
  if (transformations == null) {
    this.transformations = null;
  } else {
    this.transformations = unmodifiableList(transformations);
  }
  // resize 方法设置的图片宽度和高度
  this.targetWidth = targetWidth;
  this.targetHeight = targetHeight;
  // 图片 scaleType 是否为 centerCrop,与 centerInside 不共存
  this.centerCrop = centerCrop;
  // 图片 scaleType 是否为 centerInside,与 centerCrop 不共存
  this.centerInside = centerInside;
  // 如果设置了 centerCrop,centerCropGravity 用来设置中心的偏移量
  this.centerCropGravity = centerCropGravity;
  this.onlyScaleDown = onlyScaleDown;
  // 图片旋转的度数
  this.rotationDegrees = rotationDegrees;
  this.rotationPivotX = rotationPivotX;
  this.rotationPivotY = rotationPivotY;
  this.hasRotationPivot = hasRotationPivot;
  this.purgeable = purgeable;
  this.config = config;
  // 当前请求的优先级
  this.priority = priority;
}

Request 实例创建完毕。

4.3 requestKey 的创建

requestKey 用来标识一个 Request,requestKey 通过调用 createKey 方法实现:

static final StringBuilder MAIN_THREAD_KEY_BUILDER = new StringBuilder();

static String createKey(Request data) {
  String result = createKey(data, MAIN_THREAD_KEY_BUILDER);
  MAIN_THREAD_KEY_BUILDER.setLength(0);
  return result;
}
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) {
    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();
}

该方法根据当前 Request 配置的参数来生成对应的 requestKey。

4.4 MemoryPolicy 的实现

into 方法通过调用 shouldReadFromMemoryCache 方法来判断是否应该从 Cache 中读取当前 requestKey 对应的 Bitmap。

shouldReadFromMemoryCache 方法是枚举类型 MemoryPolicy 中的一个静态方法,MemoryPolicy 源码如下:

public enum MemoryPolicy {

  NO_CACHE(1 << 0),
  NO_STORE(1 << 1);

  static boolean shouldReadFromMemoryCache(int memoryPolicy) {
    return (memoryPolicy & MemoryPolicy.NO_CACHE.index) == 0;
  }

  static boolean shouldWriteToMemoryCache(int memoryPolicy) {
    return (memoryPolicy & MemoryPolicy.NO_STORE.index) == 0;
  }

  final int index;

  MemoryPolicy(int index) {
    this.index = index;
  }
    
}

可以看到 MemoryPolicy 用到了一些位操作。

MemoryPolicy 共两种枚举类型,NO_CACHE 和 NO_STORE,NO_CACHE 的 index 为 1 (二进制为 1),NO_STORE 的 index 为 2 (二进制为 10)。

shouldReadFromMemoryCache 方法返回 true 如果 (memoryPolicy & MemoryPolicy.NO_CACHE.index) == 0 ,即 memoryPolicy 为 0 。返回 true 表示当前 memoryPolicy 允许从 Cache 中读取图片。

shouldWriteToMemoryCache 方法在满足 (memoryPolicy & MemoryPolicy.NO_STORE.index) == 0 的条件下返回 true,即 memoryPolicy 为 0。返回 true 表示当前 memoryPolicy 允许向 Cache 中写入图片。

而在没有配置 memoryPolicy 的情况下,memoryPolicy 默认为 0,因此这两个方法这里都会返回 true。

4.5 Action 实例的创建

Picasso 将一次图片获取活动封装成一个 Action 实例。

Action 为抽象类,包含两个必须实现的抽象方法,complete 方法和 error 方法,分别表示该次图片获取活动完成或出错。

abstract void complete(Bitmap result, Picasso.LoadedFrom from);
abstract void error(Exception e);

Picasso 提供了不同的 Action 子类来对应不同的图片获取活动。

我们这里用到的是 ImageViewAction,ImageViewAction 用于将获取到的 Bitmap 加载到 ImageView 中:

class ImageViewAction extends Action {

  Callback callback;

  ImageViewAction(Picasso picasso, ImageView imageView, Request data, int memoryPolicy,
      int networkPolicy, int errorResId, Drawable errorDrawable, String key, Object tag,
      Callback callback, boolean noFade) {
    super(picasso, imageView, data, memoryPolicy, networkPolicy, errorResId, errorDrawable, key,
        tag, noFade);
    this.callback = callback;
  }

  @Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
    // 获取要加载图片进去的 ImageView
    ImageView target = this.target.get();
    if (target == null) return;
    Context context = picasso.context;
    boolean indicatorsEnabled = picasso.indicatorsEnabled;
    // 为 target 设置 Bitmap
    PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
    // 回调 callback
    if (callback != null) callback.onSuccess();
  }

  @Override public void error(Exception e) {
    ImageView target = this.target.get();
    if (target == null) return;
    // 获取占位图 Drawable 实例,如果该 Drawable 实现了 Animatable 接口
    // 这时候就应该停止该 Animatable
    Drawable placeholder = target.getDrawable();
    if (placeholder instanceof Animatable) ((Animatable) placeholder).stop();
    // 设置错误情况下的图片
    if (errorResId != 0) {
      target.setImageResource(errorResId);
    } else if (errorDrawable != null) {
      target.setImageDrawable(errorDrawable);
    }
    if (callback != null) callback.onError(e);
  }

  @Override void cancel() {
    super.cancel();
    if (callback != null) callback = null;
  }
}

可以看到 ImageViewAction 类继承自 Action,泛型参数为 ImageView 作为该 Action 的 target。

ImageViewAction 实现了 complete 方法和 error 方法并重写了 cancel 方法。

4.6 Action 实例的提交

Action 实例创建完毕后,就可以调用 Picasso 类中的 enqueueAndSubmit 方法提交该 Action 实例了,然后从这里开始一次图片获取活动,由于这部分代码过于庞大,从 enqueueAndSubmit 方法开始放在下一节分析。

5. Picasso 处理 Action 的实现

Picasso 处理 Action 从 Picasso 类的 enqueueAndSubmit 方法开始:

void enqueueAndSubmit(Action action) {
  // 省略部分代码...
  // 调用 submit 方法提交该 Action
  submit(action);
}
void submit(Action action) {
  dispatcher.dispatchSubmit(action);
}

submit 方法又调用了 Dispatcher 类的 dispatchSubmit 方法,Picasso 类这时候就将此 Action 交接给 Dispatcher 类进行处理。

5.1 Dispatcher 类提交 Action 的实现

Picasso 通过调用 Dispatcher 类的 dispatchSubmit 方法开始提交该 Action 实例:

void dispatchSubmit(Action action) {
  handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
}

该方法使用 handler 发送了一条 REQUEST_SUBMIT 信息,我们在 2.3 小节中可以看到,该 handler 即 DispatcherHandler 实例,发送该消息后会在工作线程回调到 DispatcherHandler 中的 handleMessage 方法,然后从该方法接着会调用 Dispatcher 类的 performSubmit 方法:

void performSubmit(Action action) {
  performSubmit(action, true);
}
void performSubmit(Action action, boolean dismissFailed) {
  // 创建 BitmapHunter 实例
  BitmapHunter hunter = forRequest(action.getPicasso(), this, cache, stats, action);
  // 使用配置好的 PicassoExecutorService 提交该 BitmapHunter 实例
  hunter.future = service.submit(hunter);
}

上面的代码做了一些简化,删除了与当前逻辑无关的代码。

该方法通过调用 forRequest 方法来创建 BitmapHunter 实例,而 forRequest 方法是 BitmapHunter 类中的静态方法。

从上面的代码可以看到,接下来的工作就交给 BitmapHunter 来完成了。

5.2 BitmapHunter 类的实现

BitmapHunter 类的主要职责是结合 Action 和 RequestHandler 来获取 Bitmap 实例。

BitmapHunter 提供了静态方法 forRequest 来创建实例:

static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats,
    Action action) {
  // 获取 Action 中的 Request 对象
  Request request = action.getRequest();
  // 获取 Picasso 配置的全部 RequestHandler
  List requestHandlers = picasso.getRequestHandlers();
  // 从下标 0 开始迭代全部 RequestHandler,如果该 RequestHandler 能
  // 处理该 Request,则用该 RequestHandler 创建 BitmapHunter 实例并返回。
  for (int i = 0, count = requestHandlers.size(); i < count; i++) {
    RequestHandler requestHandler = requestHandlers.get(i);
    if (requestHandler.canHandleRequest(request)) {
      return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);
    }
  }
  // 没有 RequestHandler 能处理该 Request,传入 ERRORING_HANDLER
  return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);
}

这里的 requestHandlers 部分用到了责任链模式 (Chains of Responsibility)。

接着看下 BitmapHunter 的构造函数:

final int sequence;
final Picasso picasso;
final Dispatcher dispatcher;
final Cache cache;
final Stats stats;
final String key;
final Request data;
final int memoryPolicy;
int networkPolicy;
final RequestHandler requestHandler;

Action action;
List actions;
Bitmap result;
Future future;
Picasso.LoadedFrom loadedFrom;
Exception exception;
int exifOrientation;
int retryCount;
Priority priority;

BitmapHunter(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action,
    RequestHandler requestHandler) {
  this.sequence = SEQUENCE_GENERATOR.incrementAndGet();
  this.picasso = picasso;
  this.dispatcher = dispatcher;
  this.cache = cache;
  this.stats = stats;
  this.action = action;
  this.key = action.getKey();
  this.data = action.getRequest();
  this.priority = action.getPriority();
  this.memoryPolicy = action.getMemoryPolicy();
  this.networkPolicy = action.getNetworkPolicy();
  this.requestHandler = requestHandler;
  this.retryCount = requestHandler.getRetryCount();
}

BitmapHunter 类实现了 Runnable 接口,因此当前面调用 service.submit(hunter) ,BitmapHunter 类中的 run 方法会在线程池中运行:

@Override public void run() {
  try {
    result = hunt();
    if (result == null) {
      dispatcher.dispatchFailed(this);
    } else {
      dispatcher.dispatchComplete(this);
    }
  } catch (Exception e) {
    // 处理一堆异常...
  }
}

run 方法主要通过调用 hunt 方法获取返回的 Bitmap 并赋值给 result,如果 result 不为 null,则调用 Dispatcher 类的 dispatchComplete 方法,否则调用 dispatchFailed 方法。

因此获取 Bitmap 的具体逻辑就在 hunt 方法中完成:

Bitmap hunt() throws IOException {
  Bitmap bitmap = null;
  // 先从缓存中查找 key 对应的 Bitmap,该 key 即之前创建的 requestKey
  if (shouldReadFromMemoryCache(memoryPolicy)) {
    bitmap = cache.get(key);
    if (bitmap != null) {
      stats.dispatchCacheHit();
      loadedFrom = MEMORY;
      return bitmap;
    }
  }
  // 调用 RequestHandler 的 load 方法获取 RequestHandler.Result 实例
  // Picasso 将 RequestHandler 加载的结果封装成一个 Result 对象
  // 我们这里调用的是 NetworkRequestHandler 类中的 load 方法
  RequestHandler.Result result = requestHandler.load(data, networkPolicy);
  if (result != null) {
    loadedFrom = result.getLoadedFrom();
    exifOrientation = result.getExifOrientation();
    bitmap = result.getBitmap();
    // result 中的 bitmap 为 null,将 source 中的字节流编码成 Bitmap
    if (bitmap == null) {
      Source source = result.getSource();
      try {
        bitmap = decodeStream(source, data);
      } finally {
        try {
          source.close();
        } catch (IOException ignored) {
        }
      }
    }
  }
  return bitmap;
}

hunt 方法结束,返回获取到的 Bitmap 实例,回到 BitmapHunter 的 run 方法,这时候如果返回的 Bitmap 实例不为null,就调用 Dispatcher 类中的 dispatchComplete 方法,这样剩下的工作又交接给了 Dispatcher。

5.3 Dispatcher 类请求完成的实现

BitmapHunter 获取到不为 null 的 Bitmap 实例后,调用 Dispatcher 类中的 dispatchComplete 方法分发其完成事件:

void dispatchComplete(BitmapHunter hunter) {
  handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));
}

handler 发送该消息后,接着又在工作线程中回调到了 Dispatcher 类的 performComplete 方法:

void performComplete(BitmapHunter hunter) {
  if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
    cache.set(hunter.getKey(), hunter.getResult());
  }
  batch(hunter);
}

这时候 Picasso 将 Bitmap 存入到了缓存中,然后调用 batch 方法:

private void batch(BitmapHunter hunter) {
  if (hunter.isCancelled()) {
    return;
  }
  if (hunter.result != null) {
    hunter.result.prepareToDraw();
  }
  batch.add(hunter);
  if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
    handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
  }
}

handler 最后发送了 HUNTER_DELAY_NEXT_BATCH 消息,发送该消息后又回调到了 Dispatcher 类的 performBatchComplete 方法:

void performBatchComplete() {
  batch.clear();  mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
}

这时候发送消息的是 mainThreadHandler,切换到主线程来了,发送的消息为 HUNTER_BATCH_COMPLETE,该消息回调到了 Picasso 类中的 HANDLER 实例中:

static final Handler HANDLER = new Handler(Looper.getMainLooper()) {
  @Override public void handleMessage(Message msg) {
    switch (msg.what) {
      case HUNTER_BATCH_COMPLETE: {
        List batch = (List) msg.obj;
        for (int i = 0, n = batch.size(); i < n; i++) {
          BitmapHunter hunter = batch.get(i);
          hunter.picasso.complete(hunter);
        }
        break;
      }
      default:
        throw new AssertionError("Unknown handler message received: " + msg.what);
    }
  }
};

对每个 BitmapHunter 实例调用 Picasso 类中的 complete 方法:

void complete(BitmapHunter hunter) {
  Action single = hunter.getAction();
  if (single == null) return;

  Uri uri = hunter.getData().uri;
  Exception exception = hunter.getException();
    
  Bitmap result = hunter.getResult();
  LoadedFrom from = hunter.getLoadedFrom();
  
  deliverAction(result, from, single, exception);

  if (listener != null && exception != null) {
    listener.onImageLoadFailed(this, uri, exception);
  }
}
private void deliverAction(Bitmap result, LoadedFrom from, Action action, Exception e) {
  if (action.isCancelled()) {
    return;
  }
  if (result != null) {
    if (from == null) {
      throw new AssertionError("LoadedFrom cannot be null.");
    }
    action.complete(result, from);
    }
  } else {
    action.error(e);
  }
}

我们之前提交的是 ImageViewAction 实例,因此这时候会回调到 ImageViewAction 类的 complete 方法:

@Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
  ImageView target = this.target.get();
  if (target == null) return;

  Context context = picasso.context;
  boolean indicatorsEnabled = picasso.indicatorsEnabled;
  PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);

  if (callback != null) {
    callback.onSuccess();
  }
}

最后 setBitmap:

static void setBitmap(ImageView target, Context context, Bitmap bitmap,
    Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
  Drawable placeholder = target.getDrawable();
  if (placeholder instanceof Animatable) {
    ((Animatable) placeholder).stop();
  }
  PicassoDrawable drawable =
      new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
  target.setImageDrawable(drawable);
}

结束该方法后,这时候图片终于成功在界面显示了,整个调用流程到此也终于结束。

6. 参考

  • Picasso: A powerful image downloading and caching library for Android
  • 如何正确地写出单例模式
  • ContentProvider | Android Developers

你可能感兴趣的:(Android图片加载框架Picasso源码分析(基于Picasso 2.71828))