Here’s an illustration of Picasso’s call:

1. A singleton Picasso is generated when we call picas.with ()

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

The first is a global singleton, and the second is to generate the corresponding Picasso object through the Builder mode.

2. After calling the above method, we will continue to call the load method:

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));
  }
Copy the code

Support loadFile, Uri, path, etc., which will actually end up calling the load(Uri) method, generating RequestCreator, then placeHolder, errImage, setting images, etc., via chain calls.

3. After creating RequestCreator, we execute the into method of RequestCreator:

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()) {//1 picasso.cancelRequest(target);if (setPlaceholder) {
        setPlaceholder(target, getPlaceholderDrawable());
      }
      return;
    }

    if (deferred) {
      if (data.hasSize()) {//2
        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)) {//3
      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) {//4
      setPlaceholder(target, getPlaceholderDrawable()); } Action action = new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId, errorDrawable, requestKey, tag, callback, noFade); picasso.enqueueAndSubmit(action); / / 5}Copy the code

Here is the main implementation of picture acquisition, conversion size and picture display operations.

3.1 The first step above is mainly to determine whether there are resource file Settings or local paths, etc. If there is such a direct display, this is mainly a disguised implementation of loading Picasso’s local pictures.

3.2 The second step is to set the size of the ImageView control to be displayed so that the image and control size ratio can be calculated for memory optimization at load time

3.3 The third step will read the picture from the memory, read the picture to increase the hit calculator

3.4 The fourth step is to set a blank picture

3.5 The fifth step is mainly to perform the image acquisition operation, picascaria.enqueueandSubmit (Action) method:

4. Picasso was. EnqueueAndSubmit (Action) methods:

void enqueueAndSubmit(Action action) { Object target = action.getTarget(); // Since we initialized the ImageView Action above, the target is the ImageView to load.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); }Copy the code

Here, we will call the submit method, this method is mainly will be distributed to the corresponding action time the Dispatcher, the Dispatcher. DispatchSubmit (action);

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

The corresponding REQUEST_SUBMIT message is sent through a Handler that is an instance of a DispatcherHandler and executes the corresponding HandleMessage method.

 case REQUEST_SUBMIT: {
          Action action = (Action) msg.obj;
          dispatcher.performSubmit(action);
          break;
        }
Copy the code

5. Let’s look at the performSubmit method of the Dispatcher class:

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

    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); //1 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()); }}Copy the code

Note 1 calls the canHandleRequest method of each RequestHandler at once to select which RequestHandler to handle. Piassco initializes with the following RequestHandler by default

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

    // ResourceRequestHandler needs to be the first inthe 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));Copy the code

6. After finding the RequestHandler that can handle the above, create the corresponding bitmapHunter. BitmapHunder is actually the implementation of Runnable.

@Override public Future<? > submit(Runnable task) { PicassoFutureTask ftask = new PicassoFutureTask((BitmapHunter) task); execute(ftask);return ftask;
  }
Copy the code

After creating the corresponding Runnable, add it to the thread pool for execution

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

      if (picasso.loggingEnabled) {
        log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this)); } result = hunt(); / / 1if (result == null) {
        dispatcher.dispatchFailed(this);
      } else {
        dispatcher.dispatchComplete(this);
      }
    } catch (Downloader.ResponseException e) {
      if(! e.localCacheOnly || e.responseCode ! = 504) { exception = e; } dispatcher.dispatchFailed(this); } catch (NetworkRequestHandler.ContentLengthException e) { exception = e; dispatcher.dispatchRetry(this); } catch (IOException e) { exception = e; dispatcher.dispatchRetry(this); } catch (OutOfMemoryError e) { StringWriter writer = new StringWriter(); stats.createSnapshot().dump(new PrintWriter(writer)); exception = new RuntimeException(writer.toString(), e); dispatcher.dispatchFailed(this); } catch (Exception e) { exception = e; dispatcher.dispatchFailed(this); } finally { Thread.currentThread().setName(Utils.THREAD_IDLE_NAME); }}Copy the code

Note 1 is the logic for getting the image


  Bitmap hunt() throws IOException {
    Bitmap bitmap = null;

    if (shouldReadFromMemoryCache(memoryPolicy)) {//1
      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.Result result = requestHandler.load(data, networkPolicy);
    if(result ! = null) {//2 loadedFrom = result.getLoadedFrom(); exifRotation = result.getExifOrientation(); bitmap = result.getBitmap(); // If there was no Bitmapthen we need to decode it from the stream.
      if(bitmap == null) { InputStream is = result.getStream(); try { bitmap = decodeStream(is, data); } finally { Utils.closeQuietly(is); }}}if(bitmap ! = null) {//3if (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) { bitmap = transformResult(data, bitmap, exifRotation);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;
  }
Copy the code

Note 1 above is to get the image from memory, if the image can not get the image comment 2 by RequestHandler. Load to get the image data stream result object, and then parse into the image, note 3 after getting the image, according to the size of the control to convert the image. After getting the image, send a message with message code HUNTER_COMPLETE to MessageQueue.


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

Then the dispatcherHandler executes

  case HUNTER_COMPLETE: {
          BitmapHunter hunter = (BitmapHunter) msg.obj;
          dispatcher.performComplete(hunter);
          break;
        }
Copy the code
 void performComplete(BitmapHunter hunter) {
    if(shouldWriteToMemoryCache(hunter.getMemoryPolicy())) { cache.set(hunter.getKey(), hunter.getResult()); } hunterMap.remove(hunter.getKey()); batch(hunter); / / 1if (hunter.getPicasso().loggingEnabled) {
      log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion"); }}Copy the code

Save it in memory. After the image is cached in memory, the batch method is used to Dispatch images to the ImageView

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);
    }
  }
Copy the code
   case HUNTER_DELAY_NEXT_BATCH: {
          dispatcher.performBatchComplete();
          break;
        }
Copy the code
void performBatchComplete() {
    List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);
    batch.clear();
    mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
    logBatch(copy);
  }
Copy the code

The handler bound to the main thread Looper receives the message and executes it

 case HUNTER_BATCH_COMPLETE: {
          @SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;
          //noinspection ForLoopReplaceableByForEach
          for (int i = 0, n = batch.size(); i < n; i++) {
            BitmapHunter hunter = batch.get(i);
            hunter.picasso.complete(hunter);
          }
          break;
        }
Copy the code
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;
    }

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

    if(single ! = null) { deliverAction(result, from, single); / / 1}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); }}Copy the code

Note 1 is the operation to set the Image to Image.

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

Perform interface setup via ImageViewAction.

conclusion

In fact, the above is just a source reading process records, in fact, more is the understanding of the design idea, design pattern learning, such as the Action interface here, multiple subclasses to implement the interface, will execute the Compelete method, this is the strategy mode model, It will execute the corresponding complete depending on whether you pass in ImageViewAction or GetAction, so you need to determine whether ImageViewAction or GetAction will execute the corresponding action; Also, during initialization, it adds various requesthandlers to the list, and then finds the corresponding RequestHandler based on the URI you passed in. Personally, this design is useful when there are multiple serial attempts to solve a problem.

If you find this article enlightening, I hope you can help to like it or follow it. Thank you.