Picasso 源码分析

902 阅读8分钟

基本使用

Picasso.get()
    .load(Uri.parse(getItem(position)?.pic))
    .placeholder(R.drawable.ic_android_black_24dp)
    .into(holder.iv_img)

Picasso 对象创建

Picasso.get()构造 Picasso 对象并返回, 双重锁检查的单例模式

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;
}

Picasso.Builder 对象

  • 成员变量
    private final Context context; // 上下文 application 级别的
    private Downloader downloader; // 下载器 OkHttp3Downloader
    private ExecutorService service; // 执行异步任务的线程池
    private Cache cache;  // LruCache
    private Listener listener; 
    private RequestTransformer transformer; 
    private List<RequestHandler> requestHandlers; // RequestHandler, 真正处理加载逻辑
    private Bitmap.Config defaultBitmapConfig;
    
    private boolean indicatorsEnabled;
    private boolean loggingEnabled;
    
  • build 方法
    根据builder的成员变量调用 Picasso 对象的构造方法创建 Picasso 对象, 因为这个builder是程序内创建的, 因此默认这些成员变量都没有赋值, build方法中会为这些变量提供默认值
    public Picasso build() {
      Context context = this.context;
    
      // 默认的 downloader
      if (downloader == null) {
        downloader = new OkHttp3Downloader(context);
      }
      // 默认的 cache
      if (cache == null) {
        cache = new LruCache(context);
      }
      // 默认的线程池
      if (service == null) {
        service = new PicassoExecutorService();
      }
      // 默认的 transformer
      if (transformer == null) {
        transformer = RequestTransformer.IDENTITY;
      }
    
      Stats stats = new Stats(cache);
      // 创建 Dispatcher 对象
      Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
    
      return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
          defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
    }
    

Picasso 对象

  • 成员变量
    static volatile Picasso singleton = null;
    
    private final Listener listener;
    private final RequestTransformer requestTransformer;
    private final CleanupThread cleanupThread;
    private final List<RequestHandler> requestHandlers;
    
    final Context context;
    final Dispatcher dispatcher;
    final Cache cache;
    final Stats stats;
    final Map<Object, Action> targetToAction;
    final Map<ImageView, DeferredRequestCreator> targetToDeferredRequestCreator;
    final ReferenceQueue<Object> referenceQueue;
    final Bitmap.Config defaultBitmapConfig;
    
    boolean indicatorsEnabled;
    volatile boolean loggingEnabled;
    
    boolean shutdown;
    
  • 构造方法
    将Builder中传递的参数赋值给自身的成员变量, 额外初始化一些对象
    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;
    
      int builtInHandlers = 7; // Adjust this as internal handlers are added or removed.
      int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);
      List<RequestHandler> allRequestHandlers = new ArrayList<>(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);
    
      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();
    }
    

