Detailed explanation of Glide4.0 integration and use precautions

  • 2021-10-15 11:26:26
  • OfStack

Glide 4.0 is used internally by various Google teams, and 4.0 is considered internally stable. However, external users may find problems that have not been discovered internally. Therefore, this is published as RC. If no major issues with stability or API are identified, a non-RC release is expected shortly.

1. Integration

1. project gradle


 repositories {
    mavenLocal()
 }

2. app gradle


compile 'com.android.support:support-v4:25.3.1'
compile 'com.github.bumptech.glide:glide:4.0.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.0.0'

3. Confusion


#glide4.0
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.AppGlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
 **[] $VALUES;
 public *;
}

# for DexGuard only
-keepresourcexmlelements manifest/application/meta-data@value=GlideModule

#  From glide4.0 At first, GifDrawable Not provided getDecoder() Method, 
#  Need to be obtained by reflection gifDecoder Field value, so you need to keep the GifFrameLoader And GifState Classes are not confused 
-keep class com.bumptech.glide.load.resource.gif.GifDrawable$GifState{*;}
-keep class com.bumptech.glide.load.resource.gif.GifFrameLoader {*;}

4. Instead of configuring GlideModule in AndroidManifest. xml like 3. X in 4.0, it is configured by inheriting subclasses of AppGlideModule through annotations.


@GlideModule
public class GlideConfiguration extends AppGlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {
    // Custom cache directory, disk cache to 150M  In addition 1 Set cache mode 
    builder.setDiskCache(new InternalCacheDiskCacheFactory(context, "GlideImgCache", 150 * 1024 * 1024));
    // Configure the picture cache format   The default format is 8888
    builder.setDefaultRequestOptions(RequestOptions.formatOf(DecodeFormat.PREFER_ARGB_8888));
    ViewTarget.setTagId(R.id.glide_tag_id);
  }

  /**
   *  Disable parsing Manifest Documents 
   *  Mainly aimed at V3 Upgrade to v4 Users can improve the initialization speed and avoid 1 Some potential errors 
   * @return
   */
  @Override
  public boolean isManifestParsingEnabled() {
    return false;
  }

}

2. Use Precautions

1. Use GlideApp instead of Glide, and put asBitmap, asGif, asDrawable and asFile before load (glide3.7. 0 should be called after load).


public static void loadImg(Context context,String url, ImageView imageView){
    GlideApp.with(context)
        .asBitmap()
        .load(url)
        .placeholder(R.drawable.placeholder) // Setting placeholders during resource loading 
        .into(imageView);
  }

2. Placeholder. placeholder (R. drawable. placeholder) cannot be used. 9 Figure, placeholder picture and loaded target picture will be displayed at the same time, but the target picture will display thumbnails first, and then display normally. fallback and error haven't been tested yet. If you are interested, you can test them.

3. When loading the gif diagram, if you call dontAnimate () to remove all animations, gif will fail to load.

4. Calculate the animation duration of gif playing once.

glide 3.7. 0 You can get the animation duration as follows:


 public void loadGif(Context context,ImageView mIvGif,int url){
    Glide.with(context)
        .load(url)
        .listener(new RequestListener<Integer, GlideDrawable>() {
          @Override
          public boolean onException(Exception e, Integer model, Target<GlideDrawable> target, boolean isFirstResource) {
            return false;
          }

          @Override
          public boolean onResourceReady(GlideDrawable resource, Integer model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
            try {
              int duration = 0;
              GifDrawable gifDrawable = (GifDrawable) resource;
              GifDecoder decoder = gifDrawable.getDecoder();
              for (int i = 0; i < gifDrawable.getFrameCount(); i++) {
                duration += decoder.getDelay(i);
              }
              Log.e("Glide3.7.0","gif Play 1 Sub-animation duration duration:"+duration);
            } catch (Throwable e) {
            }
            return false;
          }
        })
        .into(new GlideDrawableImageViewTarget(mIvGif, 1));
  }

GifDrawable in glide 4.0 does not provide the getDecoder () method and also removes the member variable decoder. In addition, GlideDrawableImageViewTarget class is removed from glide4.0, so how can we calculate the duration of playing gif once? You can only find the answer from the source code.

(1) Looking for decoder

In glide 3.7. 0 GifDrawable, we can see that decoder is eventually passed into the GifFrameLoader class and assigned to the gifDecoder variable.


// Source code 
//glide 3.7.0 GifDrawable.java
 GifDrawable(GifState state) {
    if (state == null) {
      throw new NullPointerException("GifState must not be null");
    }

    this.state = state;
    this.decoder = new GifDecoder(state.bitmapProvider);
    this.paint = new Paint();
    decoder.setData(state.gifHeader, state.data);
    frameLoader = new GifFrameLoader(state.context, this, decoder, state.targetWidth, state.targetHeight);
    frameLoader.setFrameTransformation(state.frameTransformation);
  }
