Implementation analysis of Glide memory cache and loading gif dynamic pictures

GlideUnderstanding memory caching and loading gifdynamic images can be DiskCacheStrategyused as a breakthrough, and DiskCacheStrategythe result is to save the result of the size transformation as an image. In this process, GIFthere are the following processes. Download GIF-> Extract Frames -> Optimize Frame Size -> Save Frames

Disk Cache Strategy

DiskCacheStrategymethod applied to each individual request. Currently supported policies allow you to prevent the loading process from using or writing to the disk cache, selectively caching only unmodified native data, or only transformed thumbnails, or both.

The default strategy is called  AUTOMATICand it will try to use the best strategy for local and remote images. When you load remote data (eg, URLdownload ), the AUTOMATIC strategy will only store raw data that has not been modified (eg: transformed, cropped) by your loading process, because downloading remote data is more expensive than adjusting data already on disk much more expensive. For local data, the AUTOMATIC strategy will only store the transformed thumbnails, because even if you need to generate another image of another size or type, it is easy to get back the original data.

To do this, specifyGIF as , and direct it to use it immediately after extracting frames from the original file . For , should be used . For other formats, if you are viewing images repeatedly or retrieving (local or remote) large images and displaying them in a small view, you can specify otherwise , if you tend to view images once, or only apply a small transformation, will tend to .DiskCacheStrategyRESOURCEGIFGIFRESOURCEDiskCacheStrategyRESOURCE

By default, Glide checks the following levels of cache before starting a new image request:

  • Active Resources - Is there another View currently showing this image?
  • Memory cache - Has the image been loaded recently and still exists in memory?
  • Resource - Has the image been decoded, transformed, and written to disk cache before?
  • Data Source ( Data ) - Has the resource that built this image been written to the file cache before? The first two steps check whether the picture is in memory, and if so, return the picture directly. The last two steps check to see if the image is on disk so that the image can be returned quickly but asynchronously.

If all four steps fail to find the image, it Glidefalls back to the original resource to retrieve the data (original file, Uri, Urletc.).

GlideThe GifDecoderalways GIFloads the complete data into memory by design. It dynamically decodes each one at a time, but the actual GIFdata is always loaded into memory.

//Shared interface for GIF decoders.
public interface GifDecoder {

  /** File read status: No errors. */
  int STATUS_OK = 0;
  /** File read status: Error decoding file (may be partially decoded). */
  int STATUS_FORMAT_ERROR = 1;
  /** File read status: Unable to open source. */
  int STATUS_OPEN_ERROR = 2;
  /** Unable to fully decode the current frame. */
  int STATUS_PARTIAL_DECODE = 3;
  /** The total iteration count which means repeat forever. */
  int TOTAL_ITERATION_COUNT_FOREVER = 0;

  /** Android Lint annotation for status codes that can be used with a GIF decoder. */
  @Retention(RetentionPolicy.SOURCE)
  @IntDef(value = {STATUS_OK, STATUS_FORMAT_ERROR, STATUS_OPEN_ERROR, STATUS_PARTIAL_DECODE})
  @interface GifDecodeStatus {
  }

  /**
   * An interface that can be used to provide reused {@link android.graphics.Bitmap}s to avoid GCs
   * from constantly allocating {@link android.graphics.Bitmap}s for every frame.
   */
  interface BitmapProvider {
    /**
     * Returns an {@link Bitmap} with exactly the given dimensions and config.
     *
     * @param width  The width in pixels of the desired {@link android.graphics.Bitmap}.
     * @param height The height in pixels of the desired {@link android.graphics.Bitmap}.
     * @param config The {@link android.graphics.Bitmap.Config} of the desired {@link
     *               android.graphics.Bitmap}.
     */
    @NonNull
    Bitmap obtain(int width, int height, @NonNull Bitmap.Config config);

    /**
     * Releases the given Bitmap back to the pool.
     */
    void release(@NonNull Bitmap bitmap);

    /**
     * Returns a byte array used for decoding and generating the frame bitmap.
     *
     * @param size the size of the byte array to obtain
     */
    @NonNull
    byte[] obtainByteArray(int size);

