Android source code advanced Glide loading process and source code detailed explanation

Android source code advanced Glide loading process and source code detailed explanation

[[421043]]

This article is reprinted from the WeChat public account "Android Development Programming", the author is Android Development Programming. Please contact the Android Development Programming public account for reprinting this article.

Preface

Glide is an open source image loading library for Android written in pure Java. It can help us download, cache, and display images in various formats, including GIF format.

Yesterday we analyzed the cache strategy mechanism of Glide from the source code;

So today we will strike while the iron is hot to analyze a wave of loading processes;

1. Common loading methods of glide

1. Load the image into imageView

  1. Glide. with (Context context). load (Strint url). into (ImageView imageView);

2. Load various forms of pictures into ImageView

  1. // Load local images
  2. File file = new File(getExternalCacheDir() + "/image.jpg" );
  3. Glide. with (this). load (file). into (imageView);
  4. // Load application resources
  5. int resource = R.drawable.image;
  6. Glide. with (this). load (resource). into (imageView);
  7. // Load binary stream
  8. byte[] image = getImageBytes();
  9. Glide. with (this). load (image). into (imageView);
  10. // Load the Uri object
  11. Uri imageUri = getImageUri();
  12. Glide. with (this). load (imageUri). into (imageView);

3. Loading with placeholder image

  1. Glide. with (this). load (url).placeholder(R.drawable.loading). into (imageView);

The purpose of the placeholder image is to display an image to the user in advance when the target image has not yet been loaded;

4. Place a placeholder when loading fails

  1. Glide. with (this). load (url).placeholder(R.drawable.loading).error(R.drawable.error)
  2. .diskCacheStrategy(DiskCacheStrategy.NONE) //Disable Glide's hard disk cache mechanism
  3. .into (imageView);
  4. //DiskCacheStrategy.NONE: means no content is cached.
  5. //DiskCacheStrategy.SOURCE: means only the original image is cached.
  6. //DiskCacheStrategy.RESULT: means only caching converted images (default option).
  7. //DiskCacheStrategy. ALL : Indicates that both the original images and the converted images are cached.

5. Load pictures in the specified format--specify as still pictures

  1. Glide.with ( this)
  2. .load (url)
  3. .asBitmap() //Only load static images. If it is a git image, only load the first frame.
  4. .placeholder(R.drawable.loading)
  5. .error(R.drawable.error)
  6. .diskCacheStrategy(DiskCacheStrategy.NONE)
  7. .into (imageView);

6. Load dynamic images

  1. Glide.with ( this)
  2. .load (url)
  3. .asGif()//Load a dynamic image. If the existing image is not a gif image, directly load the error placeholder image.
  4. .placeholder(R.drawable.loading)
  5. .error(R.drawable.error)
  6. .diskCacheStrategy(DiskCacheStrategy.NONE)
  7. .into (imageView);

7. Load pictures of specified size

  1. Glide.with ( this)
  2. .load (url)
  3. .placeholder(R.drawable.loading)
  4. .error(R.drawable.error)
  5. .diskCacheStrategy(DiskCacheStrategy.NONE)
  6. .override(100, 100) //Specify the image size
  7. .into (imageView);

8. Turn off the framework's memory cache mechanism

  1. Glide.with ( this)
  2. .load (url)
  3. .skipMemoryCache( true ) //When the passed parameter is false , the memory cache is turned off.
  4. .into (imageView);

9. Turn off the hard disk cache

  1. Glide.with ( this)
  2. .load (url)
  3. .diskCacheStrategy(DiskCacheStrategy.NONE) //Disable hard disk cache operation
  4. .into (imageView);
  5. //Other parameters indicate:
  6. //DiskCacheStrategy.NONE: means no content is cached.
  7. //DiskCacheStrategy.SOURCE: means only the original image is cached.
  8. //DiskCacheStrategy.RESULT: means only caching converted images (default option).
  9. //DiskCacheStrategy. ALL : Indicates that both the original images and the converted images are cached.

10. Solution when the referenced URL has a token

  1. public class MyGlideUrl extends GlideUrl {
  2. private String mUrl;
  3. public MyGlideUrl(String url) {
  4. super(url);
  5. mUrl = url;
  6. }
  7. @Override
  8. public String getCacheKey() {
  9. return mUrl.replace ( findTokenParam (), "" );
  10. }
  11. private String findTokenParam() {
  12. String tokenParam = "" ;
  13. int tokenKeyIndex = mUrl.indexOf( "?token=" ) >= 0 ? mUrl.indexOf( "?token=" ) : mUrl.indexOf( "&token=" );
  14. if (tokenKeyIndex != -1) {
  15. int nextAndIndex = mUrl.indexOf( "&" , tokenKeyIndex + 1);
  16. if (nextAndIndex != -1) {
  17. tokenParam = mUrl.substring (tokenKeyIndex + 1, nextAndIndex + 1);
  18. } else {
  19. tokenParam = mUrl.substring (tokenKeyIndex);
  20. }
  21. }
  22. return tokenParam;
  23. }
  24. }

Then load the image as follows:

  1. Glide.with ( this)
  2. . load (new MyGlideUrl(url))
  3. .into (imageView);

11. Use Glide to load images into different controls or load them into different usage modes

(1) Get the picture example

  1. //1. You can get the image instance by constructing the target yourself
  2. SimpleTarget<GlideDrawable> simpleTarget = new SimpleTarget<GlideDrawable>() {
  3. @Override
  4. public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) {
  5. imageView.setImageDrawable(resource);
  6. }
  7. };
  8. //2. Record the image instance to the specified imageview, and you can also do other things
  9. public void loadImage( View   view ) {
  10. String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg" ;
  11. Glide.with (this)
  12. .load (url)
  13. . into (simpleTarget);
  14. }

(2) Load the image to any location

  1. /*
  2. * Load the image as the control background
  3. */
  4. public class MyLayout extends LinearLayout {
  5. private ViewTarget<MyLayout, GlideDrawable> viewTarget;
  6. public MyLayout(Context context, AttributeSet attrs) {
  7. super(context, attrs);
  8. viewTarget = new ViewTarget<MyLayout, GlideDrawable>(this) {
  9. @Override
  10. public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) {
  11. MyLayout myLayout = getView();
  12. myLayout.setImageAsBackground(resource);
  13. }
  14. };
  15. }
  16. public ViewTarget<MyLayout, GlideDrawable> getTarget() {
  17. return viewTarget;
  18. }
  19. public void setImageAsBackground(GlideDrawable resource) {
  20. setBackground(resource);
  21. }
  22. }
  23. //Reference the image to the specified control as the background
  24. public class MainActivity extends AppCompatActivity {
  25. MyLayout myLayout;
  26. @Override
  27. protected void onCreate(Bundle savedInstanceState) {
  28. super.onCreate(savedInstanceState);
  29. setContentView(R.layout.activity_main);
  30. myLayout = (MyLayout) findViewById(R.id.background);
  31. }
  32. public void loadImage( View   view ) {
  33. String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg" ;
  34. Glide.with (this)
  35. .load (url)
  36. . into (myLayout.getTarget());
  37. }
  38. }

12. Glide implements preloading

  1. //a. Preload code
  2. Glide.with (this)
  3. .load (url)
  4. .diskCacheStrategy(DiskCacheStrategy.SOURCE)
  5. .preload();
  6. //preload() has two overloads
  7. // 1. Overload with parameters. The parameter is used to set the size of the preloaded image.
  8. //2. Without parameters, the loaded image is of original size;
  9. //b. Use preloaded images
  10. Glide.with (this)
  11. .load (url)
  12. .diskCacheStrategy(DiskCacheStrategy.SOURCE)
  13. .into (imageView);

2. Detailed explanation of Glide loading process

1. with(context)

  1. // Glide.java
  2. public   static RequestManager with (@NonNull Context context) {
  3. return getRetriever(context).get(context);
  4. }
  5. public   static RequestManager with (@NonNull Activity activity) {
  6. return getRetriever(activity).get(activity);
  7. }
  8. public   static RequestManager with (@NonNull FragmentActivity activity) {
  9. return getRetriever(activity).get(activity);
  10. }
  11. public   static RequestManager with (@NonNull Fragment fragment) {
  12. return getRetriever(fragment.getContext()).get(fragment);
  13. }
  14. public   static RequestManager with (@NonNull View   view ) {
  15. return getRetriever( view .getContext()).get( view );
  16. }

