preface

There are many solutions for loading images on Android. Popular ones include ImageLoader, Picasso, Fresco, and Glide.

  • ImageLoader is an early image loading framework that allows you to monitor the progress of image downloading and pause the loading of a View while scrolling. Supports a variety of cache policies, such as maximum first delete, least use first delete, etc.

  • Picasso is simple and lightweight, supports monitoring of image cache usage, priority processing, and lazy loading. He does not participate in local caching, but hands it over to OkHttp.

  • The Fresco framework is large and cumbersome to use. It supports Gif images and streaming display, similar to the fuzzy progressive display of web images. Two in-memory caches and a Native cache build a three-level cache.

  • Glide supports pulling, decoding, and displaying video snapshots, pictures, and GIF animations. Support priority processing, synchronization with Activity and Fragment life cycles, images default to RGB_565, poor quality and smaller memory. More functional.

It is Glide that Google approves, and it is Glide that is used most in the program.

Gilde profile

Glide is a fast and efficient Android open source media management and image loading framework that packages media decoding, memory and disk caching, and resource pooling in an easy-to-use interface.

Glide supports pulling, decoding, and displaying video snapshots, pictures, and GIF animations.

Glide’s Api is so flexible that developers can even plug in and replace it with any network stack they like. Glide by default uses a customized HttpurlConnection-based stack, but it also provides a tool library for quick integration with Google Volley and Square OkHttp.

Although Glide’s primary goal is to make scrolling through any kind of list of images as fast and smooth as possible, Glide can actually do almost anything you need to pull/zoom/display remote images.

The Android SDK requirements

Lowest SDK version – Glide The lowest SDK version is 14 (Ice Cream Sandwich) or later.

Build SDK Version – Glide must be compiled against SDK version 27 (Oreo MR1) or later.

The required permissions

The network load

To load images over a network connection, you should add INTERNET and ACCESS_NETWORK_STATE permissions to your Androidmanifest.xml:

<manifest>.<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <! Allow Glide to monitor connection status and state when a user is disconnected to a connected network. -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>.</manifest>
Copy the code

The local store

To load images from a local folder such as DCIM or images, you need to add the READ_EXTERNAL_STORAGE permission:

<manifest>.<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>.</manifest>
Copy the code

To store Glide’s cache on a public SDcard, you need the WRITE_EXTERNAL_STORAGE permission:

<manifest>.<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>.</manifest>
Copy the code

performance

Glide takes into account two key aspects of Android image loading performance:

  • 1. Picture decoding speed
  • 2. Resource pressure caused by decoding images

In order for users to have a good App experience, images should not only load quickly, but also not flicker and shake due to excessive main thread I/O or frequent garbage collection.

Glide uses a number of steps to ensure that loading images on Android is as fast and smooth as possible:

  • Automatic and intelligent downsampling and caching to minimize storage overhead and decoding times;
  • Aggressive resource reuse, such as byte arrays and bitmaps, to minimize the impact of expensive garbage collection and heap fragmentation;
  • Deep lifecycle integration to ensure that only active Fragment and Activity requests are processed first, and to help applications free resources if necessary to avoid being killed in the background.

Glide basic usage

  • With can be passed an object in the current context.
  • Load An image. The image can be a local image, an application resource image, or a network image.
  • Placeholder is the image that will be displayed while the request is in progress.
  • Error Displays the Settings picture when the request permanently fails.
  • Into passes in the control to display the picture.
  • Transform is used for clipping (cutting circles) or applying filters to bitmaps, but it can also be used to transform GIFs and even custom resource types.

Simple to use

Add dependencies in build.gradle

implementation 'com. Making. Bumptech. Glide: glide: 4.12.0'
Copy the code

Loading pictures

        Uri uri = Uri.parse("https://profile.csdnimg.cn/C/A/6/1_g984160547");