RequestCreator 对象

  • 创建
    通过调用Picasso类中提供的一系列 load() 重载方法来创建, 最终都转化成对入参为Uri对象的load方法的调用

    public RequestCreator load(@Nullable Uri uri) {
        // this是Picasso对象
        return new RequestCreator(this, uri, 0);
      }
      
    RequestCreator(Picasso picasso, Uri uri, int resourceId) {
      if (picasso.shutdown) {
        throw new IllegalStateException(
            "Picasso instance already shut down. Cannot submit new requests.");
      }
      // 保存picasso对象为成员变量
      this.picasso = picasso;
      // 创建一个 Request.Builder 保存为 data 成员
      // Request.Builder 用来创建一个 Request 对象
      this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
    }
    
  • 成员变量

    private static final AtomicInteger nextId = new AtomicInteger();
    
    private final Picasso picasso;
    private final Request.Builder data;
    
    private boolean noFade;
    private boolean deferred;
    private boolean setPlaceholder = true;
    private int placeholderResId; // 占位符 (资源id形式)
    private int errorResId;  // 请求错误的占位符 (资源id形式)
    private int memoryPolicy;
    private int networkPolicy;
    private Drawable placeholderDrawable; // 占位符 (Drawable形式)
    private Drawable errorDrawable;  // 请求错误的占位符 (Drawable形式)
    private Object tag;
    
  • RequestCreator 作用
    Picasso中将以此图片加载请求分成两个部分, 一个是 RequestCreator 负责的部分, 另一个是 Request 负责的部分, Request 真正描述了对目标图片的加载请求数据, 包括目标图片的地址、对目标图片的转换操作、图片的宽高、加载请求的优先级等等数据; 而 RequestCreator 则是则是通过 data 域持有 Request.Builder 可以用来构建Request对象, 此外它自身也提供了本次图片加载的一些策略信息, 比如图片加载出来前的占位符、图片加载失败的占位符等信息。

  • RequestCreator 中的方法
    RequestCreator中的方法主要用来对其自身和Request.Builder中成员变量赋值, 支持链式调用。 方法的具体实现大多符合下面两种模式, 一种直接复制给自身成员, 另一种代理给 data 成员。

    public RequestCreator placeholder(@DrawableRes int placeholderResId) {
      // 省略异常检测
      this.placeholderResId = placeholderResId;
      return this;
    }
    
    public RequestCreator transform(@NonNull Transformation transformation) {
      // 交给 Request.Builder
      data.transform(transformation);
      return this;
    }
    
  • into 方法
    into方法之前主要用来对本次图片加载请求进行配置, 调用into之后才会真正开始图片的加载。通过分析源码可以将一个正常的图片加载请求描述成以下几个步骤:
    1、通过 RequestCreator 的 data 成员创建请求对象 Request
    2、尝试从缓存中加载图片 (加载成功则返回)
    3、设置图片占位符
    4、构造一个Action对象, 表示该图片加载任务
    5、通过picasso对象提交Action任务

    // 调用带Callbcak的into方法
    public void into(ImageView target) {
        into(target, null);
    }
    
    public void into(ImageView target, Callback callback) {
      long started = System.nanoTime();
      checkMain();
    
      if (target == null) {
        throw new IllegalArgumentException("Target must not be null.");
      }
      // 图片uri或者资源id为空
      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) {
          if (setPlaceholder) {
            setPlaceholder(target, getPlaceholderDrawable());
          }
          picasso.defer(target, new DeferredRequestCreator(this, target, callback));
          return;
        }
        data.resize(width, height);
      }
      // 通过 data 成员创建 Reqest 对象
      Request request = createRequest(started);
      String requestKey = createKey(request);
    
      // 是否从缓存获取
      if (shouldReadFromMemoryCache(memoryPolicy)) {
        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对象提交action
      picasso.enqueueAndSubmit(action);
    }
    

缓存

  • Cache 接口

    get(String): Bitmap
    set(String, Bitmap): void
    size(): int
    maxSize(): int // 缓存的最大大小 bytes
    clear(): void 
    ckearKeyUri(String): void
    
  • LruCache
    Cache 接口的实现类、内部直接继承了Android系统中的LruCache

Action

抽象类, 描述一个图片加载任务

  • 成员变量

    final Picasso picasso;
    final Request request;
    // 对目标(加载到哪, 比如ImageView)的一个弱引用
    final WeakReference<T> target;
    final boolean noFade;
    final int memoryPolicy;
    final int networkPolicy;
    final int errorResId;
    final Drawable errorDrawable;
    final String key;
    final Object tag;
    
    boolean willReplay;
    boolean cancelled;
    
  • 抽象方法

    abstract void complete(Bitmap result, Picasso.LoadedFrom from);
    abstract void error(Exception e);
    
  • ImageViewAction
    Action的一个子类, 下面是对Action中两个方法的重新, 主要逻辑就是在加载成功的时候设置ImageView的图片, 加载失败的时候设置为占位的图片

    @Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
      // 获取target
      ImageView target = this.target.get();
      if (target == null) {
        return;
      }
    
      Context context = picasso.context;
      boolean indicatorsEnabled = picasso.indicatorsEnabled;
      // 设置ImageView图片
      PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
    
      if (callback != null) {
        callback.onSuccess();
      }
    }
    
    @Override public void error(Exception e) {
      ImageView target = this.target.get();
      if (target == null) {
        return;
      }
      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);
      }
    }
    

picasso.enqueueAndSubmit(action)

该方法最终会通过Dispatcher将Action提交

void enqueueAndSubmit(Action action) {
  Object target = action.getTarget();
  if (target != null && targetToAction.get(target) != action) {
    // This will also check we are on the main thread.
    cancelExistingRequest(target);
    targetToAction.put(target, action);
  }
  submit(action);
}

void submit(Action action) {
    dispatcher.dispatchSubmit(action);
}

Dispatcher