This function creates a Glide instance and initializes some basic parameters, then creates a RequestManager object and returns it. There are five scenarios in total, and here we will analyze the case where the parameter is of type Context.

  1. // Glide.java
  2. public   static RequestManager with (@NonNull Context context) {
  3. return getRetriever(context).get(context);
  4. }

It can be seen that the function first calls getRetriever(context) to obtain the RequestManagerRetriever object. Before creating the object, the Glide instance is first obtained through the get method in Glide.java (Glide is a singleton), and the configuration of AppGlideModule and AndroidManifest.xml is read at the same time.

  1. // Glide.java
  2. private static RequestManagerRetriever getRetriever(@Nullable Context context) {
  3. // Glide.get(context) gets the Glide instance
  4. return Glide.get(context).getRequestManagerRetriever();
  5. }
  6. public   static Glide get(@NonNull Context context) {
  7. if (glide == null ) {
  8. // Load AppGlideModule
  9. GeneratedAppGlideModule annotationGeneratedModule =
  10. getAnnotationGeneratedGlideModules(context.getApplicationContext());
  11. synchronized (Glide.class) {
  12. if (glide == null ) {
  13. // Load Mainfest configuration and register module callback
  14. // This step executes the Glide.build() method to construct a Glide instance. The build method will be discussed below.
  15. checkAndInitializeGlide(context, annotationGeneratedModule);
  16. }
  17. }
  18. }
  19. return glide;
  20. }

After obtaining the Glide instance, the getRequestManagerRetriever method is called to return the RequestManagerRetriever object that has been initialized in the previous step.

  1. // Glide.java
  2. public RequestManagerRetriever getRequestManagerRetriever() {
  3. return requestManagerRetriever;
  4. }

Next, let's take a look at how RequestManagerRetriever is initialized and what it does during the initialization process. First, paste the source code to see the specific implementation of the Glide.build method (this method is called in the checkAndInitializeGlide() function above):

  1. // GlideBuilder.java
  2. Glide build(@NonNull Context context) {
  3. // Allocate thread pool and configure cache strategy
  4. sourceExecutor = GlideExecutor.newSourceExecutor();
  5. diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
  6. animationExecutor = GlideExecutor.newAnimationExecutor();
  7. memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
  8. //Monitor network changes
  9. connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
  10. int   size = memorySizeCalculator.getBitmapPoolSize();
  11. if ( size > 0 ) {
  12. bitmapPool = new LruBitmapPool( size );
  13. } else {
  14. bitmapPool = new BitmapPoolAdapter();
  15. }
  16. arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
  17. memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
  18. diskCacheFactory = new InternalCacheDiskCacheFactory(context);
  19. // The engine is responsible for executing the loading task
  20. if (engine == null ) {
  21. engine =
  22. new Engine(
  23. memoryCache,
  24. diskCacheFactory,
  25. diskCacheExecutor,
  26. sourceExecutor,
  27. GlideExecutor.newUnlimitedSourceExecutor(),
  28. animationExecutor,
  29. isActiveResourceRetentionAllowed);
  30. }
  31. if (defaultRequestListeners == null ) {
  32. defaultRequestListeners = Collections.emptyList();
  33. } else {
  34. defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners);
  35. }
  36. RequestManagerRetriever requestManagerRetriever =
  37. new RequestManagerRetriever(requestManagerFactory);
  38. return new Glide(
  39. context,
  40. engine,
  41. memoryCache,
  42. bitmapPool,
  43. arrayPool,
  44. requestManagerRetriever,
  45. connectivityMonitorFactory,
  46. logLevel,
  47. defaultRequestOptionsFactory,
  48. defaultTransitionOptions,
  49. defaultRequestListeners,
  50. isLoggingRequestOriginsEnabled,
  51. isImageDecoderEnabledForBitmaps);
  52. }
  • When executing the Glide.get() method, resource loading, cache thread pool, and cache strategy have been allocated. The engine here is responsible for loading and decoding resources. ConnectivityMonitor registers a network status listener. When the network is disconnected, it suspends requesting network resources and continues requesting resources after reconnection.
  • RequestManagerRetriever is originally constructed by the RequestManagerFactory factory class. Enter the RequestManagerFactory.java class and you can see that the get method obtains the corresponding RequestManager object;
  • From here we can see that in either case, when the App enters the background, the page will become invisible. At this time, RequestManager is bound to ApplicationContext, which is consistent with the life cycle of App. Therefore, the callback function related to the life cycle is also implemented in the RequestManager.java class;
  1. // RequestManagerRetriever.java
  2. // get has several overloaded methods, here we only select the context parameter for analysis
  3. public RequestManager get(@NonNull Context context) {
  4. if (Util.isOnMainThread() && !(context instanceof Application)) {
  5. if (context instanceof FragmentActivity) {
  6. return get((FragmentActivity) context);
  7. } else if (context instanceof Activity) {
  8. return get((Activity) context);
  9. } else if (context instanceof ContextWrapper
  10. && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null ) {
  11. return get(((ContextWrapper) context).getBaseContext());
  12. }
  13. }
  14. return getApplicationManager(context);
  15. }

