Android图片加载框架 Picasso 源码解析(with->load->into)

基本用法

从最基本的用法开始

Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);

1,with

让我们先从with方法开始

public static Picasso with(Context context) {
    if (singleton == null) {
      synchronized (Picasso.class) {
        if (singleton == null) {
          singleton = new Builder(context).build();
        }
      }
    }
    return singleton;
}
static volatile Picasso singleton = null;

一个标准懒汉式Double Check单例模式,

    /** Start building a new {@link Picasso} instance. */
    public Builder(Context context) {
      if (context == null) {
        throw new IllegalArgumentException("Context must not be null.");
      }
      this.context = context.getApplicationContext();
    }
    /** Create the {@link Picasso} instance. */
    public Picasso build() {
      Context context = this.context;

//下载器
      if (downloader == null) {
        downloader = Utils.createDefaultDownloader(context);
      }
//LRU缓存
      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);
    }
  }

内部使用构建者模式,将downloader(下载图片用),lru缓存(内存缓存),线程池(执行下载解析图片的Runnable),dispatcher(picasso内部事件调度者)初始化
最后我们可以获取一个单例的Picasso实例
让我们接下来看load方法


Android图片加载框架 Picasso 源码解析(with->load->into)_第1张图片
image.png

2,load方法

在picasso类中有4个重载,我们先看String类型的

  public RequestCreator load(String path) {
    if (path == null) {
      return new RequestCreator(this, null, 0);
    }
    if (path.trim().length() == 0) {
      throw new IllegalArgumentException("Path must not be empty.");
    }
    return load(Uri.parse(path));
  }

如果path为空,直接回返回一个RequestCreator,如果不为空,会调用Uri的重载
让我们看看File对象的重载

  public RequestCreator load(File file) {
    if (file == null) {
      return new RequestCreator(this, null, 0);
    }
    return load(Uri.fromFile(file));
  }

同样,最后也是调用Uri的重载,

  /**
   * Start an image request using the specified URI.
   * 

* Passing {@code null} as a {@code uri} will not trigger any request but will set a placeholder, * if one is specified. * * @see #load(File) * @see #load(String) * @see #load(int) */ public RequestCreator load(Uri uri) { return new RequestCreator(this, uri, 0); }

int类型的重载和上面不一样,入参是资源ID

  public RequestCreator load(int resourceId) {
    if (resourceId == 0) {
      throw new IllegalArgumentException("Resource ID must not be zero.");
    }
    return new RequestCreator(this, null, 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,但是还没有发送

Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);

从load方法后,我们实际调用的是RequestCreator对象,我们可以根据需求,调用RequestCreator方法
往Request里修改东西,比如centerCrop等等


Android图片加载框架 Picasso 源码解析(with->load->into)_第2张图片
image.png

3,into

接下来看into方法,into方法有5个重载


Android图片加载框架 Picasso 源码解析(with->load->into)_第3张图片
image.png

我们先从ImageView的重载开始看

  public void into(ImageView target) {
    into(target, null);
  }

调用两个方法的重载

  /**
   * Asynchronously fulfills the request into the specified {@link ImageView} and invokes the
   * target {@link Callback} if it's not {@code null}.
   * 

* Note: The {@link Callback} param is a strong reference and will prevent your * {@link android.app.Activity} or {@link android.app.Fragment} from being garbage collected. If * you use this method, it is strongly recommended you invoke an adjacent * {@link Picasso#cancelRequest(android.widget.ImageView)} call to prevent temporary leaking. */ public void into(ImageView target, Callback callback) { long started = System.nanoTime(); checkMain(); if (target == null) { throw new IllegalArgumentException("Target must not be null."); } 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); } 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 = new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId, errorDrawable, requestKey, tag, callback, noFade); picasso.enqueueAndSubmit(action); }

方法比较长,我们一点点来看

  checkMain();
  static void checkMain() {
    if (!isMain()) {
      throw new IllegalStateException("Method call should happen from the main thread.");
    }
  }

  static boolean isMain() {
    return Looper.getMainLooper().getThread() == Thread.currentThread();
  }

检测线程,如果调用into方法的线程不是主线程会报异常
"Method call should happen from the main thread."

再往下看

if (!data.hasImage()) {
      picasso.cancelRequest(target);
      if (setPlaceholder) {
        setPlaceholder(target, getPlaceholderDrawable());
      }
      return;
}
    boolean hasImage() {
      return uri != null || resourceId != 0;
    }

调用request对象hasImage,如果uri和resourceId都没有,就会取消交易,如果有图片占位符,则会直接显示占位符
接下来看