1、Picasso对象中dispatcher成员所属的类
2、主要用来调度图片加载任务的

  • DispatcherThread内部类
    继承 HandlerThread 类, 是一个带有Looper的线程

    static class DispatcherThread extends HandlerThread {
      DispatcherThread() {
        super(Utils.THREAD_PREFIX + DISPATCHER_THREAD_NAME, THREAD_PRIORITY_BACKGROUND);
      }
    }
    
  • DispatcherHandler内部类

    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 REQUEST_CANCEL: {
            Action action = (Action) msg.obj;
            dispatcher.performCancel(action);
            break;
          }
          case TAG_PAUSE: {
            Object tag = msg.obj;
            dispatcher.performPauseTag(tag);
            break;
          }
          case TAG_RESUME: {
            Object tag = msg.obj;
            dispatcher.performResumeTag(tag);
            break;
          }
          case HUNTER_COMPLETE: {
            BitmapHunter hunter = (BitmapHunter) msg.obj;
            dispatcher.performComplete(hunter);
            break;
          }
          case HUNTER_RETRY: {
            BitmapHunter hunter = (BitmapHunter) msg.obj;
            dispatcher.performRetry(hunter);
            break;
          }
          case HUNTER_DECODE_FAILED: {
            BitmapHunter hunter = (BitmapHunter) msg.obj;
            dispatcher.performError(hunter, false);
            break;
          }
          case HUNTER_DELAY_NEXT_BATCH: {
            dispatcher.performBatchComplete();
            break;
          }
          case NETWORK_STATE_CHANGE: {
            NetworkInfo info = (NetworkInfo) msg.obj;
            dispatcher.performNetworkStateChange(info);
            break;
          }
          case AIRPLANE_MODE_CHANGE: {
            dispatcher.performAirplaneModeChange(msg.arg1 == AIRPLANE_MODE_ON);
            break;
          }
          default:
            Picasso.HANDLER.post(new Runnable() {
              @Override public void run() {
                throw new AssertionError("Unknown handler message received: " + msg.what);
              }
            });
        }
      }
    }
    
  • DispatcherThread & DispatcherHandler 的初始化

    this.dispatcherThread = new DispatcherThread();
    //  启动 dispatcherThread 线程
    this.dispatcherThread.start();
    // handler 是 dispatcherThread 的handler
    this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
    
  • dispatchSubmit 方法
    上面提到Action最终通过Dispatcher的dispatchSubmit进行提交。可以看到主要逻辑就是将Action包装到Message, 并通过 handler将消息发送到 DispatcherThread 中进行处理(单独的线程)。从上面 DispatcherHandler 对 REQUEST_SUBMIT 消息的处理来看, 最终会进入 performSubmit 方法

    void dispatchSubmit(Action action) {
      handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
    }
    
    
    void performSubmit(Action action, boolean dismissFailed) {
      if (pausedTags.contains(action.getTag())) {
        pausedActions.put(action.getTarget(), action);
        if (action.getPicasso().loggingEnabled) {
          log(OWNER_DISPATCHER, VERB_PAUSED, action.request.logId(),
              "because tag '" + action.getTag() + "' is paused");
        }
        return;
      }
      // 从 hunterMap 获取 BitmapHunter 
      BitmapHunter hunter = hunterMap.get(action.getKey());
      if (hunter != null) {
        hunter.attach(action);
        return;
      }
    
      if (service.isShutdown()) {
        if (action.getPicasso().loggingEnabled) {
          log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");
        }
        return;
      }
      
      // 创建 BitmapHunter
      hunter = forRequest(action.getPicasso(), this, cache, stats, action);
      // 提交 BitmapHunter, service是一个线程池对象 PicassoExecutorService
      hunter.future = service.submit(hunter);
      // BitmapHunter 保存到 hunterMap
      hunterMap.put(action.getKey(), hunter);
      if (dismissFailed) {
        failedActions.remove(action.getTarget());
      }
    
      if (action.getPicasso().loggingEnabled) {
        log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
      }
    }
    

BitmapHunter