After executing Glide.with(context), we get a corresponding RequestManager object, and then execute the next task load(url);

2. load(url)

After getting the RequestManager, call the load method to start the next step. Let's first look at the implementation of the load method.

  1. // RequestManager.java
  2. public RequestBuilder<Drawable> load (@Nullable Bitmap bitmap) {
  3. return asDrawable(). load (bitmap);
  4. }
  5. public RequestBuilder<Drawable> load (@Nullable Drawable drawable) {
  6. return asDrawable(). load (drawable);
  7. }
  8. public RequestBuilder<Drawable> load (@Nullable String string) {
  9. return asDrawable(). load (string);
  10. }
  11. public RequestBuilder<Drawable> load (@Nullable Uri uri) {
  12. return asDrawable(). load (uri);
  13. }
  14. public RequestBuilder<Drawable> load (@Nullable File file) {
  15. return asDrawable(). load (file);
  16. }
  17. public RequestBuilder<Drawable> load (@RawRes @DrawableRes @Nullable Integer resourceId) {
  18. return asDrawable(). load (resourceId);
  19. }
  20. public RequestBuilder<Drawable> load (@Nullable URL url) {
  21. return asDrawable(). load (url);
  22. }
  23. public RequestBuilder<Drawable> load (@Nullable byte[] model) {
  24. return asDrawable(). load (model);
  25. }
  26. public RequestBuilder<Drawable> load (@Nullable Object model) {
  27. return asDrawable(). load (model);
  28. }
  • load() also has multiple overloaded functions. The parameters passed in can be image objects Bitmap, Drawable, local resource Uri, online resource path Url, file object File, assets resource id. Here we only look at the case where the parameter is Url;
  • asDrawable().load(url) returns a RequestBuilder object. First, let’s see what the asDrawable method does;
  1. // RequestManager.java
  2. public RequestBuilder<Drawable> asDrawable() {
  3. return   as (Drawable.class);
  4. }
  5. public <ResourceType> RequestBuilder<ResourceType> as (@NonNull Class<ResourceType> resourceClass) {
  6. return new RequestBuilder<>(glide, this, resourceClass, context);
  7. }

The asDrawable method creates a RequestBuilder object and then calls the load method in RequestBuilder.java;

  1. // RequestBuilder.java
  2. // The String type URL passed in will be used as the cache key  
  3. public RequestBuilder<TranscodeType> load (@Nullable String string) {
  4. return loadGeneric(string);
  5. }
  6. // This returns itself
  7. private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
  8. this.model = model;
  9. isModelSet = true ;
  10. return this;
  11. }

The main job of the load function is to construct a corresponding RequestBuilder object based on the resource type passed in. Now all preparations are ready, and the next step is the most important step - loading and displaying files. Let's see how the into(view) method completes these tasks;

3. into(view)

What we get is the corresponding type RequestBuilder instance, so let's look at the specific implementation of the into method in this class. The into method also has two overloaded functions: into(@NonNull Y target) and into(@NonNull ImageView) (these two functions will eventually go to the same function). When calling the into method, the parameter we pass in is of type ImageView;

  1. // RequestBuilder.java
  2. public ViewTarget<ImageView, TranscodeType> into (@NonNull ImageView view ) {
  3. Util.assertMainThread();
  4. BaseRequestOptions<?> requestOptions = this;
  5. // View 's scale type.
  6. // Process image scaling and initialize the corresponding requestOptions object according to the scaling type
  7. ......
  8. return   into (
  9. glideContext.buildImageViewTarget( view , transcodeClass),
  10. /*targetListener=*/ null ,
  11. requestOptions,
  12. Executors.mainThreadExecutor() // handler running on the main thread
  13. );
  14. }
  • The above code snippet first processes the image scaling type (cropping, alignment, etc.), and puts the generated related parameters into the requestOptions object, and then passes it as a parameter to the private method into of the RequestBuilder.java class. The four parameters defined in this method are: viewTarget, target callback listener, request parameters, and callback function of the main thread;
  • Obviously, the external ImageView object is finally converted into a ViewTarget object, and the conversion function is glideContext.buildImageViewTarget(view, transcodeClass);
  1. // GlideContext.java
  2. public <X> ViewTarget<ImageView, X> buildImageViewTarget(@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
  3. return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
  4. }

ViewTarget is generated by the ImageViewTargetFactory factory method. Next, let’s see how the buildTarget method generates a ViewTarget object.

  1. // imageViewTargetFactory.java
  2. public class ImageViewTargetFactory {
  3. public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view , @NonNull Class<Z> clazz) {
  4. if (Bitmap.class.equals(clazz)) {
  5. return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget( view );
  6. } else if (Drawable.class.isAssignableFrom(clazz)) {
  7. return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget( view );
  8. } else {
  9. throw new IllegalArgumentException( "Unhandled class: " + clazz + ", try .as(Class).transcode(ResourceTranscoder)" );
  10. }
  11. }
  12. }

You can see that no matter what type of parameter is passed in, it will eventually be converted into two types of ViewTarget: BitmapImageViewTarget; DrawableImageViewTarget; the choice here depends on whether the asBitmap(), asGif(), and asDrawable() functions are called. The default is Bitmap type, so the default returned here is BitmapImageViewTarget;

  1. // BitmapImageViewTarget.java
  2. public class BitmapImageViewTarget extends ImageViewTarget<Bitmap> {
  3. public BitmapImageViewTarget(ImageView view ) {
  4. super( view );
  5. }
  6. @Override
  7. protected void setResource(Bitmap resource) {
  8. view .setImageBitmap(resource); // Display the image
  9. }
  10. }

Now that ViewTarget has been created, let's go back to the private into method of RequestBuilder.java

  1. // RequestBuilder.java`
  2. private <Y extends Target<TranscodeType>> Y into (
  3. @NonNull Y target,
  4. @Nullable RequestListener<TranscodeType> targetListener,
  5. BaseRequestOptions<?> options,
  6. Executor callbackExecutor) {
  7. // Note 1: Create request
  8. Request request = buildRequest(target, targetListener, options, callbackExecutor);
  9. // Get the previous request object
  10. Request previous = target.getRequest();
  11. // Same as the previous request AND the previous request has completed
  12. if (request.isEquivalentTo(previous)&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
  13. // The previous request has completed, so restart it
  14. if (!Preconditions.checkNotNull(previous).isRunning()) {
  15. previous.begin ();
  16. }
  17. return target;
  18. }
  19. // If it is different from the previous request, clear the previous one and add the new request
  20. requestManager.clear(target);
  21. target.setRequest(request);
  22. requestManager.track(target, request);
  23. return target;
  24. }