    /**
     * Releases the given byte array back to the pool.
     */
    void release(@NonNull byte[] bytes);

    /**
     * Returns an int array used for decoding/generating the frame bitmaps.
     */
    @NonNull
    int[] obtainIntArray(int size);

    /**
     * Release the given array back to the pool.
     */
    void release(@NonNull int[] array);
  }

  int getWidth();

  int getHeight();

  @NonNull
  ByteBuffer getData();

  /**
   * Returns the current status of the decoder.
   *
   * <p> Status will update per frame to allow the caller to tell whether or not the current frame
   * was decoded successfully and/or completely. Format and open failures persist across frames.
   * </p>
   */
  @GifDecodeStatus
  int getStatus();

  /**
   * Move the animation frame counter forward.
   */
  void advance();

  /**
   * Gets display duration for specified frame.
   *
   * @param n int index of frame.
   * @return delay in milliseconds.
   */
  int getDelay(int n);

  /**
   * Gets display duration for the upcoming frame in ms.
   */
  int getNextDelay();

  /**
   * Gets the number of frames read from file.
   *
   * @return frame count.
   */
  int getFrameCount();

  /**
   * Gets the current index of the animation frame, or -1 if animation hasn't not yet started.
   *
   * @return frame index.
   */
  int getCurrentFrameIndex();

  /**
   * Resets the frame pointer to before the 0th frame, as if we'd never used this decoder to
   * decode any frames.
   */
  void resetFrameIndex();

  /**
   * Gets the "Netscape" loop count, if any. A count of 0 means repeat indefinitely.
   *
   * @deprecated Use {@link #getNetscapeLoopCount()} instead.
   *             This method cannot distinguish whether the loop count is 1 or doesn't exist.
   * @return loop count if one was specified, else 1.
   */
  @Deprecated
  int getLoopCount();

  /**
   * Gets the "Netscape" loop count, if any.
   * A count of 0 ({@link GifHeader#NETSCAPE_LOOP_COUNT_FOREVER}) means repeat indefinitely.
   * It must not be a negative value.
   * <br>
   * Use {@link #getTotalIterationCount()}
   * to know how many times the animation sequence should be displayed.
   *
   * @return loop count if one was specified,
   *         else -1 ({@link GifHeader#NETSCAPE_LOOP_COUNT_DOES_NOT_EXIST}).
   */
  int getNetscapeLoopCount();

  /**
   * Gets the total count
   * which represents how many times the animation sequence should be displayed.
   * A count of 0 ({@link #TOTAL_ITERATION_COUNT_FOREVER}) means repeat indefinitely.
   * It must not be a negative value.
   * <p>
   *     The total count is calculated as follows by using {@link #getNetscapeLoopCount()}.
   *     This behavior is the same as most web browsers.
   *     <table border='1'>
   *         <tr class='tableSubHeadingColor'><th>{@code getNetscapeLoopCount()}</th>
   *             <th>The total count</th></tr>
   *         <tr><td>{@link GifHeader#NETSCAPE_LOOP_COUNT_FOREVER}</td>
   *             <td>{@link #TOTAL_ITERATION_COUNT_FOREVER}</td></tr>
   *         <tr><td>{@link GifHeader#NETSCAPE_LOOP_COUNT_DOES_NOT_EXIST}</td>
   *             <td>{@code 1}</td></tr>
   *         <tr><td>{@code n (n > 0)}</td>
   *             <td>{@code n + 1}</td></tr>
   *     </table>
   * </p>
   *
   * @see <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=592735#c5">Discussion about
   *      the iteration count of animated GIFs (Chromium Issue 592735)</a>
   *
   * @return total iteration count calculated from "Netscape" loop count.
   */
  int getTotalIterationCount();

  /**
   * Returns an estimated byte size for this decoder based on the data provided to {@link
   * #setData(GifHeader, byte[])}, as well as internal buffers.
   */
  int getByteSize();