/*---------------------------------------------------------------------------------------------------*/
//glide 3.7.0 GifFrameLoader.java
private final GifDecoder gifDecoder;// Private attribute 
public GifFrameLoader(Context context, FrameCallback callback, GifDecoder gifDecoder, int width, int height) {
    this(callback, gifDecoder, null,
        getRequestBuilder(context, gifDecoder, width, height, Glide.get(context).getBitmapPool()));
 }

  GifFrameLoader(FrameCallback callback, GifDecoder gifDecoder, Handler handler,
      GenericRequestBuilder<GifDecoder, GifDecoder, Bitmap, Bitmap> requestBuilder) {
    if (handler == null) {
      handler = new Handler(Looper.getMainLooper(), new FrameLoaderCallback());
    }
    this.callback = callback;
    // Look here 
    this.gifDecoder = gifDecoder;
    this.handler = handler;
    this.requestBuilder = requestBuilder;
  }

In the construction of glide4.0 GifDrawable class, we can see that there is one gifDecoder parameter, which is interpreted as the decoder is used to decode GIF data (The decoder to use to decode GIF data). Continuing to look at this construction, we find that gifDecoder is also eventually passed into the GifFrameLoader class and assigned to the gifDecoder variable. So decoder in glide 3.7. 0 is actually gifDecoder in 4.0.


// Source code 
//glide 4.0 GifDrawable.java
private final GifState state;
/*
  * @param gifDecoder     The decoder to use to decode GIF data.
  * @param firstFrame     The decoded and transformed first frame of this GIF.
  * @see #setFrameTransformation(com.bumptech.glide.load.Transformation, android.graphics.Bitmap)
  */
 public GifDrawable(Context context, GifDecoder gifDecoder, BitmapPool bitmapPool,
   Transformation<Bitmap> frameTransformation, int targetFrameWidth, int targetFrameHeight,
   Bitmap firstFrame) {
  this(
    new GifState(
      bitmapPool,
      new GifFrameLoader(
        // TODO(b/27524013): Factor out this call to Glide.get()
        Glide.get(context),
        gifDecoder,
        targetFrameWidth,
        targetFrameHeight,
        frameTransformation,
        firstFrame)));
 }
/*---------------------------------------------------------------------------------------------*/
//glide4.0 GifFrameLoader.java
private final GifDecoder gifDecoder;// Private attribute 
 public GifFrameLoader(
   Glide glide,
   GifDecoder gifDecoder,
   int width,
   int height,
   Transformation<Bitmap> transformation,
   Bitmap firstFrame) {
  this(
    glide.getBitmapPool(),
    Glide.with(glide.getContext()),
    gifDecoder,
    null /*handler*/,
    getRequestBuilder(Glide.with(glide.getContext()), width, height),
    transformation,
    firstFrame);
 }

 @SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
 GifFrameLoader(
   BitmapPool bitmapPool,
   RequestManager requestManager,
   GifDecoder gifDecoder,
   Handler handler,
   RequestBuilder<Bitmap> requestBuilder,
   Transformation<Bitmap> transformation,
   Bitmap firstFrame) {
  this.requestManager = requestManager;
  if (handler == null) {
   handler = new Handler(Looper.getMainLooper(), new FrameLoaderCallback());
  }
  this.bitmapPool = bitmapPool;
  this.handler = handler;
  this.requestBuilder = requestBuilder;

  // Look here 
  this.gifDecoder = gifDecoder;

  setFrameTransformation(transformation, firstFrame);
 }

(2) Get decoder

From the GifDrawable construction of Glide4.0 above, we can see that gifDecoder is passed to GifFrameLoader and assigned to private attribute gifDecoder; And GifFrameLoader is passed into GifState and assigned to its member variable frameLoader, how do you get frameLoader?

From the source code, we can see that GifDrawable provides getConstantState () method to get state variable (the type of this variable is GifState), but GifState does not have get method to get frameLoader, gifDecoder in frameLoader object is also private, and there is no get method to get, so we can only get it through reflection.


// Source code 
//glide4.0 GifDrawable.java
private final GifState state;
 @Override
 public ConstantState getConstantState() {
  return state;
 }
 static class GifState extends ConstantState {
  static final int GRAVITY = Gravity.FILL;
  final BitmapPool bitmapPool;
  final GifFrameLoader frameLoader;

  public GifState(BitmapPool bitmapPool, GifFrameLoader frameLoader) {
   this.bitmapPool = bitmapPool;
   this.frameLoader = frameLoader;
  }

  @Override
  public Drawable newDrawable(Resources res) {
   return newDrawable();
  }

  @Override
  public Drawable newDrawable() {
   return new GifDrawable(this);
  }

  @Override
  public int getChangingConfigurations() {
   return 0;
  }
 }