Following the code order, let's see what this method does at each step:

  • First, execute the buildRequest method to create a new Request request req1;
  • Get the ongoing Request req2 on the current ViewTarget;
  • Determine whether the newly created request req1 is the same as the existing request req2. If they are the same, determine whether to skip the cache of the request req2. If both conditions are met, start executing the begin() method to request resources and stop executing further. If neither condition is met, continue to step 4.
  • Set the latest request req1 to ViewTarget, and then execute the track method to track req1.
  • Executing the into(view) method first gets the Request request, and then starts executing the Request. If it is a reused Request, directly execute begin(), otherwise execute track(target, request), but eventually still execute begin();
  1. // RequestManager.java
  2. synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
  3. //Binding with lifecycle
  4. targetTracker.track(target);
  5. // Start request
  6. requestTracker.runRequest(request);
  7. }
  8. // RequestTracker.java
  9. public void runRequest(@NonNull Request request) {
  10. requests.add (request);
  11. if (!isPaused) {
  12. request.begin (); // Start loading immediately
  13. } else {
  14. //Prevents any bitmaps from previous requests from being loaded, releases any resources owned by this request, displays the current placeholder (if one was provided), and marks the request as canceled.
  15. // request.java(Interface)
  16. request.clear();
  17. pendingRequests.add (request); // Join the queue and wait for execution
  18. }
  19. }
  • The source code of the track method first executes targetTracker.track(target) to listen to the ViewTarget's request, and then runRequest starts to execute. Since the request is ultimately started through the begin() method, let's first look at the specific implementation of the begin() method;
  • The Request class is of interface type, and begin() is its abstract method. So if we want to figure out the specific implementation of begin(), we must first find the implementation class of Request and start with the buildRequest(xx) method. Also, post the source code first:
  1. // RequestBuilder.java
  2. private Request buildRequest(
  3. Target<TranscodeType> target,
  4. @Nullable RequestListener<TranscodeType> targetListener,
  5. BaseRequestOptions<?> requestOptions,
  6. Executor callbackExecutor) {
  7. return buildRequestRecursive(
  8. /*requestLock=*/ new Object(),
  9. target,
  10. targetListener,
  11. /*parentCoordinator=*/ null ,
  12. transitionOptions,
  13. requestOptions.getPriority(),
  14. requestOptions.getOverrideWidth(),
  15. requestOptions.getOverrideHeight(),
  16. requestOptions,
  17. callbackExecutor);
  18. }
  19. private Request buildRequestRecursive(
  20. Object requestLock,
  21. Target<TranscodeType> target,
  22. @Nullable RequestListener<TranscodeType> targetListener,
  23. @Nullable RequestCoordinator parentCoordinator,
  24. TransitionOptions<?, ? super TranscodeType> transitionOptions,
  25. Priority priority,
  26. int overrideWidth,
  27. int overrideHeight,
  28. BaseRequestOptions<?> requestOptions,
  29. Executor callbackExecutor) {
  30. // Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
  31. ErrorRequestCoordinator errorRequestCoordinator = null ;
  32. // Request error
  33. if (errorBuilder != null ) {
  34. errorRequestCoordinator = new ErrorRequestCoordinator(requestLock, parentCoordinator);
  35. parentCoordinator = errorRequestCoordinator;
  36. }
  37. // It is not possible to confirm which of the completion request and the thumbnail request is completed first, so when the thumbnail is completed after the completion request, the thumbnail will no longer be displayed
  38. Request mainRequest =
  39. buildThumbnailRequestRecursive(
  40. requestLock,
  41. target,
  42. targetListener,
  43. parentCoordinator,
  44. transitionOptions,
  45. priority,
  46. overrideWidth,
  47. overrideHeight,
  48. requestOptions,
  49. callbackExecutor);
  50. // The request is successful, and the thumbnail Request is returned directly
  51. if (errorRequestCoordinator == null ) {
  52. return mainRequest;
  53. }
  54. // ...
  55. Request errorRequest =
  56. errorBuilder.buildRequestRecursive(
  57. requestLock,
  58. target,
  59. targetListener,
  60. errorRequestCoordinator,
  61. errorBuilder.transitionOptions,
  62. errorBuilder.getPriority(),
  63. errorOverrideWidth,
  64. errorOverrideHeight,
  65. errorBuilder,
  66. callbackExecutor);
  67. // Return thumbnail requests and error requests at the same time
  68. errorRequestCoordinator.setRequests(mainRequest, errorRequest);
  69. return errorRequestCoordinator;
  70. }