if (deferred) {
      //fit不能和resize一起使用
      if (data.hasSize()) {
        throw new IllegalStateException("Fit cannot be used with resize.");
      }
      //获取ImageView宽高
      int width = target.getWidth();
      int height = target.getHeight();
      //如果宽和高中有一个是0
      if (width == 0 || height == 0) {
      //如果设置过图片占位符,则会先显示占位符
        if (setPlaceholder) {
          setPlaceholder(target, getPlaceholderDrawable());
        }
        //调用defer方法,延迟调用任务,返
        picasso.defer(target, new DeferredRequestCreator(this, target, callback));
        return;
      }
      //如果target这个ImageView宽高都不为0,则,图片之后会被调整大小和ImageView一样大
      data.resize(width, height);
    }

deferred是一个RequestCreator类的成员变量,是否延期的意思,如果之前调用过RequestCreator的fit方法,会把deferred值为true

  /**
   * Attempt to resize the image to fit exactly into the target {@link ImageView}'s bounds. This
   * will result in delayed execution of the request until the {@link ImageView} has been laid out.
   * 

* Note: This method works only when your target is an {@link ImageView}. */ public RequestCreator fit() { deferred = true; return this; }

让我们继续into方法继续往下看

   Request request = createRequest(started);
 /** Create the request optionally passing it through the request transformer. */
  private Request createRequest(long started) {
    int id = nextId.getAndIncrement();

//这里通过build把request对象,构建好了
    Request request = data.build();
    request.id = id;
    request.started = started;

    boolean loggingEnabled = picasso.loggingEnabled;
    if (loggingEnabled) {
      log(OWNER_MAIN, VERB_CREATED, request.plainId(), request.toString());
    }

//如果之前有设置过requestTransformer,会对request进行transform
    Request transformed = picasso.transformRequest(request);
    if (transformed != request) {
      // If the request was changed, copy over the id and timestamp from the original.
      transformed.id = id;
      transformed.started = started;

      if (loggingEnabled) {
        log(OWNER_MAIN, VERB_CHANGED, transformed.logId(), "into " + transformed);
      }
    }

    return transformed;
  }

我们继续从into往下看

String requestKey = createKey(request);

这个方法,主要是之后图片内存缓存标志
接下来看

    //判断是否从内存获取图片
    if (shouldReadFromMemoryCache(memoryPolicy)) {
      //根据之前生成的key,从内存中获取缓存的bitmap
      Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
      //如果内存中有,则会完成请求
      if (bitmap != null) {
        picasso.cancelRequest(target);
        //将图片设置给ImageView
        setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
        if (picasso.loggingEnabled) {
          log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);
        }
        //如果设置了回调,则会调用onSuccess
        if (callback != null) {
          callback.onSuccess();
        }
        return;
      }
    }

我们继续into方法往下看

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

设置占位符

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

    picasso.enqueueAndSubmit(action);

到这里,我们要开始异步请求图片了

  void enqueueAndSubmit(Action action) {
    Object target = action.getTarget();
    if (target != null && targetToAction.get(target) != action) {
      //如果现在map中有一个ImageView正在进行的请求,或者还没有请求
      // This will also check we are on the main thread.
      // 取消当前的请求
      cancelExistingRequest(target);
      targetToAction.put(target, action);//将target为key,action为值,塞进map中
    }
    submit(action);
  }

让我们来看看submit方法

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

调用Dispatcher来之前创建的Action


Android图片加载框架 Picasso 源码解析(with->load->into)_第4张图片
image.png

dispatcher在picasso.builder build方法中创建

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

内部通过handler来传递消息


Android图片加载框架 Picasso 源码解析(with->load->into)_第5张图片
image.png

这个handler再Dispatcher构造方法中初始化,传入的DispatcherThread的looper,DispatcherThread继承自HandlerThread,当这个线程运行的时候,会自动创建一个looper
实际上走的是DispatcherHandler,让我们看看这个类

Android图片加载框架 Picasso 源码解析(with->load->into)_第6张图片
image.png

找到handlerMessage中REQUEST_SUBMIT这个分支,调用了dispatcher的performSubmit的方法

void performSubmit(Action action) {
    performSubmit(action, true);
  }

  void performSubmit(Action action, boolean dismissFailed) {
//如果当前action处于pause状态,会暂停加载,并且放入pausedActions中缓存起来
    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;
    }

//这里我们先忽略这一段
    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;
    }


    hunter = forRequest(action.getPicasso(), this, cache, stats, action);

    hunter.future = service.submit(hunter);
    hunterMap.put(action.getKey(), hunter);
    if (dismissFailed) {
      failedActions.remove(action.getTarget());
    }

    if (action.getPicasso().loggingEnabled) {
      log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
    }
  }

