Glide的Gif加载

125 阅读1分钟

通过简单画的几张图来解释Glide中对于Gif的解析:

在加载到Gif相关的Stream流后,会回调到DecodeJob中,然后经过LoadPathDecodePath,具体的方法执行如下面的时序图。

DecodeJob到DecodePath.png 在对应方法的回调过程中会根据返回的Stream来获取对应的DecodePath和其中保存的ResourceDecoderResourceDecoder表示Stream对应的解码器,会通过调用对应的handle方法来判断是否能够处理数据,然后在调用decode方法实现解码。

Gif首次解码时会执行到ByteBufferGifDecoder中,简单看下源码:

@Override
public GifDrawableResource decode(
    @NonNull ByteBuffer source, int width, int height, @NonNull Options options) {
  // 这里的GifHeaderParser用于解码Gif相关的信息,比如header、帧数据、帧数等
  final GifHeaderParser parser = parserPool.obtain(source);
  try {
    return decode(source, width, height, parser, options);
  } finally {
    parserPool.release(parser);
  }
}

@Nullable
private GifDrawableResource decode(
    ByteBuffer byteBuffer, int width, int height, GifHeaderParser parser, Options options) {
  long startTime = LogTime.getLogTime();
  try {
    final GifHeader header = parser.parseHeader();
    if (header.getNumFrames() <= 0 || header.getStatus() != GifDecoder.STATUS_OK) {
      // If we couldn't decode the GIF, we will end up with a frame count of 0.
      return null;
    }

    Bitmap.Config config =
        options.get(GifOptions.DECODE_FORMAT) == DecodeFormat.PREFER_RGB_565
            ? Bitmap.Config.RGB_565
            : Bitmap.Config.ARGB_8888;

    int sampleSize = getSampleSize(header, width, height);
    // 创建一个StandardGifDecoder用于读取GIF源中的帧数据
    GifDecoder gifDecoder = gifDecoderFactory.build(provider, header, byteBuffer, sampleSize);
    gifDecoder.setDefaultBitmapConfig(config);
    // advance表示跳到下一帧的index
    gifDecoder.advance();
    // getNextFrame返回下一帧数据
    Bitmap firstFrame = gifDecoder.getNextFrame();
    if (firstFrame == null) {
      return null;
    }

    Transformation<Bitmap> unitTransformation = UnitTransformation.get();
    // 继承自Drawable,获取到下一帧数据后,会通过刷新View执行到Drawable的draw方法中,通过gifDecoder获取帧数据并绘制
    GifDrawable gifDrawable =
        new GifDrawable(context, gifDecoder, unitTransformation, width, height, firstFrame);
    // 返回Resource,回调给上层,最终会返回到ImageViewTarget中
    return new GifDrawableResource(gifDrawable);
  } finally {
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      Log.v(TAG, "Decoded GIF from stream in " + LogTime.getElapsedMillis(startTime));
    }
  }
}

StandardGifDecoder解码GIF源数据并保存对应的index,来维持动画的目的。在GifDrawable中会创建GifFrameLoader,GifFrameLoader负责循环的加载下一帧数据,然后回调到GifDrawable中刷新Drawable,具体的流程图如下所示:

Glide中Gif循环加载时序图.png

在GifFrameLoader中加载下一帧时,会创建一个完整的请求流程来加载数据,和普通的请求区别在于,Gif的下一帧请求会将StandardGifDecoder作为参数传递给下一次请求的整个流程,用来获取对应的解码器GifFrameResourceDecoder,具体的流程如下所示:

Gif下一帧的流程图.png