实现Runnable接口

  • run方法

    @Override public void run() {
      try {
        updateThreadName(data);
        // 主要逻辑
        result = hunt();
        // 对结果的处理, 涉及到线程的切换
        if (result == null) {
          // 图片加载完成
          dispatcher.dispatchFailed(this);
        } else {
          // 图片加载失败
          dispatcher.dispatchComplete(this);
        }
      } catch (NetworkRequestHandler.ResponseException e) {
        // 异常处理
      } finally {
        Thread.currentThread().setName(Utils.THREAD_IDLE_NAME);
      }
    }
    
  • hunt方法
    核心逻辑是利用RequestHandler加载图片

    Bitmap hunt() throws IOException {
      Bitmap bitmap = null;
    
      if (shouldReadFromMemoryCache(memoryPolicy)) {
        bitmap = cache.get(key);
        if (bitmap != null) {
          stats.dispatchCacheHit();
          loadedFrom = MEMORY;
          if (picasso.loggingEnabled) {
            log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache");
          }
          return bitmap;
        }
      }
    
      networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
      // 通过RequestHandler去获取图片
      RequestHandler.Result result = requestHandler.load(data, networkPolicy);
      if (result != null) {
        loadedFrom = result.getLoadedFrom();
        exifOrientation = result.getExifOrientation();
        bitmap = result.getBitmap();
    
        // If there was no Bitmap then we need to decode it from the stream.
        if (bitmap == null) {
          Source source = result.getSource();
          try {
            // 创建Bitmap对象
            bitmap = decodeStream(source, data);
          } finally {
            try {
              //noinspection ConstantConditions If bitmap is null then source is guranteed non-null.
              source.close();
            } catch (IOException ignored) {
            }
          }
        }
      }
    
      // 进行 transformer相关的操作
      if (bitmap != null) {
        if (picasso.loggingEnabled) {
          log(OWNER_HUNTER, VERB_DECODED, data.logId());
        }
        stats.dispatchBitmapDecoded(bitmap);
        if (data.needsTransformation() || exifOrientation != 0) {
          synchronized (DECODE_LOCK) {
            if (data.needsMatrixTransform() || exifOrientation != 0) {
              bitmap = transformResult(data, bitmap, exifOrientation);
              if (picasso.loggingEnabled) {
                log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());
              }
            }
            if (data.hasCustomTransformations()) {
              bitmap = applyCustomTransformations(data.transformations, bitmap);
              if (picasso.loggingEnabled) {
                log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId(), "from custom transformations");
              }
            }
          }
          if (bitmap != null) {
            stats.dispatchBitmapTransformed(bitmap);
          }
        }
      }
    
      return bitmap;
    }
    

RequestHandler

抽象类, RequestHandler allows you to extend Picasso to load images in ways that are not supported by default in the library. RequestHandler must be subclassed to be used. You will have to override two methods canHandleRequest(Request) and load(Request, int) with your custom logic to load images. You should then register your RequestHandler using Picasso.Builder#addRequestHandler(RequestHandler)

  • NetworkRequestHandler
    RequestHandler的一个实现类
    • load方法
      通过分析源码可知NetworkRequestHandler的load方法利用 OkHttp完成图片下载
      @Override public Result load(Request request, int networkPolicy) throws IOException {
        // 创建 OkHttp 的 Request 对象
        okhttp3.Request downloaderRequest = createRequest(request, networkPolicy);
        // downloader 在 Picasso.Builder 的 build方法中创建 OkHttp3Downloader 
        // OkHttp3Downloader 内部包含一个OKHttpClient实例
        // return client.newCall(request).execute(); OkHttp3Downloader 中 load 方法的实现
        Response response = downloader.load(downloaderRequest);
        ResponseBody body = response.body();
      
        if (!response.isSuccessful()) {
          body.close();
          throw new ResponseException(response.code(), request.networkPolicy);
        }
      
        // Cache response is only null when the response comes fully from the network. Both completely
        // cached and conditionally cached responses will have a non-null cache response.
        Picasso.LoadedFrom loadedFrom = response.cacheResponse() == null ? NETWORK : DISK;
      
        // Sometimes response content length is zero when requests are being replayed. Haven't found
        // root cause to this but retrying the request seems safe to do so.
        if (loadedFrom == DISK && body.contentLength() == 0) {
          body.close();
          throw new ContentLengthException("Received response with 0 content-length header.");
        }
        if (loadedFrom == NETWORK && body.contentLength() > 0) {
          stats.dispatchDownloadFinished(body.contentLength());
        }
        return new Result(body.source(), loadedFrom);
      }
      

OkHttp3Downloader

进行图片下载, 有多个重载的构造方法, 最终都会调用带 OKHttpClient 参数的构造方法, 提供load方法来真正执行图片加载, 主要逻辑是通过 client.newCall(request).execute()完成, 这其实是一个典型的OkHttp同步请求方法, 因为现在的请求已经运行在线程池里面了, 因此可以直接以同步方式执行而不必担心阻塞主线程。

PicassoExecutorService

Picasso中执行图片加载任务的线程池, 继承自 ThreadPoolExecutor。 在Picasso.Builder的build方法中创建