先看看forRequest方法

  static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats,
      Action action) {
    Request request = action.getRequest();
    List requestHandlers = picasso.getRequestHandlers();

    // Index-based loop to avoid allocating an iterator.
    //noinspection ForLoopReplaceableByForEach

//遍历requestHandlers,看看是否有可以处理这个request的handler
    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);
      }
    }
//没找到会把一个错误的默认handler传回去
    return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);
  }

最后会返回一个BitmapHunter,他继承自Runnable
我们继续看之前Dispatcher类的performSubmit方法

hunter.future = service.submit(hunter);

会把刚刚生产的hunter交给线程池去执行,这个线程池也是在picasso构建的时候生成

Android图片加载框架 Picasso 源码解析(with->load->into)_第7张图片
image.png

我们继续看刚刚的BitmapHunter,既然是Runnable,我们先看看他的run方法

@Override public void run() {
    try {
      updateThreadName(data);

      if (picasso.loggingEnabled) {
        log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));
      }

      result = hunt();

      if (result == null) {
        dispatcher.dispatchFailed(this);
      } else {
        dispatcher.dispatchComplete(this);
      }

       ·····

  }

调用hunt方法,result就是返回的bitmap,那么核心从硬盘网络读取bitmap就是hunt方法了

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

    data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
//通过之前匹配的RequestHandler来处理这个request
    RequestHandler.Result result = requestHandler.load(data, networkPolicy);

    ···

    return bitmap;
  }

我们回头看,在picasso构造的时候,已经加入了好多个RequestHandler


Android图片加载框架 Picasso 源码解析(with->load->into)_第8张图片
image.png

从这里我们可以看到picasso支持的几种获取图片的途径
依次是联系人相册,媒体库,ContentResolver,asset,文件,网络

我们随便挑一个看看


Android图片加载框架 Picasso 源码解析(with->load->into)_第9张图片
image.png

如果之前Picasso的load方法传入的是file对象,则会走FileRequestHandler
如果之前传入的string里是http路径,则会走NetworkRequestHandler

让我们来看看NetworkRequestHandler的实现

  @Override public Result load(Request request, int networkPolicy) throws IOException {
//调用downloader的load方法, downloader有两个实现,一个是okhttp,一个是urlconnection
    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;
    }
    // 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 && 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);
  }

最新的方法里,downloader返回的response都不会直接返回bitmap,而是inputstream
最后load方法会把inputsteam流封装人result返回

