当前位置:网站首页>Confused, the interviewer suddenly asked me: what does glide do? I'm a big talker about the new version of glide

Confused, the interviewer suddenly asked me: what does glide do? I'm a big talker about the new version of glide

2020-12-07 19:20:24 Android technology dry goods sharing

Always wanted to write an article Glide Principle explanation , But I haven't been able to write .

It's not because there's no time , yes Glide It's too extensive , The internal logic is too sharp . I haven't been able to find a proper commanding point to overlook the whole body .

Previous pair Glide Has been staying at the use level , It's easy to use , call chaining , To save memory , Without regard to internal principles , Not to learn its architecture design .

Next, I will extend the analysis through the following questions Glide The internal logic of , do as far as possible From use to principle , From architecture to logic , Explain profound theories in simple language .

  1. Glide What are the ?
  2. Why Glide?
  3. Glide How to use it? ?
  4. Glide Which modules are included ? What do you do ?
  5. Glide What is done after calling a method ?

This article is based on Glide Latest version 4.10.0 Analyze , If there is any mistake, please point out .

notes : The latest version is 4.11.0, It's late , however 4.10.0 It's very, very new .

960 The web is the most complete Android Development Notes :Android Basics 、Java Basics 、Android Source code correlation analysis 、 Some common principle problems and so on , To help you understand Android The principle of relevant knowledge points and interview related knowledge .

1. Please use the posture of a confused face to understand ,Glide What the hell is it ?

Glide is a fast and efficient open source media management and image loading framework for Android that wraps media decoding, memory and disk caching, and resource pooling into a simple and easy to use interface.

Translate it into something you can understand

Glide Is a fast and efficient Android Image loading Library , Focus on smooth rolling .Glide Provides easy to use API, High performance 、 Scalable picture decoding pipeline (decode pipeline), And automatic resource pool technology .

2. Then why do you use it Glide?

There's a man who doesn't know where , People who don't know their names once said , When you start asking Why? When , Then you may have to change .

The question is very simple , But thorough analysis is not easy , Even this problem is accompanied by the need to use , The scene changes . Since it is not easy to intuitively analyze and explain , Let's list the corresponding advantages , It's convenient to take your seat when you need it

  1. Graceful gesture Glide.with(fragment).load(url).into(imageView);
  2. Multi dimensional image format (png,jpg,gif,webp,video etc. ...)
  3. Strong physical fitness Excellent performance , It can prevent frequent main threads I/O, The page flicker and jam caused by garbage collection .
  4. Flexible load request According to the life cycle of the page, it can dynamically manage the loading request of pictures .
  5. Cache strategy of coquettish Supports caching images of corresponding size according to control size , And the default image format is RGB_565, It takes less space . So it's faster .

Um. ~~~ Just look at these , Do you want to choose Glide.....

3. Glide How to use it? ?

It's time for me to write with my eyes closed , Come on, come on , Let's code it .

First add the following quotation .

implementation 'com.github.bumptech.glide:glide:4.10.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'

Usage mode , Let's list them 5 Common usage :

//**********************************  Use it directly  **********************************
Glide.with(Context).load("").into(mImageView)

// Use Generated API,  Scope of action Application  In module use 
// establish MyAppGlideModule Class plus @GlideModule annotation ,buid Can be used after  GlideApp
@GlideModule
public final class MyAppGlideModule extends AppGlideModule {}

//Generated API Loading pictures 
GlideApp.with(Context).load("").into(mImageView);



//**********************************   Add footprint  **********************************
RequestOptions requestOptions = new RequestOptions()
         .placeholder(R.drawable.a)
         .error(R.drawable.e)
         .diskCacheStrategy(DiskCacheStrategy.NONE);// Settings cache parameters 
Glide.with(Context).load("").apply(requestOptions).into(mImageView);

//Generated API  The way ( and Glide3  equally )
GlideApp.with(Context).load("")
         .placeholder(R.drawable.a)
         .error(R.drawable.e)
         .diskCacheStrategy(DiskCacheStrategy.NONE)
         .into(mImageView);

//  Backup callback character (Fallback) Generated API  The way 
// In the scene where the picture is set , If the user does not set , That is for null The situation of , You can use the fallback to display the default graph 
private static final String NULL_URL=null;
GlideApp.with(Context).load(NULL_URL)
         .fallback(R.drawable.a)
         .into(mImageView);


//**********************************   Specifies the size of the loaded picture (override) **********************************
RequestOptions requestOptions = new RequestOptions().override(200,100);
Glide.with(Context).load("").apply(requestOptions).into(mImageView);

//Generated API  The way 
GlideApp.with(Context).load("")
             .override(200,100)
             .into(mImageView);


//**********************************   adopt thumbnail Load thumbnails  **********************************
// And placeholder similar , however thumbnail Loadable network diagram ,placeholder Only this map can be loaded .
RequestOptions requestOptions = new RequestOptions()
                .override(200,100)
                .diskCacheStrategy(DiskCacheStrategy.NONE);
Glide.with(MainActivity.this)
                .load(url)
                .thumbnail( Glide.with(this)
                .load(url)
                .apply(requestOptions))
                .into(iv);

//Generated API  The way 
 GlideApp.with(Context)
            .load(url)
            .thumbnail( GlideApp.with(this)
            .load(IMAGE_URL).override(200,100)          
            .diskCacheStrategy(DiskCacheStrategy.NONE))
            .into(mImageView);


//**********************************   Set the change operation of the picture  **********************************
// It can be done by 
//CenterCrop( The central area of the original image is cropped and displayed )
//FitCenter( The original length and width of the picture are covered )
//CircleCrop( Round cut )
// Set the change operation of the picture 
Glide.with(Context)
            .load(IMAGE_URL)
            .apply(RequestOptions.circleCropTransform())
            .into(mImageView);

//Generated API  The way 
GlideApp.with(Context).load(IMAGE_URL)
            .circleCrop()
            .diskCacheStrategy(DiskCacheStrategy.NONE)
            .into(mImageView);

4. Glide Which modules are included ? What do you do ?

The next link will be the high ground of this article .

We can think about it briefly first , If you are Glide The author of , How do you design an excellent image loading framework , What links need to be considered , What modules need to be included ?

Simply speaking , Image loading requires the following modules .

  1. Parameter encapsulation
  2. Load request
  3. Execution engine
  4. Data loader
  5. decoder
  6. Encoder
  7. cache

That's practical Glide Is that how it's designed ?

  • Parameter encapsulation ( Get the requested parameters , Configure properties of load graph )
  • Network request ( Download the pictures )
  • Caching logic ( Resource reuse )
  • Add and decode ( Processing images )
  • Data loading ( Determine the loading mode / Request network /IO Read / Memory read )
  • Thread pool / The thread queue ( Or task queue , Used to handle each load task )
  • prevent OOM Processing mechanism ( Soft citation , cache , Compress , Storage, etc ..)
  • Life cycle management ( Prevent memory leaks )
  • .......

Said so much , Then load an image , What kind of process is it ? let me put it another way , How do these modules work together , What's the call order ?

I drew a simple picture for you , brothers , This picture is hard to come by , You don't know how to draw if you don't go through it ...

Many friends may see this picture , It's silly again , What are you doing , One Glide How many pieces do you draw over 了 ?

How did the network load ? What about the cache , How did the thread switch ? How is the life cycle controlled . I didn't say anything ..

With all of you , With all of you , Don't worry . What we strive for is first Whole and part , Overview and subdivision . Let's follow up on the code .

Are you ready to face the wind, brother ?

5. Glide What is done after calling a method ?

First, let's follow up with a simple use

Glide.with(context).load("").into(mImageView)

call chaining , We decompose it into the following three methods .

Glide.with(context) Glide.with(context).load("") Glide.with(context).load("").into(mImageView)

5.1. Glide.with(context) What did you do ?

The wind of death , Always my body . I'll give it to you !

/**
   *  In short, the method takes the following steps 
   * 1. Glide  initialization 
   * 2.  Various thread pool initialization , Initialization of various cache objects 
   * 3. Engine initialization , as for Engine What is it , We will introduce it later 
   * 4. RequestManager  initialization 
   * 5.  Create transparent fragment, Dynamic monitoring lifecycle 
   */
  @NonNull
  public static RequestManager with(@NonNull FragmentActivity activity) {
    return getRetriever(activity).get(activity);
  }

 private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    //  Detect incoming context Compliance 
    Preconditions.checkNotNull(context, "You cannot start .....");
    return Glide.get(context).getRequestManagerRetriever();
  }

  public static Glide get(@NonNull Context context) {
    if (glide == null) {
      GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules(context.getApplicationContext());
      synchronized (Glide.class) {
        if (glide == null) {
          // If glide by null Start the detection initialization logic 
          checkAndInitializeGlide(context, annotationGeneratedModule);
        }
      }
    }
    return glide;
  }

  private static void checkAndInitializeGlide(@NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
    //  If it's already initialized , Throw exception .
    if (isInitializing) {
      throw new IllegalStateException( "You cannot call Glide.get() in registerComponents(), use the provided Glide instance instead");
    }
    // Set the initialization state 
    isInitializing = true;
    initializeGlide(context, generatedAppGlideModule);
    isInitializing = false;
  }

  private static void initializeGlide(
      @NonNull Context context,
      @NonNull GlideBuilder builder,
      @Nullable GeneratedAppGlideModule annotationGeneratedModule) {
    Context applicationContext = context.getApplicationContext();
...
    // call GlideBuilder.build() methods Glide initialization 
    Glide glide = builder.build(applicationContext);
...
   // Register callback 
    applicationContext.registerComponentCallbacks(glide);
    Glide.glide = glide;
  }

 //GlideBuilder.build() Method , Used to initialize various thread pools , And cache objects , initialization Glide
  Glide build(@NonNull Context context) {
    // Create resource pools 
    if (sourceExecutor == null) {
      sourceExecutor = GlideExecutor.newSourceExecutor();
    }
    // Hard disk cache pool 
    if (diskCacheExecutor == null) {
      diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
    }

    if (animationExecutor == null) {
      animationExecutor = GlideExecutor.newAnimationExecutor();
    }

    if (memorySizeCalculator == null) {
      memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
    }

    if (connectivityMonitorFactory == null) {
      connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
    }

    if (bitmapPool == null) {
      int size = memorySizeCalculator.getBitmapPoolSize();
      if (size > 0) {
        bitmapPool = new LruBitmapPool(size);
      } else {
        bitmapPool = new BitmapPoolAdapter();
      }
    }

    if (arrayPool == null) {
      arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
    }

    if (memoryCache == null) {
      memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
    }

    if (diskCacheFactory == null) {
      diskCacheFactory = new InternalCacheDiskCacheFactory(context);
    }

    if (engine == null) {
  // initialization Engine
      engine = new Engine(
              memoryCache,
              diskCacheFactory,
              diskCacheExecutor,
              sourceExecutor,
              GlideExecutor.newUnlimitedSourceExecutor(),
              animationExecutor,
              isActiveResourceRetentionAllowed);
    }

    if (defaultRequestListeners == null) {
      defaultRequestListeners = Collections.emptyList();
    } else {
      defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners);
    }

    RequestManagerRetriever requestManagerRetriever = new RequestManagerRetriever(requestManagerFactory);
   // initialization Glide
    return new Glide(
        context,
        engine,
        memoryCache,
        bitmapPool,
        arrayPool,
        requestManagerRetriever,
        connectivityMonitorFactory,
        logLevel,
        defaultRequestOptionsFactory,
        defaultTransitionOptions,
        defaultRequestListeners,
        isLoggingRequestOriginsEnabled,
        isImageDecoderEnabledForBitmaps,
        hardwareBitmapFdLimit,
        minHardwareDimension);
  }

 //RequestManagerRetriever.get() adopt Glide.with(Context) Input parameter acquisition FragmentManager 
  public RequestManager get(@NonNull FragmentActivity activity) {
    if (Util.isOnBackgroundThread()) {
      return get(activity.getApplicationContext());
    } else {
      // adopt Android edition >17, And the page has isDestroyed Throw an exception 
      assertNotDestroyed(activity);
      // Get by entering parameter FragmentManager 
      FragmentManager fm = activity.getSupportFragmentManager();
      return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
    }
  }