  /**
   * Get the next frame in the animation sequence.
   *
   * @return Bitmap representation of frame.
   */
  @Nullable
  Bitmap getNextFrame();

  /**
   * Reads GIF image from stream.
   *
   * @param is containing GIF file.
   * @return read status code (0 = no errors).
   */
  @GifDecodeStatus
  int read(@Nullable InputStream is, int contentLength);

  void clear();

  void setData(@NonNull GifHeader header, @NonNull byte[] data);

  void setData(@NonNull GifHeader header, @NonNull ByteBuffer buffer);

  void setData(@NonNull GifHeader header, @NonNull ByteBuffer buffer, int sampleSize);

  /**
   * Reads GIF image from byte array.
   *
   * @param data containing GIF file.
   * @return read status code (0 = no errors).
   */
  @GifDecodeStatus
  int read(@Nullable byte[] data);


  /**
   * Sets the default {@link android.graphics.Bitmap.Config} to use when decoding frames of a GIF.
   *
   * <p>Valid options are {@link android.graphics.Bitmap.Config#ARGB_8888} and
   * {@link android.graphics.Bitmap.Config#RGB_565}.
   * {@link android.graphics.Bitmap.Config#ARGB_8888} will produce higher quality frames, but will
   * also use 2x the memory of {@link android.graphics.Bitmap.Config#RGB_565}.
   *
   * <p>Defaults to {@link android.graphics.Bitmap.Config#ARGB_8888}
   *
   * <p>This value is not a guarantee. For example if set to
   * {@link android.graphics.Bitmap.Config#RGB_565} and the GIF contains transparent pixels,
   * {@link android.graphics.Bitmap.Config#ARGB_8888} will be used anyway to support the
   * transparency.
   */
  void setDefaultBitmapConfig(@NonNull Bitmap.Config format);
}

复制代码

633041-96362340c3df20d6.webp

//Set of available caching strategies for media.
public abstract class DiskCacheStrategy {
  ...省略
  //Writes resources to disk after they've been decoded.
  public static final DiskCacheStrategy RESOURCE =
      new DiskCacheStrategy() {
        @Override
        public boolean isDataCacheable(DataSource dataSource) {
          return false;
        }

        @Override
        public boolean isResourceCacheable(
            boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) {
          return dataSource != DataSource.RESOURCE_DISK_CACHE
              && dataSource != DataSource.MEMORY_CACHE;
        }

        @Override
        public boolean decodeCachedResource() {
          return true;
        }

        @Override
        public boolean decodeCachedData() {
          return false;
        }
      };
}


复制代码

If you load a very large giffile , you can start zipthe file first. GifHeaderParser.parseHeader()is gifthe parser for .


/**
 * A class responsible for creating {@link com.bumptech.glide.gifdecoder.GifHeader}s from data
 * representing animated GIFs.
 *
 * @see <a href="https://www.w3.org/Graphics/GIF/spec-gif89a.txt">GIF 89a Specification</a>
 */
public class GifHeaderParser {
  ...省略
  public GifHeader parseHeader() {
    if (rawData == null) {
      throw new IllegalStateException("You must call setData() before parseHeader()");
    }
    if (err()) {
      return header;
    }

    readHeader();
    if (!err()) {
      readContents();
      if (header.frameCount < 0) {
        header.status = STATUS_FORMAT_ERROR;
      }
    }

    return header;
  }
}
复制代码
//An animated Drawable that plays the frames of an animated GIF.
class GifDrawable extends Drawable implements GifFrameLoader.FrameCallback, Animatable
, Animatable2Compat
复制代码

cache refresh

Because disk caching uses hash keys, there is no good way to simply delete all cached files for a particular urlor file path. The problem might be simpler if you only allowed to load or cache the original image, but since Glidealso caches thumbnails and provides various transforms ( transformation), any of them will cause a new file to be created in the cache, And to track and delete all versions of a picture is undoubtedly difficult.

在实践中,使缓存文件无效的最佳方式是在内容发生变化时(urluri,文件路径等)更改你的标识符。

Guess you like

Origin juejin.im/post/7119412495202123790