回到BitmapHunter hunt方法

  Bitmap hunt() throws IOException {

    ```
    RequestHandler.Result result = requestHandler.load(data, networkPolicy);
    if (result != null) {
      loadedFrom = result.getLoadedFrom();
      exifRotation = result.getExifOrientation();

      bitmap = result.getBitmap();

      // If there was no Bitmap then we need to decode it from the stream.
      if (bitmap == null) {
        InputStream is = result.getStream();
        try {
//从inputstream转换成bitmap
          bitmap = decodeStream(is, data);
        } finally {
          Utils.closeQuietly(is);
        }
      }
    }

    if (bitmap != null) {
      if (picasso.loggingEnabled) {
        log(OWNER_HUNTER, VERB_DECODED, data.logId());
      }
      stats.dispatchBitmapDecoded(bitmap);
      if (data.needsTransformation() || exifRotation != 0) {
        synchronized (DECODE_LOCK) {
          if (data.needsMatrixTransform() || exifRotation != 0) {
//如果图片中Exif有旋转信息,进行旋转
            bitmap = transformResult(data, bitmap, exifRotation);
            if (picasso.loggingEnabled) {
              log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());
            }
          }
          if (data.hasCustomTransformations()) {
//如果requestCreator设置了transformation
            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;
  }

我们先来看decodeStream方法

  /**
   * Decode a byte stream into a Bitmap. This method will take into account additional information
   * about the supplied request in order to do the decoding efficiently (such as through leveraging
   * {@code inSampleSize}).
   */
  static Bitmap decodeStream(InputStream stream, Request request) throws IOException {
    MarkableInputStream markStream = new MarkableInputStream(stream);
    stream = markStream;

    long mark = markStream.savePosition(65536); // TODO fix this crap.

    final BitmapFactory.Options options = RequestHandler.createBitmapOptions(request);
    final boolean calculateSize = RequestHandler.requiresInSampleSize(options);

    boolean isWebPFile = Utils.isWebPFile(stream);
    markStream.reset(mark);
    // When decode WebP network stream, BitmapFactory throw JNI Exception and make app crash.
    // Decode byte array instead
    if (isWebPFile) {
      byte[] bytes = Utils.toByteArray(stream);
      if (calculateSize) {
        BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
        RequestHandler.calculateInSampleSize(request.targetWidth, request.targetHeight, options,
            request);
      }
      return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
    } else {
      if (calculateSize) {
        BitmapFactory.decodeStream(stream, null, options);
        RequestHandler.calculateInSampleSize(request.targetWidth, request.targetHeight, options,
            request);

        markStream.reset(mark);
      }
      Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
      if (bitmap == null) {
        // Treat null as an IO exception, we will eventually retry.
        throw new IOException("Failed to decode stream.");
      }
      return bitmap;
    }
  }

在这里获取request中的图片格式(config) 和 设置好的宽高(resize方法)
config如果之前没有设置,默认是ARGB_8888,
如果设置了resize,在这里则会使用inSampleSize调整图片大小,否则会按默认大小加载
然后回到run方法

Android图片加载框架 Picasso 源码解析(with->load->into)_第10张图片
image.png

返回给dispatcher

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

回到DispatcherHandler

Android图片加载框架 Picasso 源码解析(with->load->into)_第11张图片
image.png

去到dispatcher的performComplete

  void performComplete(BitmapHunter hunter) {
    if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
      cache.set(hunter.getKey(), hunter.getResult());
    }
    hunterMap.remove(hunter.getKey());
    batch(hunter);
    if (hunter.getPicasso().loggingEnabled) {
      log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion");
    }
  }

在这里,根据对request设置的内存策略,判断需不需要内存缓存,
如果需要,会放入LRUCache里
重点看batch,准备批量任务

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

回到DispatcerHandler

image.png
  void performBatchComplete() {
    List copy = new ArrayList(batch);
    batch.clear();
    mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
    logBatch(copy);
  }

批量任务都已经准备就绪

  static final Handler HANDLER = new Handler(Looper.getMainLooper()) {
    @Override public void handleMessage(Message msg) {
      switch (msg.what) {
        case HUNTER_BATCH_COMPLETE: {
          @SuppressWarnings("unchecked") List batch = (List) msg.obj;
          //noinspection ForLoopReplaceableByForEach
          for (int i = 0, n = batch.size(); i < n; i++) {
            BitmapHunter hunter = batch.get(i);
//
            hunter.picasso.complete(hunter);
          }
          break;
        }

        ···
      }
    }
  };

最后for循环遍历batch

hunter.picasso.complete(hunter);

调用picasso的complete

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

    boolean hasMultiple = joined != null && !joined.isEmpty();
    boolean shouldDeliver = single != null || hasMultiple;

    if (!shouldDeliver) {
      return;
    }

    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) {
      //noinspection ForLoopReplaceableByForEach
      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);
    }
  }

终于看到了complete方法,但是还没完,这里核心是走deliverAction方法

  private void deliverAction(Bitmap result, LoadedFrom from, Action action) {
    if (action.isCancelled()) {
      return;
    }
    if (!action.willReplay()) {
      targetToAction.remove(action.getTarget());
    }
    if (result != null) {
      if (from == null) {
        throw new AssertionError("LoadedFrom cannot be null.");
      }
      action.complete(result, from);
      if (loggingEnabled) {
        log(OWNER_MAIN, VERB_COMPLETED, action.request.logId(), "from " + from);
      }
    } else {
      action.error();
      if (loggingEnabled) {
        log(OWNER_MAIN, VERB_ERRORED, action.request.logId());
      }
    }
  }
action.complete(result, from);

主要看action.complete(result, from);
之前我们在把任务加入dispatcher的时候,new了一个ImageViewAction,ImageViewAction继承自Action


Android图片加载框架 Picasso 源码解析(with->load->into)_第12张图片
image.png

查看Action的继承,有很多子类
我们最开始设置的是ImageView,我们看看ImageViewAction的complete实现

  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) {
    if (result == null) {
      throw new AssertionError(
          String.format("Attempted to complete action with no result!\n%s", this));
    }

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

重点看这一句

PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
  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);
  }
target.setImageDrawable(drawable);

这个target就是我们的ImageView

好了,到这里,我们终于把我们的Bitmap转换成PicassoDrawable,PicassoDrawable继承自BitmapDrawable,最后塞给ImageView了,
如果这里不是ImageView,流程也都大体相似,


Android图片加载框架 Picasso 源码解析(with->load->into)_第13张图片
image.png

如果我们在调用picasso的into方法时,传入的时一个target实现类,最终会通过调用Target的onBitmaoLoaded方法完成图片的加载

你可能感兴趣的:(Android图片加载框架 Picasso 源码解析(with->load->into))