//RequestManagerRetriever.supportFragmentGet()
 private RequestManager supportFragmentGet(
      @NonNull Context context,
      @NonNull FragmentManager fm,
      @Nullable Fragment parentHint,
      boolean isParentVisible) {
// Get request fragment
    SupportRequestManagerFragment current =getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
    // obtain fragment in RequestManager  object , If fragment It was just created requestManager  by null
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      // TODO(b/27524013): Factor out this Glide.get() call.
      Glide glide = Glide.get(context);
  // establish requestManager  And set to fragment
      requestManager =
          factory.build( glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }

  private SupportRequestManagerFragment getSupportRequestManagerFragment(
      @NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
// First look for this tag Of fragment Has it been created 
    SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
      // from pendingSupportRequestManagerFragments Find once in 
      current = pendingSupportRequestManagerFragments.get(fm);
      if (current == null) {
        // If it's all for null be new establish 
        current = new SupportRequestManagerFragment();
        current.setParentFragmentHint(parentHint);
        if (isParentVisible) {
          current.getGlideLifecycle().onStart();
        }
        // Storage 
        pendingSupportRequestManagerFragments.put(fm, current);
         // Add to FragmentManager  in 
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        // Sending notice 
        handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }

brothers , Let's get back to our senses , After this talk , We finally put with() This method analysis is completed .

Is the thief pulling the bull . Right now with() The process of method and the control and management of life cycle have a little impression . It doesn't matter if you can't retell it . First we can get down , When you see it the second time, you'll get twice the result with half the effort .

It's a bunch of them , Let's review this with() What did you do ?

1. call getRetriever(activity).get(activity)

2. call checkAndInitializeGlide(context, annotationGeneratedModule) Detect and start initialization

3. call initializeGlide(context, generatedAppGlideModule); Joint call GlideBuilder.build(applicationContext) Conduct Glide initialization

4. Glide Various thread pools are created during initialization , Various cache objects , And initialization Engine object .

5. call RequestManagerRetriever.get(activity) return RequestManager object , At the same time, the activity.getSupportFragmentManager() obtain FragmentManager object .

6. call supportFragmentGet(activity, fm, null, isActivityVisible(activity)) establish RequestManager and SupportRequestManagerFragment And make connections .

This place needs attention , What might be called fragmentGet() or getApplicationManager(), Final The purpose is establish RequestManager.

The corresponding relationship between the calling objects under the main thread is

1 individual Activity<-->1 individual FragmentManager<-->1 individual RequestManagerFragment<--> 1 individual RequestManager.

The child thread call is passed in ApplicationContext, In this case, the global singleton is used RequestManager, Life cycle and App Agreement .

Expand knowledge points , Specifically Glide How does it depend on the life cycle to dynamically control the loading of images ?

SupportRequestManagerFragment Object has properties that are ActivityFragmentLifecycle object , And in SupportRequestManagerFragment In the lifecycle methods of ActivityFragmentLifecycle The corresponding method of .

 @Override
  public void onStart() {
    super.onStart();
    lifecycle.onStart();
  }

  @Override
  public void onStop() {
    super.onStop();
    lifecycle.onStop();
  }

  @Override
  public void onDestroy() {
    super.onDestroy();
    lifecycle.onDestroy();
    unregisterFragmentWithRoot();
  }

And then through LifecycleListener Call the corresponding method

void onStart() {
    isStarted = true;
    for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
      lifecycleListener.onStart();
    }
  }

  void onStop() {
    isStarted = false;
    for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
      lifecycleListener.onStop();
    }
  }

  void onDestroy() {
    isDestroyed = true;
    for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
      lifecycleListener.onDestroy();
    }
  }

