Android Evolution Badge - Picasso

In recent days, I have read many articles about source code analysis written by Dashen. I can't understand them at all. Have you got a haggard heart?

So I decided to analyze the source code by myself, which may be incorrect or shallow, but I think it will be more valuable than just looking at other people's analysis!

Okay, let's start with the famous Picasso!

Simple usage of Picasso:

        Picasso.with(this).load(R.mipmap.ic_launcher).into(iv);// iv is a reference object for an imageView

OK, let's take a step-by-step look at how picasso works!

First, observe with () method;

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

Singleton is an instance of a Picasso class. You can see that the singleton pattern is used here to create instances.
// What kind of singleton pattern is todo?

In the singleton pattern, the Builder pattern is used to build the instance. Let's see what it does.

 public Picasso build() {
      Context context = this.context;

      if (downloader == null) {
        downloader = Utils.createDefaultDownloader(context); // Initialize Downloader
      }
      if (cache == null) {
        cache = new LruCache(context); // Initialize cache space
      }
      if (service == null) {
        service = new PicassoExecutorService(); // Initialization service
      }
      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);
    }

We can see that in the build () method we initialize a bunch of objects, such as downloaders, caches, services, etc., and then use these objects to build Picasso instances, well, this is very consistent with the way builder is used.
Finally, a Picasso instance was created.

Summary: with () method mainly initializes a series of objects that must be used, such as downloaders, caches, etc., and returns a Picasso instance.

Let's go on and see what the load () method has done.

 public RequestCreator load(int resourceId) {
    if (resourceId == 0) {
      throw new IllegalArgumentException("Resource ID must not be zero.");
    }
    return new RequestCreator(this, null, resourceId);
  }

This method returns a RequestCreator object, which is constructed by using the resource file id, so let's move on.

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

The construction method does two things altogether.
1. Let RequestCreator hold picasso instances;
2. Initialize data objects;

Okay, finally come to the in () method, let's take the ultimate snooping trip!

public void into(ImageView target, Callback callback) {
    long started = System.nanoTime(); // Get the current time
    checkMain(); // Check if it is in the UI thread, if not, an exception will be thrown

    if (target == null) {
      throw new IllegalArgumentException("Target must not be null.");
    }

     // If data does not hold the resource file id, the request is cancelled.
    if (!data.hasImage()) {
      picasso.cancelRequest(target);
      if (setPlaceholder) { // If the display waiting picture is set, the target is set to wait for the picture.
        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); // Officially declare a request and pass in the time previously obtained
    String requestKey = createKey(request); // Get a key

    if (shouldReadFromMemoryCache(memoryPolicy)) { // If you want to get pictures from the cache
      Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey); // Get the image in the cache according to this key
      if (bitmap != null) {
        picasso.cancelRequest(target);
        setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled); // Here, the image from the cache is loaded into target, the iv above, and the picture comes out!
        if (picasso.loggingEnabled) {
          log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);
        }
        if (callback != null) {
          callback.onSuccess();
        }
        return;
      }
    }

    // First load the waiting picture
    if (setPlaceholder) {
      setPlaceholder(target, getPlaceholderDrawable());
    }

    // Create an Action object and finally insert the action object into the queue and submit it it
    Action action =
        new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
            errorDrawable, requestKey, tag, callback, noFade);

    picasso.enqueueAndSubmit(action);
  }

We can get a general idea of picasso's image loading strategy.
1. First, judge whether the resources can be loaded normally. If not, then judge whether there are settings to load waiting pictures. If there are, display waiting pictures.
2. Determine whether the image should be retrieved from the cache, if it is retrieved according to the key, if it is successful, cancel the request and load the image from the cache into the target. If acquisition fails, keep going.
3. If waiting pictures are set, the waiting pictures are loaded as transitions first.
4. Construct an action object with the above variables and insert the action object into the queue; (Prove that the operation is asynchronous? That's why the second picture will appear earlier than the first one.

It's not over yet. Xiaobian is the first time that I have written such a long article.

Let's continue to see what the action object is and what we did when we inserted it into the queue.

First, what the hell is the action object?

abstract class Action<T> {

····
Action(Picasso picasso, T target, Request request, int memoryPolicy, int networkPolicy,
      int errorResId, Drawable errorDrawable, String key, Object tag, boolean noFade) {
    this.picasso = picasso;
    this.request = request;
    this.target =
        target == null ? null : new RequestWeakReference<T>(this, target, picasso.referenceQueue);
    this.memoryPolicy = memoryPolicy;
    this.networkPolicy = networkPolicy;
    this.noFade = noFade;
    this.errorResId = errorResId;
    this.errorDrawable = errorDrawable;
    this.key = key;
    this.tag = (tag != null ? tag : this);
  }
  ····
  }

Action object is an abstract class, which holds many variables, such as picasso, request, target, key, etc. Almost all the useful variables are held by it! What kind of big action do you want to make with such "resources"?
Let's look down at what this action did when it was inserted into the queue.

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

Here the target is also our iv above, and this targetToAction was created when the picasso object was initialized. Target To Action has not done any put ting before, so it must be judged when it is first executed, and the target and action are associated and saved.

Come off work, continue tomorrow!

Posted by Random on Sun, 23 Jun 2019 14:05:00 -0700