Obviously, the mainRequest in the code is the Request we are looking for, which is returned by the buildThumbnailRequestRecursive method. Going deep into it, we find that the Request is actually generated by the SingleRequest.obtain method, which means that the Request we finally get is actually an instance of the SingleRequest class. The process here is relatively simple, so I won't post the code. Let's go directly to the SingleRequest class to see how the begin method is implemented;

  1. // SingleRequest.java
  2. public void begin () {
  3. if (status == Status.COMPLETE) {
  4. // The resource has been downloaded, call back directly
  5. // Execute the animation
  6. onResourceReady(resource, DataSource.MEMORY_CACHE);
  7. return ;
  8. }
  9. // Calculate the size
  10. if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
  11. onSizeReady(overrideWidth, overrideHeight);
  12. } else {
  13. target.getSize(this);
  14. }
  15. if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
  16. && canNotifyStatusChanged()) {
  17. // Start loading
  18. // Set the position
  19. target.onLoadStarted(getPlaceholderDrawable());
  20. }
  21. }

After entering the begin method, first determine if the resource has been loaded, then directly call back onResourceReady to display the image and cache it. Otherwise, measure the image size and then start loading the image (execute the loading task in onSizeReady()) and display the placeholder image at the same time:

① overrideWith, overrideHeight are set by override(width, height):

Glide.with(mContext).load(url).override(75, 75).into(imageView);

②The placeholder image is set by the user calling placeholder(resId):

Glide.with(mContext).load(url).placeholder(resId).into(imageView);

Next, let's look at how onSizeReady() loads the image after measuring the image size:

  1. // SingleRequest.java
  2. @Override
  3. public void onSizeReady( int width, int height) {
  4. if (status != Status.WAITING_FOR_SIZE) {
  5. return ;
  6. }
  7. status = Status.RUNNING;
  8. // Get the image size
  9. float sizeMultiplier = requestOptions.getSizeMultiplier();
  10. this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
  11. this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
  12. // Start loading the task
  13. loadStatus =
  14. engine.load (
  15. glideContext,
  16. model,
  17. requestOptions.getSignature(),
  18. this.width,
  19. this.height,
  20. requestOptions.getResourceClass(),
  21. transcodeClass,
  22. priority,
  23. requestOptions.getDiskCacheStrategy(),
  24. requestOptions.getTransformations(),
  25. requestOptions.isTransformationRequired(),
  26. requestOptions.isScaleOnlyOrNoTransform(),
  27. requestOptions.getOptions(),
  28. requestOptions.isMemoryCacheable(),
  29. requestOptions.getUseUnlimitedSourceGeneratorsPool(),
  30. requestOptions.getUseAnimationPool(),
  31. requestOptions.getOnlyRetrieveFromCache(),
  32. this,
  33. callbackExecutor);
  34. }
  • It can be seen that the actual download task is implemented in the load method of the Engine class, which also involves the image caching logic;
  • Finally, through the Handler mechanism, Glide switches from the worker thread to the main thread, and finally displays the Drawable object on the ImageView;

Summarize

The entire business process of Glide initialization, displaying placeholder images, and picture covers has been completed;

You can learn the design patterns in Glide: singleton pattern, factory method, strategy pattern, etc., and find your own shortcomings;

Glide's image cache can be found in the previous article

<<:  How to choose a mobile phone? Remember these points, it will help you

>>:  The era of extensive growth is over, and high-precision maps tighten the gray area

Recommend

What issues should enterprises pay attention to when doing bidding hosting?

Since some companies don’t know much about biddin...

Wolf Warriors | How to Tame a Wolf

Author: Li Lu and Wu Cunshuang, Chief Pharmacists...

The battle for supremacy between Tencent and Alibaba—where has Baidu gone?

[[139349]] In 2015, the Chinese stock market was ...

APP strategy analysis: should we make a small program or a big portal?

(one) Many of today’s Internet products are actua...

The tiny rockhopper penguin is the brightest rock star

Rockhopper penguins are small, with a body length...

One article to understand brand overseas marketing

When the growth rate of domestic traffic and demo...

I have a user portrait, but why can’t I understand the user?

The concept of user portrait has been around for ...

Zhihu Marketing Methodology in 2019!

According to the data from the "iiMedia Repo...

An in-depth review of Durex’s red envelope fission!

The Business Card Mini Program gave out 200,000 r...