Let's look at which classes implement LifecycleListener The interface ? Among them is RequestManager,TargetTracker and Target( Inherit ) The interface is implemented , We draw out RequestManager Look at it alone .

RequestManager

 @Override
  public synchronized void onStart() {
    resumeRequests();
    targetTracker.onStart();
  }

  /**
   * Lifecycle callback that unregisters for connectivity events (if the
   * android.permission.ACCESS_NETWORK_STATE permission is present) and pauses in progress loads.
   */
  @Override
  public synchronized void onStop() {
    pauseRequests();
    targetTracker.onStop();
  }

  /**
   * Lifecycle callback that cancels all in progress requests and clears and recycles resources for
   * all completed requests.
   */
  @Override
  public synchronized void onDestroy() {
    targetTracker.onDestroy();
    for (Target<?> target : targetTracker.getAll()) {
      clear(target);
    }
    targetTracker.clear();
    requestTracker.clearRequests();
    lifecycle.removeListener(this);
    lifecycle.removeListener(connectivityMonitor);
    mainHandler.removeCallbacks(addSelfToLifecycle);
    glide.unregisterRequestManager(this);
  }

 public synchronized void resumeRequests() {
    requestTracker.resumeRequests();
  }

  public void pauseRequests() {
    isPaused = true;
    for (Request request : Util.getSnapshot(requests)) {
      // If it's in the process of execution 
      if (request.isRunning()) {
        // Avoid clearing parts of requests that may have completed (thumbnails) to avoid blinking
        // in the UI, while still making sure that any in progress parts of requests are immediately
        // stopped.
        // Call pause 
        request.pause();
        // Add to pending request .
        pendingRequests.add(request);
      }
    }
  }
  public void clear(@Nullable final Target<?> target) {
    if (target == null) {
      return;
    }

    untrackOrDelegate(target);
  }

private void untrackOrDelegate(@NonNull Target<?> target) {
    boolean isOwnedByUs = untrack(target);
    // We'll end up here if the Target was cleared after the RequestManager that started the request
    // is destroyed. That can happen for at least two reasons:
    // 1. We call clear() on a background thread using something other than Application Context
    // RequestManager.
    // 2. The caller retains a reference to the RequestManager after the corresponding Activity or
    // Fragment is destroyed, starts a load with it, and then clears that load with a different
    // RequestManager. Callers seem especially likely to do this in retained Fragments (#2262).
    //
    // #1 is always an error. At best the caller is leaking memory briefly in something like an
    // AsyncTask. At worst the caller is leaking an Activity or Fragment for a sustained period of
    // time if they do something like reference the Activity RequestManager in a long lived
    // background thread or task.
    //
    // #2 is always an error. Callers shouldn't be starting new loads using RequestManagers after
    // the corresponding Activity or Fragment is destroyed because retaining any reference to the
    // RequestManager leaks memory. It's possible that there's some brief period of time during or
    // immediately after onDestroy where this is reasonable, but I can't think of why.
    Request request = target.getRequest();
    if (!isOwnedByUs && !glide.removeFromManagers(target) && request != null) {
      // Remove request call cleanup .
      target.setRequest(null);
      request.clear();
    }
  }

See the above and its brief comments , I guess you know what's going on .

The essence is that SupportRequestManagerFragment It was passed in Context Created . Perceptible onStart(), onStop(), onDestroy() Other methods .

Call in these methods ActivityFragmentLifecycle The corresponding method in , then loop find ActivityFragmentLifecycle The corresponding RequestManager object , Call again Corresponding life cycle Methods .

Finally through requestTracker Loop to find the corresponding Request object , Then the corresponding processing method is called to achieve the purpose of dynamically controlling the loading of pictures according to the life cycle .

Okay , I won't go over it . I still have to hurry , Strive for success in one go .

5.2. Glide.with(context).load("") What did you do ?

Long journey Only sword as company My glory A long separation Ning ri'an Nobody can cloud And go with the wind

Glide.with(context) It's back RequestManager, therefore Glide.with(context).load("") amount to RequestManager.load("");