Acquiring decoder by reflection


.listener(new RequestListener<GifDrawable>() {
          @Override
          public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<GifDrawable> target, boolean isFirstResource) {
            return false;
          }

          @Override
          public boolean onResourceReady(GifDrawable gifDrawable, Object model, Target<GifDrawable> target, DataSource dataSource, boolean isFirstResource) {
            try {
              int duration = 0;
              //  Calculate animation duration 
              //GifDecoder decoder = gifDrawable.getDecoder();//4.0 There is no such method at first 
              Drawable.ConstantState state = gifDrawable.getConstantState();
              if(state!=null){
                // Can't be confused GifFrameLoader And GifState Class 
                Object gifFrameLoader = getValue(state,"frameLoader");
                if(gifFrameLoader!=null){
                  Object decoder = getValue(gifFrameLoader,"gifDecoder");
                  if(decoder!=null && decoder instanceof GifDecoder){
                    for (int i = 0; i < gifDrawable.getFrameCount(); i++) {
                      duration += ((GifDecoder) decoder).getDelay(i);
                    }
                  }
                }
                Log.e("Glide4.0","gif Duration of playing animation duration:"+duration);
              }
            } catch (Throwable e) {
            }
            return false;
          }
        })

/*---------------------------------------------------------------------------------------------*/
/**
   *  Get the value of the field from the object or the parent class of the object by the field name 
   * @param object  Object instance 
   * @param fieldName  Field name 
   * @return  The value corresponding to the field 
   * @throws Exception
   */
  public static Object getValue(Object object, String fieldName) throws Exception {
    if (object == null) {
      return null;
    }
    if (TextUtils.isEmpty(fieldName)) {
      return null;
    }
    Field field = null;
    Class<?> clazz = object.getClass();
    for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
      try {
        field = clazz.getDeclaredField(fieldName);
        field.setAccessible(true);
        return field.get(object);
      } catch (Exception e) {
        // Don't do anything here! And the exception here must be written like this and cannot be thrown. 
        // If the exception here is printed or thrown out, it will not be executed clazz = clazz.getSuperclass(), You won't end up in the parent class 
      }
    }

    return null;
  }

(3) Set the number of gif loops

There is no GlideDrawableImageViewTarget class in glide 4.0, so how do you set the number of loops?

From the source code of glide3.7. 0, it can be found that GlideDrawableImageViewTarget is set by setLoopCount method of GlideDrawable, and the specific implementation place of setLoopCount is in GifDrawable, so what is called here is actually setLoopCount method of GifDrawable. There are no GlideDrawable classes and GlideDrawableImageViewTarget classes in glide 4.0, but there are still GifDrawable classes, and the first parameter in onResourceReady method is GifDrawable, so you can directly call setLoopCount (loopCount) of GifDrawable to set the playback times.


compile 'com.android.support:support-v4:25.3.1'
compile 'com.github.bumptech.glide:glide:4.0.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.0.0'
0

glide4.0 Calculate gif1 playback duration code:


compile 'com.android.support:support-v4:25.3.1'
compile 'com.github.bumptech.glide:glide:4.0.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.0.0'
1

Note: The GifFrameLoader and GifState classes cannot be confused because reflection is used to obtain decoder

5. Set fade-in and fade-out animation

glide3.7.0


compile 'com.android.support:support-v4:25.3.1'
compile 'com.github.bumptech.glide:glide:4.0.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.0.0'
2

glide4.0


GlideApp.with(context)
        .load(url)
        .transition(DrawableTransitionOptions.withCrossFade(100))// Fade in and fade out 100m
        .placeholder(R.drawable.placeholder)
        .fallback(R.drawable.fallback) 
        .diskCacheStrategy(DiskCacheStrategy.ALL)
        .into(imageView);

6. Caching strategy

glide3.7.0


compile 'com.android.support:support-v4:25.3.1'
compile 'com.github.bumptech.glide:glide:4.0.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.0.0'
4

glide4.0


compile 'com.android.support:support-v4:25.3.1'
compile 'com.github.bumptech.glide:glide:4.0.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.0.0'
5

7. Placeholder, wrong picture setting

glide4.0 If target is set in into, placeholders (placeholder, error) need to be set again in the callback, otherwise it is invalid.


compile 'com.android.support:support-v4:25.3.1'
compile 'com.github.bumptech.glide:glide:4.0.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.0.0'
6

Related articles: