preface

I think when using a third party library, it’s important to know exactly what you know and why you want to change it and leave it behind.

  • First, be able to use it proficiently.
  • And then, understand how it works, how it works, why it works that way
  • Then, on the basis of understanding the principles, if you want to implement functionality that the library itself does not support or feel that the implementation is not good, you can modify it in case.
  • Finally, can use, the principle of understanding, can move easily change, but also feel that their talent can not display, it can be natural, that is, their own wheel. Next, the first stage is skipped and the second stage is skipped. Detach the wheel and find out how Glide library is implemented.

How is the overall Glide structure, how the big wheels turn up, through this article on the overall Glide understanding and then break through. I spent two hours on this picture…. , manual heart comparison, for praise)

1. Creation of Glide

A simple use of Glide is the following:

Glide.with(context).load("http:xxxx").into(target) 
Copy the code

Let’s see what Glide. With () does first. Glide’s with (x) method can take the following parameters: Context, Activity, Fragment, or View. Either argument directly or indirectly calls the following method:

private static RequestManagerRetriever getRetriever(@Nullable Context context){
    Preconditions.checkNotNull(context,"..");
    return Glide.get(context).getRequestManagerRetriever();
}
Copy the code

This method will eventually return a RequestManagerRetriever object. Glide. Get (CTX) returns a Glide object, a singleton for internal use, and a singleton for in-process Glide, as shown below. Knock on the blackboard, this is the legendary double effect lock.

  @NonNull
  public static Glide get(@NonNull Context context) {
    if (glide == null) {
      synchronized (Glide.class) {
        if(glide == null) { checkAndInitializeGlide(context); }}}return glide;
  }
Copy the code

Glide. Get (CTX)->checkAndInitializeGlide(CTX)->initializeGlide(CTX,glideBuilder), The final burden of creating the Glide singleton fell to initializeGlide (..) On the way

private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) { Context applicationContext = context.getApplicationContext(); GeneratedAppGlideModule annotationGeneratedModulegetAnnotationGeneratedGlideModules(); . Glide glide = builder.build(applicationContext); . applicationContext.registerComponentCallbacks(glide); Glide.glide = glide; }Copy the code

Our source code about using APT technology to dynamically load GeneratedAppGlideModule objects and then configure Glide related code hidden, space limited this section is a separate detailed exploration.

First, this approach accomplishes three main things:

  • Load the GeneratedAppGlideModule object dynamically using the APT technique and then configure Glide. (Find out later)
  • Glide objects are created using the constructor pattern GlideBuilder
  • ApplicationContext. RegisterComponentCallbacks (glide), applicationContext registered ComponentCallbacks listening, incoming here in the previous step to create fresh glide objects, Glide realized ComponentCallbacks2 interface, the interface void onTrimMemory(@trimMemoryLevel int level); The method is called when the operating system is running out of memory, which we can assume is a good time to clear the cache.

Create a summary

# GlideBuild
 Glide build(@NonNull Context context) {
    if (sourceExecutor == null) {... }if(diskCacheExecutor == null) {... }if (animationExecutor == null) {...}
    ...
    if(memoryCache == null) {... }if(diskCacheFactory == null) {... }if(engine == null) {... }returnNew Glide (context, engine, memoryCache bitmapPool, arrayPool...). }Copy the code

As shown above, initializeGlide () is finally called. GlideBuilder is used in this method to create Glide object, but it is worth noting that it is a singleton. And GlideBuider generates default parameters for GlideBuilder to create Glide objects when no response parameters are set.

2. About request management

2.1, RequestManagerRetriever

As the book continues, we have a general idea of how the Glide class was created.

The same line of code,

RequestManager requestManager = Glide.with(this);
Copy the code

This line of code does three things in sequence

  • Glide Glide = Glide. Get (context);
  • 2. Call Glide. GetRetriever () to get the requestManagerRetriever object
  • 3, RequestManager manager = requestManagerRetriever. GetRequestManager (context) get a RequestManager object.

Comment on the RequestManagerReriever class

A collection of static methods for creating new RequestManagers or retrieving existing ones from activities and fragment.

The job of this class is to create new RequestManagers or retrieve existing ones from activities and Fragments.

One of the important things about creating a RequestManager document is that LifeCycle

  • There are two class implements the LifeCycle is ActivityFragmentLifecycle and ApplicationLifecycle respectively. Main difference is that ActivityFragmentLifecycle has the ability to perceive fragments/Activity statement cycle.
  • If the incoming fragments or Acitivy, requestManagerReriever view create a RequestManagerFragment object, instead of binding, thus realized the Activity, or the perception of the fragments statement cycle.
  • If the incoming ApplicationContext will be introduced into two class implements the LifeCycle is ActivityFragmentLifecycle and ApplicationLifecycle respectively.

Pick up the core of the comparison of a section of code mark, add a comment.

private RequestManager fragmentGet(@NonNull Context context,FragmentManager fm,Fragment parentHint,boolean IsParentVisible) {/ / get a RequestManagerFragment RequestManagerFragment current = getRequestManagerFragment (FM, parentHint, isParentVisible); / / trying to get in RequestManagerFragment requestManager requestManager requestManager = current. GetRequestManager ();if(requestManager == null) {// Create a fragment if it is empty and store it in the RequestManagerFragment Glide Glide = Glide. Get (context); requestManager = factory.build( glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context); current.setRequestManager(requestManager); }return requestManager;
  }
Copy the code

Pay attention to the point

  • When was the RequestManagerReriever created? In the GlideBuilder build method, a new one is passed in through the Glide constructor.
  • When RequestManagerReriever. Get (view), A Requestmanager will be created by looking for the Activity where the View is located. If the Activity is not found, an ApplicationLifecycle will be used to build the Requestmanager.

Summarize the RequestManagerReriever object creation RequestManager process.

2.2 RequestManager, RequestBuilder

The RequestManager, as it is known, manages image requests, starts, pauses, and restarts requests, and takes appropriate actions based on the lifecycle and network of the component in which they are located

Using a series of load() methods on the RequestManager object, you can end up with a RequestBuilder object. RequestBuilder is a generic class, and what type of image resource to load determines what type it is. The source of the image resource can be Recource, File, or network.

RequestBuilder<T> requestBuidlder = requestManager.load(xxxx);
Copy the code

Once you have the RequestBuilder, you can set some expectations for the requested image resource

  • For example, specify the size you want it to be: Override ().
  • What clipping rules do you want it to use: centerCrop(), centerInside(); .
  • What is the expected mechanism for his cache :diskCacheStrategy().

RequestBuildler, which can be configured using RequestOptions. RequestOptions ¶ ReuqestBuilder extends from the BaseRequestOptions class, which is the bridge between RequestOption and RequestBuilder. A series of Settings are made to RequestOption through objects of this class.

Request has three implementation classes, RequestCoordinator, SingleRequest and ErrorRequestCoordinator, RequestCoodinator.

  • A RequestCoordinator contains two requests, one primary and one error. The RequestCoordinator uses the primary request to initiate a request, and if the primary request fails, it invokes an error request.
  • SingleReuqest implements the Request method to load image resources from different types.
  • ThumbnailRequestCoordinator loading thumbnails and original figure at the same time.

#RequestBuilder Is used as an example to describe how to build an ErrorRequestCoordinator object.

private Request buildRequestRecursive(..) ErrorRequestCoordinator ErrorRequestCoordinator = null;if(errorBuilder ! = null) { errorRequestCoordinator = new ErrorRequestCoordinator(parentCoordinator); parentCoordinator = errorRequestCoordinator; } // Build mainRequest =(...) ;if (errorRequestCoordinator == null) {
      return mainRequest;
    }

    int errorOverrideWidth = errorBuilder.getOverrideWidth();
    int errorOverrideHeight = errorBuilder.getOverrideHeight();
    if(Util.isValidDimensions(overrideWidth, overrideHeight) && ! errorBuilder.isValidOverride()) { errorOverrideWidth = requestOptions.getOverrideWidth(); errorOverrideHeight = requestOptions.getOverrideHeight(); } / / build errors Request Request errorRequest = errorBuilder. BuildRequestRecursive (..) ; errorRequestCoordinator.setRequests(mainRequest, errorRequest);return errorRequestCoordinator;
  }
Copy the code

The overall structure of Request build management is as follows

3, Engine

The begin() method of the Request object is loaded depending on the source of the image. Once the desired size is obtained, onSizeReady() is called, passing in a list of parameters.

onSizeReady() {... loadStatus = engine.load( glideContext, model, requestOptions.getSignature(), this.width, this.height, requestOptions.getResourceClass(), transcodeClass, priority, requestOptions.getDiskCacheStrategy(), requestOptions.getTransformations(), requestOptions.isTransformationRequired(), requestOptions.isScaleOnlyOrNoTransform(), requestOptions.getOptions(), requestOptions.isMemoryCacheable(), requestOptions.getUseUnlimitedSourceGeneratorsPool(), requestOptions.getUseAnimationPool(), requestOptions.getOnlyRetrieveFromCache(), this, callbackExecutor); . }Copy the code

engin.load()

public synchronized <R> LoadStatus load(...) {... EngineJob<R> engineJob =engineJobFactory.build(...) ; DecodeJob<R> decodeJob =decodeJobFactory.build(...) ; . engineJob.addCallback(cb, callbackExecutor); / / load engineJob. Start (decodeJob); // Decode binary data into pictures for editing according to configuration...return new LoadStatus(cb, engineJob);
  }
Copy the code

EngineJob. Load is the executor that actually loads the image data and then gives it to decodeJob. A separate section on how to implement the internal details, such as how thread pools are used in this section, tips on how to use bitmaps, etc.

The above has an overall understanding of Glide, an excellent open source library, which can be divided into three parts

  • Glide to create
  • Request management
  • Will we do a separate article on load parsing? We may also analyze the application of caching strategies vertically. Finally, I put up the overall architecture diagram that I spent two hours on