Glide源码分析之编解码

今天整篇就围绕一个数据后获取阶段,所谓后获取指的是数据从网络请求成功回本地后到转换成所需的数据类型过程,总结出来就是两个问题:

  • Glide的数据后获取阶段的流程?
  • Glide的编解码阶段流程?

1.数据后获取阶段流程

里面提到过,网络下载数据是在SourceGenerator中,下载成功后会把数据存在本地再从本地读取,再回忆下这个过程。刚开始run1和run2dataToCache和sourceCacheGenerator为空,会走run3获取数据,成功请求后回调run4,给dataToCache赋值,因为状态没变,所以DecodeJob还是会调用SourceGenerator,现在会走run1,接着往后走。

// SourceGenerator.java
  @Override
  public boolean startNext() {
    // run 1
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      cacheData(data);
    }

    // run 2
    if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
      return true;
    }
    sourceCacheGenerator = null;

    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      loadData = helper.getLoadData().get(loadDataListIndex++);
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
          || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        // run 3  
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }

  @Override
  public void onDataReady(Object data) {
    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
    if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
      dataToCache = data;
      // run 4
      cb.reschedule();
    } else {
      cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
          loadData.fetcher.getDataSource(), originalKey);
    }
  }

run1里面会调用cacheData, 会从DecodeHelper中获取一个编码器,把数据通过编码器写到本地文件中,文件缓存通过DiskCache管理,然后会给sourceCacheGenerator赋值,它其实是一个DataCacheGenerator类型,负责从本地加载数据。接着上面就走到run2, 从本地加载数据然后返回true.

  private void cacheData(Object dataToCache) {
    long startTime = LogTime.getLogTime();
    try {
      Encoder encoder = helper.getSourceEncoder(dataToCache);
      DataCacheWriter writer =
          new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
      originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
      helper.getDiskCache().put(originalKey, writer);
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Finished encoding source to cache"
            + ", key: " + originalKey
            + ", data: " + dataToCache
            + ", encoder: " + encoder
            + ", duration: " + LogTime.getElapsedMillis(startTime));
      }
    } finally {
      loadData.fetcher.cleanup();
    }

    sourceCacheGenerator =
        new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
  }
 
 

很明显,接下来执行到DataCacheGenerator中的startNext方法,在run1中根据File这种类型从注册的ModelRegistry中取出对应的ModelLoader,取出来的可能会有多个,然后循环遍历这些ModelLoader,在run2中会构造出DataFetcher,run3判断拿到的DataFetcher转换后的数据类型是否有对应的Decoder,如果有就用这个DataFetcher加载从缓存拿到的cacheFile文件。

// DataCacheGenerator.java    
while (modelLoaders == null || !hasNextModelLoader()) {
      sourceIdIndex++;
      if (sourceIdIndex >= cacheKeys.size()) {
        return false;
      }

      Key sourceId = cacheKeys.get(sourceIdIndex);
      @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
      Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
      cacheFile = helper.getDiskCache().get(originalKey);
      if (cacheFile != null) {
        this.sourceKey = sourceId;
        // run 1
        modelLoaders = helper.getModelLoaders(cacheFile);
        modelLoaderIndex = 0;
      }
    }

    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      ModelLoader modelLoader = modelLoaders.get(modelLoaderIndex++);
      // run 2
      loadData =
          modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(),
              helper.getOptions());
      // run 3
      if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
        started = true;
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }

看下对应的File,会取到哪些ModelLoader,会取到有四个,分别是ByteBufferFileLoader,FileLoader,UnitModelLoader,其中两个FileLoader分别对应InputStream,FileDescriptor

AfterDataFetcher.PNG

上面的循环会先取到第一个ByteBufferFileLoader,然后用ByteBufferFileFetcher去加载数据,看下这个类,它是ByteBufferFileLoader的内部类, 它的DataSourceDataSource.LOCAL,表示从数据来源是本地, 取到的数据类型是ByteBuffer.class, 在loadData中通过NIO内存映射读入文件。