public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string);
  }

  // Note that the return value is RequestBuilder
  public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class);
  }

  public <ResourceType> RequestBuilder<ResourceType> as(
      @NonNull Class<ResourceType> resourceClass) {
    // initialization RequestBuilder object 
    return new RequestBuilder<>(glide, this, resourceClass, context);
  }
  //RequestBuilder Construction method of 
  protected RequestBuilder(
      @NonNull Glide glide,
      RequestManager requestManager,
      Class<TranscodeType> transcodeClass,
      Context context) {
    this.glide = glide;
    this.requestManager = requestManager;
    this.transcodeClass = transcodeClass;
    this.context = context;
    this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
    this.glideContext = glide.getGlideContext();
    // initialization RequestListeners
    initRequestListeners(requestManager.getDefaultRequestListeners());
    apply(requestManager.getDefaultRequestOptions());
  }

 private void initRequestListeners(List<RequestListener<Object>> requestListeners) {
    for (RequestListener<Object> listener : requestListeners) {
      addListener((RequestListener<TranscodeType>) listener);
    }
  }

  public RequestBuilder<TranscodeType> apply(@NonNull BaseRequestOptions<?> requestOptions) {
    Preconditions.checkNotNull(requestOptions);
    return super.apply(requestOptions);
  }

In the above source code, we just analyze load() The first step of the method , First of all, it analyzes load() in asDrawable() Method and a series of joint methods .

The logic is very simple , Relatively representative is initialization RequestBuilder object .

We continue to analyze RequestBuilder.load()

public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
  }

 private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    this.model = model;
    isModelSet = true;
    return this;
  }

EH ? It's over ? Why a little bit uncomfortable ? I just set it up model and isModelSet The attributes are done ? It seems so ,load() It's that simple , One thing to note is that model adopt Object Received .

5.3. Glide.with(context).load("").into(mImageView) What did you do ?

Looking back the past Further more Though I roam all over the world But I never lost my heart . The last way , Dawn is right in front of us Glide.with(context).load("") The return value of is RequestBuilder, So no less than RequestBuilder.into(mImageView);

//   Note that the return value is ViewTarget
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    // Determine whether it is the main thread , If it is not the main thread, it throws an exception 
    Util.assertMainThread();
    // Null logic 
    Preconditions.checkNotNull(view);
    // This requestOptions  It's not clear what it is , It seems to store the relevant attributes of the image display .
    BaseRequestOptions<?> requestOptions = this;
    if (!requestOptions.isTransformationSet()
        && requestOptions.isTransformationAllowed()
        && view.getScaleType() != null) {
      //  Clone in this method , So that if you use this RequestBuilder Load into view , And then load it into other targets ,
      // The conversion applied based on the zoom type of the previous view is not retained .
      // The switch  It is mainly used to control the call Glide Set the various styles , If it is not set, it is the default 
      switch (view.getScaleType()) {
        case CENTER_CROP:
          requestOptions = requestOptions.clone().optionalCenterCrop();
          break;
        case CENTER_INSIDE:
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case FIT_CENTER:
        case FIT_START:
        case FIT_END:
          requestOptions = requestOptions.clone().optionalFitCenter();
          break;
        case FIT_XY:
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case CENTER:
        case MATRIX:
        default:
          // Do nothing.
      }
    }

    return into(
        // Pay attention to this transcodeClass In the above 4.2 In a transcodeClass by Drawable.class
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
  }
//
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
      @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
    return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
  }
// As a result of Drawable.class So this method returns DrawableImageViewTarget object .
public <Z> ViewTarget<ImageView, Z> buildTarget(
      @NonNull ImageView view, @NonNull Class<Z> clazz) {
    if (Bitmap.class.equals(clazz)) {
      return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
    } else if (Drawable.class.isAssignableFrom(clazz)) {
      return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
    } else {
      throw new IllegalArgumentException(
          "Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
    }
  }

In short, the above logic can be understood as

a. call into() First of all, judge whether it is the main thread b. adopt view.getScaleType() Decide which one to use requestOptions object , If not set, the default is used . c. call glideContext.buildImageViewTarget(view, transcodeClass) return DrawableImageViewTarget object .

ok, Let's keep looking into Method , current into It can be understood as

into(DrawableImageViewTarget,null,requestOptions,Executors.mainThreadExecutor());
  private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> options,
      Executor callbackExecutor) {
    // Or check logic 
    Preconditions.checkNotNull(target);
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }
    // establish Request object , Look at your face   This should still be the object of the request , We have to see how to create 
    Request request = buildRequest(target, targetListener, options, callbackExecutor);
    // Here target by DrawableImageViewTarget
    Request previous = target.getRequest();
    if (request.isEquivalentTo(previous) && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      // If the request is complete , Restarting will ensure that the results are retransmitted ,
      // Trigger request listener and target . If the request fails , Then start over 
      //  restart request , There will be another chance to finish . If the request has already started, we can keep it running without interruption 
      if (!Preconditions.checkNotNull(previous).isRunning()) {
      // Use the previous request instead of the new request , For example, skip setting the place holder 、 Tracking and UN tracking targets and getting view dimensions are done in individual requests .
        previous.begin();
      }
      return target;
    }
    // These three methods are analyzed later , It's not something that can be understood in one sentence .
    requestManager.clear(target);
    target.setRequest(request);
    requestManager.track(target, request);

    return target;
  }