// String uri = "https://profile.csdnimg.cn/C/A/6/1_g984160547";
        // The above two methods have the same effect
        Glide.with(this).load(uri).into(agb.ivInitial);
        // Apply resources
        Glide.with(this).load(R.mipmap.sc1).into(agb.ivAppMipmap);
        / / draw circles
        Glide.with(this).load(uri).circleCrop().into(agb.ivCirclecrop);
        // Add placeholders and load failures to display pictures
        Glide.with(this).load(uri).
                placeholder(R.mipmap.sc1)
                .error(R.mipmap.sc2)
                .into(agb.ivInitial);
        FutureTarget<Bitmap> target= Glide.with(this)
                .asBitmap()
                .load(uri)
                .submit();
        new Thread(new Runnable() {
            @Override
            public void run(a) {
                try {
                    Bitmap bitmap = target.get();// Synchronize the request
                    // The child thread cuts the main thread to update the UI
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run(a) { agb.ivOptions.setImageBitmap(bitmap); }}); }catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
Copy the code

Cancel the loading

Glide.with(this).clear(agb.ivCirclecrop);
Copy the code

Note: While it is a good practice to cancel unnecessary loads in a timely manner, it is not necessary. In fact, Glide automatically unloads and recycles resources when an Activity or Fragment instance passed in Glide. With () is destroyed.

Generated API

Glide V4 uses the Annotation Processor to generate an API that allows applications to extend Glide’s API and include components provided by various integration libraries.

The Generated API mode is designed for two purposes:

  • 1. The integration library can extend custom options for the Generated API.
  • 2. In the Application module, you can package common method groups into an option and use the Generated API

While all of this can be done manually by subclassing RequestOptions, getting it right is more challenging and makes API usage less fluid.

Using the Generated API(advanced usage)

1. Add dependencies in build.gradle

annotationProcessor 'com. Making. Bumptech. Glide: the compiler: 4.12.0'
Copy the code

2. Create a class with the @glidemodule annotation inherited from AppGlideModule. This class generates a streaming API that inlines multiple options and custom options from the integration library:

@GlideModule
public class MyGlideModule extends AppGlideModule 
{}Copy the code

3. The default API name is GlideApp, which is the same as the package name of the AppGlideModule subclass. Replace Glide. With () with glideapp.with () in the Application module to use the API for loading.

GlideApp.with(this).load(uri).circleCrop().into(agb.ivCirclecrop);
Copy the code

Android Studio handles the Annotation Processor and generated API correctly most of the time. However, after you add your AppGlideModule for the first time or make certain types of changes, you may need to rebuild your project. If you find that the API is not imported, you can rebuild it by:

  • 1. Open Build menu.
  • 2. Click Rebuild Project.

Custom request

Glide provides a number of options that can be applied to a single request, including transformations, transitions, caching options, and more.

1. Default options can be applied directly to requests:

	GlideApp.with(this).load(uri)
.placeholder(R.mipmap.sc2)
.error(R.mipmap.sc1)
.override(150)
.circleCrop().into(agb.ivOptions);
Copy the code

2. Share between multiple requests using the RequestOptions class:

RequestOptions requestOptions = new RequestOptions().circleCrop()
.placeholder(R.mipmap.sc2)
.error(R.mipmap.sc1).override(150);
        GlideApp.with(this).load(uri).apply(requestOptions).into(agb.ivOptions);      GlideApp.with(this).load(uri).apply(requestOptions).into(agb.ivCirclecrop);
Copy the code

GlideExtension

The Glide Generated API can be extended in the Application and Library. Extensions use annotated static methods to add new options, modify existing options, and even add additional type support.

Classes annotated by @glideExtension should be written with the mindset of a utility class. The class should have a private, empty constructor, be final, and contain only static methods. An annotated class can have static variables and can refer to other classes or objects.

Classes annotated by @glideExtention can be extended in two ways:

  • GlideOption – Adds a custom option to the RequestOptions.
  • GlideType – Adds support for new resource types (GIF, SVG, and so on).

GlideOption

1. Create a class annotated with @glideExtension. This class generates a streaming API that inlines multiple options and custom options from the integration library:

@GlideExtension
public final class MyGlideExtension {
    private MyGlideExtension(a){}

    @GlideOption
    public staticBaseRequestOptions<? > applyHeadScc(BaseRequestOptions<? > options,int size){
        returnoptions.circleCrop() .override(size) .placeholder(R.mipmap.sc2) .error(R.mipmap.sc1); }}Copy the code

2. This will generate a method in the RequestOptions subclass as follows:

public class GlideOptions extends RequestOptions {
  
  / * * *@see MyGlideExtension#applyHeadScc(BaseRequestOptions, int)
   */
  @SuppressWarnings("unchecked")
  @CheckResult
  @NonNull
  public GlideOptions applyHeadScc(int size) {
    return (GlideOptions) MyGlideExtension.applyHeadScc(this, size); }... }Copy the code

3. You can include as many additional parameters in the method as you want, as long as the first parameter is always RequestOptions:

@GlideOption
    public staticBaseRequestOptions<? > applyHeadScc(BaseRequestOptions<? > options,int size){
        return options.circleCrop()
                .override(size)
                .placeholder(R.mipmap.sc2)
                .error(R.mipmap.sc1);
}
Copy the code

4. Additional parameters will be added as parameters to the generated method:

public GlideOptions applyHeadScc(int size) {
    return (GlideOptions) MyGlideExtension.applyHeadScc(this, size);
  }
Copy the code

5. Use the generated GlideApp class to call your custom method:

GlideApp.with(this)
  .load(uri)
  .applyHeadScc(150)
  .into(agb.ivOptions);
Copy the code

Methods annotated with GlideOption should be static and return BaseRequestOptions
. Note: The Generated method is not available on the standard Glide and RequestOptions classes, only the Generated equivalent is available.

Gaussian blur (rendering)

1. Use the library portal

implementation 'jp. Wasabeef: glide - transformations: 4.3.0'
Copy the code

Begin to use

    @GlideOption
    public staticBaseRequestOptions<? > applyBlurHeadScc(BaseRequestOptions<? > options,int size){
        BlurTransformation blurTransformation = new BlurTransformation(20.1);
        return options
                .override(size)
                .placeholder(R.mipmap.sc2)
                .error(R.mipmap.sc1)
// .circleCrop()
// .transform(blurTransformation);
// .transform(blurTransformation,new CircleCrop());
                .transform(new MultiTransformation<>(blurTransformation,new CircleCrop()));
}
Copy the code

Note: By default, every subsequent call to transform() or any specific transform method (fitCenter(), centerCrop(), bitmapTransform(), etc.) will replace the previous transform.

To apply multiple transforms to a single load, use the MultiTransformation class or the shortcut.transforms() method.

You can also inherit BitmapTransformation and rewrite the Transform method to create a BlurTransformation or a glide Transformation anywhere.

public class BlurTransformation extends BitmapTransformation {

  private static final int VERSION = 1;
  private static final String ID =
    "jp.wasabeef.glide.transformations.BlurTransformation." + VERSION;

  private static final int MAX_RADIUS = 25;
  private static final int DEFAULT_DOWN_SAMPLING = 1;

  private final int radius;
  private final int sampling;

  public BlurTransformation(a) {
    this(MAX_RADIUS, DEFAULT_DOWN_SAMPLING);
  }

  public BlurTransformation(int radius) {
    this(radius, DEFAULT_DOWN_SAMPLING);
  }

  public BlurTransformation(int radius, int sampling) {
    this.radius = radius;
    this.sampling = sampling;
  }

  @Override
  protected Bitmap transform(@NonNull Context context, @NonNull BitmapPool pool,
                             @NonNull Bitmap toTransform, int outWidth, int outHeight) {

    int width = toTransform.getWidth();
    int height = toTransform.getHeight();
    int scaledWidth = width / sampling;
    int scaledHeight = height / sampling;

    Bitmap bitmap = pool.get(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888);

    setCanvasBitmapDensity(toTransform, bitmap);

    Canvas canvas = new Canvas(bitmap);
    canvas.scale(1 / (float) sampling, 1 / (float) sampling);
    Paint paint = new Paint();
    paint.setFlags(Paint.FILTER_BITMAP_FLAG);
    canvas.drawBitmap(toTransform, 0.0, paint);

    try {
      bitmap = RSBlur.blur(context, bitmap, radius);
    } catch (RSRuntimeException e) {
      bitmap = FastBlur.blur(bitmap, radius, true);
    }

    return bitmap;
  }

  @Override
  public String toString(a) {
    return "BlurTransformation(radius=" + radius + ", sampling=" + sampling + ")";
  }

  @Override
  public boolean equals(Object o) {
    return o instanceof BlurTransformation &&
      ((BlurTransformation) o).radius == radius &&
      ((BlurTransformation) o).sampling == sampling;
  }

  @Override
  public int hashCode(a) {
    return ID.hashCode() + radius * 1000 + sampling * 10;
  }

  @Override
  public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) { messageDigest.update((ID + radius + sampling).getBytes(CHARSET)); }}Copy the code

GlideType

GlideType annotated static methods extend the RequestManager. The GlideType annotated method allows you to add support for new types, including specifying default options.

1. For example, to add support for GIF (select resource type), you can add a GlideType method:

    @GlideType(GifDrawable.class)
    public static RequestBuilder<GifDrawable> scGif(RequestBuilder<GifDrawable> requestBuilder) {
        return requestBuilder
                .transition(new DrawableTransitionOptions())
                .apply(decodeTypeOf(GifDrawable.class).lock());
}
Copy the code

The method name is scGif instead of asGif because GlideRequests already have an asGif method.

2. A GlideRequests method is generated as follows:

public class GlideRequests extends RequestManager {
  public GlideRequest<GifDrawable> scGif(a) {
    return (GlideRequest<GifDrawable>) MyGlideExtension.scGif(this.as(GifDrawable.class)); }... }Copy the code

3. Use the generated GlideApp class to call your custom type (select the resource type) :

        GlideApp.with(this)
                .scGif()
                .load(uri)
                .applyBlurHeadScc(150)
                .addListener(new RequestListener<GifDrawable>() {
                    @Override
                    public boolean onLoadFailed(GlideException e, Object model, Target<GifDrawable> target, boolean isFirstResource) {
                        return false;
                    }

                    @Override
                    public boolean onResourceReady(GifDrawable resource, Object model, Target<GifDrawable> target, DataSource dataSource, boolean isFirstResource) {
                        return false;
                    }
                })
                .into(agb.ivCirclecrop);
Copy the code

RequestListener returns a type corresponding to the GifDrawable set to your scGif.

Submit synchronizes the image resource

FutureTarget<Bitmap> target= GlideApp.with(this) .asBitmap() .load(uri) .applyBlurHeadScc(150) .submit(); New Thread(new Runnable() {@override public void run() {try {Bitmap Bitmap = target.get(); The synchronous request / / / / the child thread cutting the main thread to update the UI runOnUiThread (new Runnable () {@ Override public void the run () {agb. IvOptions. SetImageBitmap (bitmap); }}); } catch (Exception e) { e.printStackTrace(); } } }).start();Copy the code

The effect is the same as before. This function is to have special requirements for the acquisition of pictures for special processing.

Application Options

Glide allows applications to have complete control over Glide’s memory and disk cache usage through the AppGlideModule implementation. Glide tries to provide reasonable default options for most applications, but for some applications you may need to customize these values. As you make any changes, measure the results to avoid performance slippage.

Memory cache

Glide by default uses LruResourceCache, a default implementation of the MemoryCache interface that uses fixed-size memory and the LRU algorithm. The size of the LruResourceCache is determined by Glide’s MemorySizeCalculator class, which focuses on the device memory type, device RAM size, and screen resolution.

1. Customize the size of MemoryCache by configuring the MemorySizeCalculator in their AppGlideModule using applyOptions(Context, GlideBuilder) :

@GlideModule
public class MyGlideModule extends AppGlideModule {
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context)
                .setMemoryCacheScreens(2)
                .build();
        builder.setMemoryCache(newLruResourceCache(calculator.getMemoryCacheSize())); }}Copy the code

2. Overwrite cache size directly:

@GlideModule
public class MyGlideModule extends AppGlideModule {
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        int memoryCacheSizeBytes = 1024 * 1024 * 10; // 10mb
        builder.setMemoryCache(newLruResourceCache(memoryCacheSizeBytes)); }}Copy the code

3. Provide your own MemoryCache implementation:

@GlideModule
public class MyGlideModule extends AppGlideModule {
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        builder.setMemoryCache(newMyMemoryCacheImpl()); }}Copy the code

Disk Cache

Glide uses DiskLruCacheWrapper as the default disk cache. DiskLruCacheWrapper is a fixed-size disk cache using the LRU algorithm. The default disk size is 250 MB and is located in a specific directory in the application’s cache folder.

1. If the media displayed is public (retrieved from unauthenticated websites, search engines, etc.), the application can change the location to external storage:

@GlideModule
public class MyGlideModule extends AppGlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {
    builder.setDiskCache(newExternalCacheDiskCacheFactory(context)); }}Copy the code

2. An application can change the size of the disk cache whether it uses internal or external disk cache:

@GlideModule
public class MyGlideModule extends AppGlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {
    int diskCacheSizeBytes = 1024 * 1024 * 100; // 100 MB
    builder.setDiskCache(newInternalCacheDiskCacheFactory(context, diskCacheSizeBytes)); }}Copy the code

3. The application can also change the name of the cache folder in external or memory:

@GlideModule
public class MyGlideModule extends AppGlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {
    int diskCacheSizeBytes = 1024 * 1024 * 100; // 100 MB
    builder.setDiskCache(
        new InternalCacheDiskCacheFactory(context, "cacheFolderName", diskCacheSizeBytes)); }}Copy the code

4. Applications can also choose their own implementation of the DiskCache interface and provide their own diskCache.factory to create caches. Glide uses a factory interface to turn on the disk cache in background threads, which makes it easier for the cache to do IO operations such as checking for path existence without triggering strict mode.

@GlideModule
public class MyGlideModule extends AppGlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {
    builder.setDiskCache(new DiskCache.Factory() {
        @Override
        public DiskCache build(a) {
          return newMyCustomDiskCache(); }}); }}Copy the code

Bitmap pool

Glide uses LruBitmapPool as the default BitmapPool. An LruBitmapPool is a fixed-size BitmapPool in memory that is cleaned using the LRU algorithm. The default size is based on the resolution and density of the device, and also takes into account the return value of the memory class and isLowRamDevice. The calculation is done by the MemorySize Alculator of Glide, similar to the size detection method of MemoryCache of Glide.

1. You can customize the size of the BitmapPool in their AppGlideModule using the applyOptions(Context, GlideBuilder) method and configure the MemorySizeCalculator:

@GlideModule
public class MyGlideModule extends AppGlideModule {
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context)
                .setBitmapPoolScreens(3)
                .build();
        builder.setBitmapPool(newLruBitmapPool(calculator.getBitmapPoolSize())); }}Copy the code

2. Directly copy the size of BitmapPool:

@GlideModule
public class MyGlideModule extends AppGlideModule {
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        int bitmapPoolSizeBytes = 1024 * 1024 * 30; // 30mb
        builder.setBitmapPool(newLruBitmapPool(bitmapPoolSizeBytes)); }}Copy the code

3. Provide your own BitmapPool implementation:

@GlideModule
public class MyGlideModule extends AppGlideModule {
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        builder.setBitmapPool(newMyBitmapPoolImpl()); }}Copy the code

Configure the cache

Disk Cache Strategy

DiskCacheStrategy can be applied to each individual request by the DiskCacheStrategy method. Currently supported policies allow you to prevent the loading process from using or writing to disk cache, optionally caching only unmodified native data, or only altered thumbnails, or both.

Specifying DiskCacheStrategy is very easy:

    Glide.with(this)
            .load(uri)
            .diskCacheStrategy(DiskCacheStrategy.ALL)
            .into(imageView);
Copy the code

A diskCacheStrategy() method can adjust its disk caching strategy. There are five parameters that can be passed in:

  • DiskCacheStrategy. AUTOMATIC: it means for the local data, AUTOMATIC strategy will be stored only transform the thumbnail; Remote data (Uri download) will only store raw data that has not been modified (e.g. transformed, cropped) by your loading process (default).
  • Diskcachestrategy. NONE: indicates that no content is cached.
  • Diskcachestrategy. DATA: indicates that only raw images are cached.
  • Diskcachestrategy. RESULT: Only converted images are cached.
  • Diskcachestrategy. ALL: caching both the original image and the converted image.

Load images only from cache

In some cases, you may want the load to fail as long as the image is not in the cache. . To accomplish this, you can use the onlyRetrieveFromCache method on a single request basis:

Glide.with(this)
        .load(uri)
        .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
        .onlyRetrieveFromCache(true);
        .into(imageView);
Copy the code

If the image is in memory cache or disk cache, it will be displayed. Otherwise, as long as this option is set to true, the load will be considered a failure.

Skip the cache

Glide also provides some alternatives if you want to ensure that a particular request skips disk and/or memory caches (such as image captcha).

Skip only memory cache, use skipMemoryCache() :

Glide.with(fragment)
  .load(url)
  .skipMemoryCache(true)
  .into(imageView);
Copy the code

To skip only disk caching, use diskCacheStrategy.none:

Glide.with(fragment)
  .load(url)
  .diskCacheStrategy(DiskCacheStrategy.NONE)
  .into(imageView);
Copy the code

The above two options can be used simultaneously:

Glide.with(fragment)
  .load(url)
  .diskCacheStrategy(DiskCacheStrategy.NONE)
  .skipMemoryCache(true)
  .into(imageView);
Copy the code

Although there are ways to skip caching, you usually don’t want to. Loading an image from the cache is much faster than the full process of pull-decode-convert to a new image.

Clearing the disk cache

To try to clear all disk cache entries, you can use clearDiskCache.

new Thread(new Runnable() {
            @Override
            public void run(a) {
                try {
                    // This method must be called on the child thread.
                    Glide.get(AppGlobalUtils.getApplication()).clearDiskCache();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
Copy the code

The related resources

Glide Git portal

Glide Chinese document

Glide – transformations portal

glide-transformations: An Android conversion library that provides image conversion for Glide. Here’s how it looks:

The basic use of Glide must be no problem after reading the above content. If there is any problem, please correct it. To learn more, check it out for yourself through the portal below. There will be an article dedicated to Glide caching. Come on, everybody.