// ByteBufferFileLoader.java
  private static final class ByteBufferFetcher implements DataFetcher {

    private final File file;

    @Synthetic
    @SuppressWarnings("WeakerAccess")
    ByteBufferFetcher(File file) {
      this.file = file;
    }

    @Override
    public void loadData(@NonNull Priority priority,
        @NonNull DataCallback callback) {
      ByteBuffer result;
      try {
        result = ByteBufferUtil.fromFile(file);
      } catch (IOException e) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
          Log.d(TAG, "Failed to obtain ByteBuffer for file", e);
        }
        callback.onLoadFailed(e);
        return;
      }

      callback.onDataReady(result);
    }
    ...

    @NonNull
    @Override
    public Class getDataClass() {
      return ByteBuffer.class;
    }

    @NonNull
    @Override
    public DataSource getDataSource() {
      return DataSource.LOCAL;
    }
  }

文件成功映射后回调到DataCacheGenerator, 在这里是简单的调用回调函数,到这里数据来源变成了DataSource.DATA_DISK_CACHE,意思是已经做了映射,但是未做修改的原始数据。这里的cb是在SourceGenerator中传过来的。

// DataCacheGenerator.java  
@Override
  public void onDataReady(Object data) {
    cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
  }

SourceGenerator接口中只是简单的再调用它上一级传给它的cb,也就是DecodeJob, 到了这里就是赋值一个关键变量,data就是上面的ByteBufferFileFetcher取到的ByteBuffer数据,然后会调用decodeFromRetrievedData进行数据解码。

// DecodeJob.java
  @Override
  public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher fetcher,
      DataSource dataSource, Key attemptedKey) {
    this.currentSourceKey = sourceKey;
    this.currentData = data;
    this.currentFetcher = fetcher;
    this.currentDataSource = dataSource;
    this.currentAttemptingKey = attemptedKey;
    if (Thread.currentThread() != currentThread) {
      runReason = RunReason.DECODE_DATA;
      callback.reschedule(this);
    } else {
      GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
      try {
        decodeFromRetrievedData();
      } finally {
        GlideTrace.endSection();
      }
    }
  }

decodeFromRetrievedData中会一路调用,来到decodeFromFetcher, 会根据Data的类型ByteBufferDecodeHelper中获取LoadPath,然后runLoadPath

// DecodeJob.java  
private  Resource decodeFromFetcher(Data data, DataSource dataSource)
      throws GlideException {
    LoadPath path = decodeHelper.getLoadPath((Class) data.getClass());
    return runLoadPath(data, dataSource, path);
}

private  Resource runLoadPath(Data data, DataSource dataSource,
      LoadPath path) throws GlideException {
    Options options = getOptionsWithHardwareConfig(dataSource);
    DataRewinder rewinder = glideContext.getRegistry().getRewinder(data);
    try {
      // ResourceType in DecodeCallback below is required for compilation to work with gradle.
      return path.load(
          rewinder, options, width, height, new DecodeCallback(dataSource));
    } finally {
      rewinder.cleanup();
    }
}

后面经过解码和转码得到了数据,会notifyEncodeAndRelease通知到上层,调用到notifyComplete回调到EngineJob:

// DecodeJob.java
  private void decodeFromRetrievedData() {
    ...
    Resource resource = null;
    try {
      resource = decodeFromData(currentFetcher, currentData, currentDataSource);
    } catch (GlideException e) {
      e.setLoggingDetails(currentAttemptingKey, currentDataSource);
      throwables.add(e);
    }
    if (resource != null) {
      notifyEncodeAndRelease(resource, currentDataSource);
    } else {
      runGenerators();
    }
  }

  private void notifyEncodeAndRelease(Resource resource, DataSource dataSource) {
    if (resource instanceof Initializable) {
      ((Initializable) resource).initialize();
    }
    ...
    notifyComplete(result, dataSource);
    ...
  }

  private void notifyComplete(Resource resource, DataSource dataSource) {
    setNotifiedOrThrow();
    callback.onResourceReady(resource, dataSource);
  }

EngineJob中,会给主线程发送MSG_COMPLETE消息,回调到主线程执行handleResultOnMainThread方法,在run 1中会回调onEngineJobComplete,其中listener是Engine。接着在run2中通知监听者Request,在Request里面调用TargetonResourceReady

// EngineJob.java
  @Override
  public void onResourceReady(Resource resource, DataSource dataSource) {
    this.resource = resource;
    this.dataSource = dataSource;
    MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
  }

    public boolean handleMessage(Message message) {
      EngineJob job = (EngineJob) message.obj;
      switch (message.what) {
        case MSG_COMPLETE:
          job.handleResultOnMainThread();
          break;
        ...
        default:
          throw new IllegalStateException("Unrecognized message: " + message.what);
      }
      return true;
    }

  void handleResultOnMainThread() {
    ...
    engineResource.acquire();
    // run 1
    listener.onEngineJobComplete(this, key, engineResource);

    //noinspection ForLoopReplaceableByForEach to improve perf
    for (int i = 0, size = cbs.size(); i < size; i++) {
      ResourceCallback cb = cbs.get(i);
      if (!isInIgnoredCallbacks(cb)) {
        engineResource.acquire();
        // run 2
        cb.onResourceReady(engineResource, dataSource);
      }
    }
    // Our request is complete, so we can release the resource.
    engineResource.release();

    release(false /*isRemovedFromQueue*/);
  }

上面调到的onEngineJobComplete是在Engine中,这里会先把资源放到active resources这一层内存缓存中。注册一个resourcelistener,在资源释放的时候回调,把资源从active resource中移除,挪到cache这一层的内存缓存中:

// Engine
  public void onEngineJobComplete(EngineJob engineJob, Key key, EngineResource resource) {
    Util.assertMainThread();
    if (resource != null) {
      resource.setResourceListener(key, this);

      if (resource.isCacheable()) {
        activeResources.activate(key, resource);
      }
    }

    jobs.removeIfCurrent(key, engineJob);
  }

  public void onResourceReleased(Key cacheKey, EngineResource resource) {
    Util.assertMainThread();
    activeResources.deactivate(cacheKey);
    if (resource.isCacheable()) {
      cache.put(cacheKey, resource);
    } else {
      resourceRecycler.recycle(resource);
    }
  }

2.LoadPath

所以看下LoadPath的源码,它负责把获取到的ByteBuffer放到一个个的DecodePath中进行解码转换,有三个泛型,其中Data类型是获取到的数据类型(比如ByteBuffer, InputStream), ResourceType是中间类型(可能有Gif/Bitmap),Transcode就是最后返回给应用层的最终类型(可能有Drawable)等

// LoadPath.java
/**
 * For a given {@link com.bumptech.glide.load.data.DataFetcher} for a given data class, attempts to
 * fetch the data and then run it through one or more
 * {@link com.bumptech.glide.load.engine.DecodePath}s.
 *
 * @param          The type of data that will be fetched.
 * @param  The type of intermediate resource that will be decoded within one of the
 *                       {@link com.bumptech.glide.load.engine.DecodePath}s.
 * @param     The type of resource that will be returned as the result if the load and
 *                       one of the decode paths succeeds.
 */
public class LoadPath {
  private final Class dataClass;
  private final Pool> listPool;
  private final List> decodePaths;
  private final String failureMessage;
  ...
}

知道了什么是LoadPath后再返回到前面的decodeFromFetcher中,看下LoadPath是怎么拿到的,先从缓存取如果没有会从新构造,构造需要有一个参数DecodePath,会从decoderRegistry和transcoderRegistry分别获取ResourceDecoder和ResourceTranscoder,分别用于解码和转码。

// DecodeHelper.java
   LoadPath getLoadPath(Class dataClass) {
    return glideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass);
  }
// Registry.java
  public  LoadPath getLoadPath(
      @NonNull Class dataClass, @NonNull Class resourceClass,
      @NonNull Class transcodeClass) {
    LoadPath result =
        loadPathCache.get(dataClass, resourceClass, transcodeClass);
    if (loadPathCache.isEmptyLoadPath(result)) {
      return null;
    } else if (result == null) {
      List> decodePaths =
          getDecodePaths(dataClass, resourceClass, transcodeClass);
      if (decodePaths.isEmpty()) {
        result = null;
      } else {
        result =
            new LoadPath<>(
                dataClass, resourceClass, transcodeClass, decodePaths, throwableListPool);
      }
      loadPathCache.put(dataClass, resourceClass, transcodeClass, result);
    }
    return result;
  }

  private  List> getDecodePaths(
      @NonNull Class dataClass, @NonNull Class resourceClass,
      @NonNull Class transcodeClass) {
    List> decodePaths = new ArrayList<>();
    List> registeredResourceClasses =
        decoderRegistry.getResourceClasses(dataClass, resourceClass);

    for (Class registeredResourceClass : registeredResourceClasses) {
      List> registeredTranscodeClasses =
          transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass);

      for (Class registeredTranscodeClass : registeredTranscodeClasses) {

        List> decoders =
            decoderRegistry.getDecoders(dataClass, registeredResourceClass);
        ResourceTranscoder transcoder =
            transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);
        @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
        DecodePath path =
            new DecodePath<>(dataClass, registeredResourceClass, registeredTranscodeClass,
                decoders, transcoder, throwableListPool);
        decodePaths.add(path);
      }
    }
    return decodePaths;
  }

看下调试过程中有哪些具体的ResourceClasses和TranscodeClasses,可以看到ResourceClasses有三个

  • GifDrawable
  • Bitmap
  • BitmapDrawable

TranscodeClasses有一个Drawable,也就是最后返回给应用层的会是Drawable.

decoder.PNG

构造LoadPath需要一个很重要的参数DecodePath,接下来看看它。

3.DecodePath

再看看DecodePath的源码, 三个泛型和前面的LoadPath是一样的。解码的时候回调用decode方法,这个方法完成下面三件事:

  • 调用decodeResource完成数据的解码,得到decoded
  • 解码完成后调用回调的onResourceDecoded,在回调里面回做一些比如图片的变换,我们设置的transform就是在这里起作用
  • 对上面解码得到的decoded进行转码,返回转码成功的资源
public class DecodePath {
  private static final String TAG = "DecodePath";
  private final Class dataClass;
  private final List> decoders;
  private final ResourceTranscoder transcoder;
  private final Pool> listPool;
  private final String failureMessage;

  public DecodePath(Class dataClass, Class resourceClass,
      Class transcodeClass,
      List> decoders,
      ResourceTranscoder transcoder, Pool> listPool) {
    this.dataClass = dataClass;
    this.decoders = decoders;
    this.transcoder = transcoder;
    this.listPool = listPool;
    failureMessage = "Failed DecodePath{" + dataClass.getSimpleName() + "->"
        + resourceClass.getSimpleName() + "->" + transcodeClass.getSimpleName() + "}";
  }

  public Resource decode(DataRewinder rewinder, int width, int height,
      @NonNull Options options, DecodeCallback callback) throws GlideException {
    Resource decoded = decodeResource(rewinder, width, height, options);
    Resource transformed = callback.onResourceDecoded(decoded);
    return transcoder.transcode(transformed, options);
  }

  @NonNull
  private Resource decodeResource(DataRewinder rewinder, int width,
      int height, @NonNull Options options) throws GlideException {
    List exceptions = Preconditions.checkNotNull(listPool.acquire());
    try {
      return decodeResourceWithList(rewinder, width, height, options, exceptions);
    } finally {
      listPool.release(exceptions);
    }
  }

  @NonNull
  private Resource decodeResourceWithList(DataRewinder rewinder, int width,
      int height, @NonNull Options options, List exceptions) throws GlideException {
    Resource result = null;
    //noinspection ForLoopReplaceableByForEach to improve perf
    for (int i = 0, size = decoders.size(); i < size; i++) {
      ResourceDecoder decoder = decoders.get(i);
      try {
        DataType data = rewinder.rewindAndGet();
        if (decoder.handles(data, options)) {
          data = rewinder.rewindAndGet();
          result = decoder.decode(data, width, height, options);
        }
      if (result != null) {
        break;
      }
    }
    return result;
  }
  ...

  interface DecodeCallback {
    @NonNull
    Resource onResourceDecoded(@NonNull Resource resource);
  }
}

看一个具体的DecodePath的例子,从failureMessage中可以看到三个泛型分别是DirectByteBuffer->Bitmap->Drawable.同时资源也有设置宽高等属性。

DecodePath.PNG

再返回到前面获取LoadPath的过程中。

4.解码

看下最终根据数据类型获取到的DecodePath有三个:

  • dataClass: DirectByteBuffer, decoder: GifDecoder, transcoder: UnitTranscoder
  • dataClass: DirectByteBuffer, decoder: ByteBufferBitmapDecoder, transcoder: BitmapDrawableTranscoder
  • dataClass: DirectByteBuffer, decoder: BitmapDrawableDecoder, transcoder: UnitTranscoder
LoadPath.PNG

很明显我们这里会是第二个起作用。看下ByteBufferBitmapDecoder中的源码,通过Downsampler进行对ByteBuffer进行解码成Bitmap,Downsampler里面根据采样率、图片大小等构造一个Bitmap返回。

/**
 * Decodes {@link android.graphics.Bitmap Bitmaps} from {@link java.nio.ByteBuffer ByteBuffers}.
 */
public class ByteBufferBitmapDecoder implements ResourceDecoder {
  private final Downsampler downsampler;

  public ByteBufferBitmapDecoder(Downsampler downsampler) {
    this.downsampler = downsampler;
  }

  @Override
  public boolean handles(@NonNull ByteBuffer source, @NonNull Options options) {
    return downsampler.handles(source);
  }

  @Override
  public Resource decode(@NonNull ByteBuffer source, int width, int height,
      @NonNull Options options)
      throws IOException {
    InputStream is = ByteBufferUtil.toStream(source);
    return downsampler.decode(is, width, height, options);
  }
}

拿到Resource后会通过BitmapDrawableTranscoder进行转码,返回Resource

// BitmapDrawableTranscoder.java
/**
 * An {@link com.bumptech.glide.load.resource.transcode.ResourceTranscoder} that converts {@link
 * android.graphics.Bitmap}s into {@link android.graphics.drawable.BitmapDrawable}s.
 */
public class BitmapDrawableTranscoder implements ResourceTranscoder {
  private final Resources resources;

  // Public API.
  @SuppressWarnings("unused")
  public BitmapDrawableTranscoder(@NonNull Context context) {
    this(context.getResources());
  }

  /**
   * @deprecated Use {@link #BitmapDrawableTranscoder(Resources)}, {@code bitmapPool} is unused.
   */
  @Deprecated
  public BitmapDrawableTranscoder(
      @NonNull Resources resources, @SuppressWarnings("unused") BitmapPool bitmapPool) {
    this(resources);
  }

  public BitmapDrawableTranscoder(@NonNull Resources resources) {
    this.resources = Preconditions.checkNotNull(resources);
  }

  @Nullable
  @Override
  public Resource transcode(@NonNull Resource toTranscode,
      @NonNull Options options) {
    return LazyBitmapDrawableResource.obtain(resources, toTranscode);
  }
}

最后是通过LazyBitmapDrawableResource.obtain返回的数据, 这个可以实现懒加载,只有在真正调用get方法的时候才会分配BitmapDrawable:

// LazyBitmapDrawableResource.java
/**
 * Lazily allocates a {@link android.graphics.drawable.BitmapDrawable} from a given
 * {@link android.graphics.Bitmap} on the first call to {@link #get()}.
 */
public final class LazyBitmapDrawableResource implements Resource,
    Initializable {

  private final Resources resources;
  private final Resource bitmapResource;

  ...

  @Nullable
  public static Resource obtain(
      @NonNull Resources resources, @Nullable Resource bitmapResource) {
    if (bitmapResource == null) {
      return null;
    }
    return new LazyBitmapDrawableResource(resources, bitmapResource);

  }

  private LazyBitmapDrawableResource(@NonNull Resources resources,
      @NonNull Resource bitmapResource) {
    this.resources = Preconditions.checkNotNull(resources);
    this.bitmapResource = Preconditions.checkNotNull(bitmapResource);
  }

  @NonNull
  @Override
  public Class getResourceClass() {
    return BitmapDrawable.class;
  }

  @NonNull
  @Override
  public BitmapDrawable get() {
    return new BitmapDrawable(resources, bitmapResource.get());
  }

  @Override
  public int getSize() {
    return bitmapResource.getSize();
  }

  @Override
  public void recycle() {
    bitmapResource.recycle();
  }

  @Override
  public void initialize() {
    if (bitmapResource instanceof Initializable) {
      ((Initializable) bitmapResource).initialize();
    }
  }
}

5.总结

Glide在数据请求成功后如果能允许缓存,就会缓存完成后通过读取本地数据,然后完成解码和转码返回给应用层,也是基于门面模式,所以支持类型的转接码都会在Glide初始化的时候注册到对应的Registry中,数据下载成功后根据数据类型从Registry组装LoadPath和DecodePath,完成解码转码工作。

你可能感兴趣的:(Glide源码分析之编解码)