private Request buildRequest(
      Target<TranscodeType> target,
      @Nullable RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> requestOptions,
      Executor callbackExecutor) {
    return buildRequestRecursive(
        /*requestLock=*/ new Object(),
        target,
        targetListener,
        /*parentCoordinator=*/ null,
        transitionOptions,
        requestOptions.getPriority(),
        requestOptions.getOverrideWidth(),
        requestOptions.getOverrideHeight(),
        requestOptions,
        callbackExecutor);
  }
// At this time target by DrawableImageViewTarget,targetListener by null
private Request buildRequestRecursive(
      Object requestLock,
      Target<TranscodeType> target,
      @Nullable RequestListener<TranscodeType> targetListener,
      @Nullable RequestCoordinator parentCoordinator,
      TransitionOptions<?, ? super TranscodeType> transitionOptions,
      Priority priority,
      int overrideWidth,
      int overrideHeight,
      BaseRequestOptions<?> requestOptions,
      Executor callbackExecutor) {

    //  if necessary , Please build first ErrorRequestCoordinator, So we can update parentCoordinator.
    // But because we didn't set up error, therefore errorBuilder  by null
    ErrorRequestCoordinator errorRequestCoordinator = null;
    if (errorBuilder != null) {
      errorRequestCoordinator = new ErrorRequestCoordinator(requestLock, parentCoordinator);
      parentCoordinator = errorRequestCoordinator;
    }
    // Let's take a closer look at this mainRequest Create logic 
    Request mainRequest = buildThumbnailRequestRecursive(
                                            requestLock,
                                            target,
                                            targetListener,
                                            parentCoordinator,
                                            transitionOptions,
                                            priority,
                                            overrideWidth,
                                            overrideHeight,
                                            requestOptions,
                                            callbackExecutor);
    // because errorBuilder  by null So the above errorRequestCoordinator Also for the null, Go straight back here 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();
    }

    Request errorRequest = errorBuilder.buildRequestRecursive(
                                            requestLock,
                                            target,
                                            targetListener,
                                            errorRequestCoordinator,
                                            errorBuilder.transitionOptions,
                                            errorBuilder.getPriority(),
                                            errorOverrideWidth,
                                            errorOverrideHeight,
                                            errorBuilder,
                                            callbackExecutor);
    errorRequestCoordinator.setRequests(mainRequest, errorRequest);
    return errorRequestCoordinator;
  }

buildThumbnailRequestRecursive() The logic of the method is relatively long , Let's take it out alone and analyze , But we didn't set it up thumbnail(), therefore thumbnailBuilder and thumbSizeMultiplier Yes, it is null, Only the last one else.

I post the whole logic code , Don't panic , This piece of code , Create only for the last SingleRequest.

private Request buildThumbnailRequestRecursive(
      Object requestLock,
      Target<TranscodeType> target,
      RequestListener<TranscodeType> targetListener,
      @Nullable RequestCoordinator parentCoordinator,
      TransitionOptions<?, ? super TranscodeType> transitionOptions,
      Priority priority,
      int overrideWidth,
      int overrideHeight,
      BaseRequestOptions<?> requestOptions,
      Executor callbackExecutor) {
    if (thumbnailBuilder != null) { 
      ....
     return coordinator;
    } else if (thumbSizeMultiplier != null) {
      ....
      return coordinator;
    } else {
      //  No settings thumbnail go 
      return obtainRequest(
          requestLock,
          target,
          targetListener,
          requestOptions,
          parentCoordinator,
          transitionOptions,
          priority,
          overrideWidth,
          overrideHeight,
          callbackExecutor);
    }
  }

 private Request obtainRequest(
      Object requestLock,
      Target<TranscodeType> target,
      RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> requestOptions,
      RequestCoordinator requestCoordinator,
      TransitionOptions<?, ? super TranscodeType> transitionOptions,
      Priority priority,
      int overrideWidth,
      int overrideHeight,
      Executor callbackExecutor) {
    return SingleRequest.obtain(
        context,
        glideContext,
        requestLock,
        model,
        transcodeClass,
        requestOptions,
        overrideWidth,
        overrideHeight,
        priority,
        target,
        targetListener,
        requestListeners,
        requestCoordinator,
        glideContext.getEngine(),
        transitionOptions.getTransitionFactory(),
        callbackExecutor);
  }

public static <R> SingleRequest<R> obtain(
      Context context,
      GlideContext glideContext,
      Object requestLock,
      Object model,
      Class<R> transcodeClass,
      BaseRequestOptions<?> requestOptions,
      int overrideWidth,
      int overrideHeight,
      Priority priority,
      Target<R> target,
      RequestListener<R> targetListener,
      @Nullable List<RequestListener<R>> requestListeners,
      RequestCoordinator requestCoordinator,
      Engine engine,
      TransitionFactory<? super R> animationFactory,
      Executor callbackExecutor) {
    return new SingleRequest<>(
        context,
        glideContext,
        requestLock,
        model,
        transcodeClass,
        requestOptions,
        overrideWidth,
        overrideHeight,
        priority,
        target,
        targetListener,
        requestListeners,
        requestCoordinator,
        engine,
        animationFactory,
        callbackExecutor);
  }

  private SingleRequest(
      Context context,
      GlideContext glideContext,
      @NonNull Object requestLock,
      @Nullable Object model,
      Class<R> transcodeClass,
      BaseRequestOptions<?> requestOptions,
      int overrideWidth,
      int overrideHeight,
      Priority priority,
      Target<R> target,
      @Nullable RequestListener<R> targetListener,
      @Nullable List<RequestListener<R>> requestListeners,
      RequestCoordinator requestCoordinator,
      Engine engine,
      TransitionFactory<? super R> animationFactory,
      Executor callbackExecutor) {
    this.requestLock = requestLock;
    this.context = context;
    this.glideContext = glideContext;
    this.model = model;
    this.transcodeClass = transcodeClass;
    this.requestOptions = requestOptions;
    this.overrideWidth = overrideWidth;
    this.overrideHeight = overrideHeight;
    this.priority = priority;
    this.target = target;
    this.targetListener = targetListener;
    this.requestListeners = requestListeners;
    this.requestCoordinator = requestCoordinator;
    this.engine = engine;
    this.animationFactory = animationFactory;
    this.callbackExecutor = callbackExecutor;
    status = Status.PENDING;

    if (requestOrigin == null && glideContext.isLoggingRequestOriginsEnabled()) {
      requestOrigin = new RuntimeException("Glide request origin trace");
    }
  }

To generate the SingleRequest 了 , See the head , Is not suddenly do not know where to start again ?

Remember RequestBuilder.into() The last three methods of the method ? This is where we start again ....

 private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> options,
      Executor callbackExecutor) {
    // Or check logic 
    Preconditions.checkNotNull(target);
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }
    // establish Request object , Look at your face   This should still be the object of the request , We have to see how to create 
    Request request = buildRequest(target, targetListener, options, callbackExecutor);
    // Here target by DrawableImageViewTarget
    Request previous = target.getRequest();
    if (request.isEquivalentTo(previous) && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      // If the request is complete , Restarting will ensure that the results are retransmitted ,
      // Trigger request listener and target . If the request fails , Then start over 
      //  restart request , There will be another chance to finish . If the request has already started, we can keep it running without interruption 
      if (!Preconditions.checkNotNull(previous).isRunning()) {
      // Use the previous request instead of the new request , For example, skip setting the place holder 、 Tracking and UN tracking targets and getting view dimensions are done in individual requests .
        previous.begin();
      }
      return target;
    }
    // Yes, yes, yes   These are the three methods .
    requestManager.clear(target);
    target.setRequest(request);
    requestManager.track(target, request);

    return target;
  }

We follow up one by one , Let's take a look at clear()

  public void clear(@Nullable final Target<?> target) {
    if (target == null) {
      return;
    }

    untrackOrDelegate(target);
  }

  private void untrackOrDelegate(@NonNull Target<?> target) {
    boolean isOwnedByUs = untrack(target);
    //  If the target is cleared after starting the request manager for the request , We will end here 
    //  Destroyed . There are at least two reasons :
    // 1.  We call clear(), You are not using the application context request manager .
    // 2.  The caller is destroyed in the corresponding activity or fragment , Use it to start loading , Then use another request manager . The caller seems particularly likely to do so in the reserved fragment (#2262). 
    //
    // #1  It's always a mistake . at best , Callers are just like asynchronous tasks . In the worst case , The caller is on-going 
    //  If they do a long time operation like referring to the active request manager, the background thread or task .
    //
    // #2  It's always a mistake . Callers should not leak memory to the request manager due to reservation . It could be a very short time 
    //  Then onDestroy It's reasonable .
    Request request = target.getRequest();
    if (!isOwnedByUs && !glide.removeFromManagers(target) && request != null) {
      target.setRequest(null);
      request.clear();
    }
  }

clear() Relatively simple , Judge the present target Is there a request on it , Make some logical decisions about whether to cancel . then DrawableImageViewTarget Of Request Object empty .

target.setRequest(request); There's nothing to say, just will Request Set to DrawableImageViewTarget In the object .

Let's take a look at the last requestManager.track(target, request);

//track() Two methods are called internally .
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
    targetTracker.track(target);
    requestTracker.runRequest(request);
  }

//TargetTracker.track() Relatively simple , take DrawableImageViewTarget Add to targets  Of Set<Target<?>> In the object .
private final Set<Target<?>> targets = Collections.newSetFromMap(new WeakHashMap<Target<?>, Boolean>());
public void track(@NonNull Target<?> target) {
    targets.add(target);
  }

private final Set<Request> requests = Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());
//RequestTracker.runRequest()
public void runRequest(@NonNull Request request) {
    // First of all, will request Object added to Set<Request> Unified management in objects 
    requests.add(request); 
    if (!isPaused) {
      // If the interface is not in onStop state , The representative page is visible 
      request.begin();
    } else {
      // If the interface is in onStop state , The page is not visible 
      request.clear();
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Paused, delaying request");
      }
     // Add to pendingRequests In the object 
     //pendingRequests The notes for ,pendingRequests Used to store unfinished requests , For next request , This list is used to maintain strong references , Make sure not to be run again GC,
      pendingRequests.add(request);
    }
  }

Let's move on to the code above request.begin() Method , Here requests by SingleRequest object , So just look SingleReques.begin();

public void begin() {
    synchronized (requestLock) {
      assertNotCallingCallbacks();
      stateVerifier.throwIfRecycled();
      startTime = LogTime.getLogTime();
      // First of all, model  Judge ,model  by load The one that came in url.
      if (model == null) {
         // If null It's a direct failure .
        if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
          width = overrideWidth;
          height = overrideHeight;
        }
       // When the user sets the fallback drawable, Enter detailed log , because fallback drawable Need to be empty occasionally models 
        int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
        onLoadFailed(new GlideException("Received null model"), logLevel);
        return;
      }
      // If the current state is already running , Throw exception 
      if (status == Status.RUNNING) {
        throw new IllegalArgumentException("Cannot restart a running request");
      }

      // If it's done and reloaded ,( Usually by notifyDataSetChanged This way , In the same Target or View Start in   Same request )
      // We can simply use the last retrieved resource and size , Skip getting new size , Start new loading and other links .
      // Because the view size may have changed , At this point, users who want to restart the load need to manually clear before starting the load View or Target.
      if (status == Status.COMPLETE) {
        onResourceReady(resource, DataSource.MEMORY_CACHE);
        return;
      }

      // For requests that are neither completed nor running , You can think of its restart as a new request .
      status = Status.WAITING_FOR_SIZE;  
      // Judging width and height is effective for dogs .
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        onSizeReady(overrideWidth, overrideHeight);
      } else {
        // Width and height are invalid , Need to wait for valid 
        target.getSize(this);
      }

      if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)&& canNotifyStatusChanged()) {
        target.onLoadStarted(getPlaceholderDrawable());
      }
      if (IS_VERBOSE_LOGGABLE) {
        logV("finished run method in " + LogTime.getElapsedMillis(startTime));
      }
    }
  }

2 There's a way

onSizeReady(overrideWidth, overrideHeight); target.onLoadStarted(getPlaceholderDrawable());

brothers , fast , This time it was really fast ... Hold on for a second .

Oh, my God , Can be regarded as the dawn of victory , I'm too hard ....

  public void onSizeReady(int width, int height) {
    stateVerifier.throwIfRecycled();
    if (IS_VERBOSE_LOGGABLE) {
      logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
    }
    // Check whether the current status is waiting .
    if (status != Status.WAITING_FOR_SIZE) {
      return;
    }
    status = Status.RUNNING;

    float sizeMultiplier = requestOptions.getSizeMultiplier();
    this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
    this.height = maybeApplySizeMultiplier(height, sizeMultiplier);

    if (IS_VERBOSE_LOGGABLE) {
      logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
    }
    // It's a lot of hype , This is the core of loading , Cache request , It's all here , I'll write a separate article to explain , Or I'm afraid you can't hold on .
    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);

    // This is a hack that's only useful for testing right now where loads complete synchronously
    // even though under any executor running on any thread but the main thread, the load would
    // have completed asynchronously.
    if (status != Status.RUNNING) {
      loadStatus = null;
    }
    if (IS_VERBOSE_LOGGABLE) {
      logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
    }
  }

  //ImageViewTarget.onLoadStarted() Loading complete 
  public void onLoadStarted(@Nullable Drawable placeholder) {
    super.onLoadStarted(placeholder);
    setResourceInternal(null);
    setDrawable(placeholder);
  }

  public void setDrawable(Drawable drawable) {
    view.setImageDrawable(drawable);
  }

The logic of scalp numbness is spray .... Um. .. incorrect , It's over at last .

You can see that. I respect you as a man , It's a great honor to learn such an excellent framework . It doesn't matter if you don't understand it once . What matters is the heart of learning .

I don't want to say more , The last sentence .

Even if the road ahead is confused , Even if the sky is apart , Even if the blade in my hand is broken , It can't stop me from looking for you . My original intention , Never forget , The story is not over yet .

Last

Finally, in order to help you understand Android The principle of relevant knowledge points and interview related knowledge , Here's what I've collected 24 Set Tencent 、 Bytes to beat 、 Ali 、 Baidu 2019-2020BAT Analysis of the real interview questions , I interviewed the big factory Often asked about technical points The video and PDF( In fact, it took a lot more energy than expected ), The context of knowledge + A lot of details .

also Advanced architecture technology advanced brain map To help you learn and upgrade , It also saves time for people to search information on the Internet to learn , You can also share it with your friends and learn together .

I will review the interview questions according to this information , Then I went to the interview , It helps a lot ~

All of the above are in open source projects :github Has been included in , It includes self-study in different directions Android Route 、 Interview question set / Face the 、 And a series of technical articles , Resources are constantly updated ...

It's easy to be a programmer , To be a good programmer is to keep learning , From junior programmer to senior programmer , From junior architect to senior architect , Or go to management , From technical manager to technical director , Each stage requires different abilities . Set your career direction early , In order to get rid of peers in work and ability improvement .

Original statement , This article is authorized by the author + Community publication , Unauthorized , Shall not be reproduced .

If there is any infringement , Please contact the yunjia_community@tencent.com Delete .

版权声明
本文为[Android technology dry goods sharing]所创,转载请带上原文链接,感谢
https://chowdera.com/2020/11/20201113185659764t.html