美文网首页
Glide最新源码解析(四)- 解码和转码流程

Glide最新源码解析(四)- 解码和转码流程

作者: 烧伤的火柴 | 来源:发表于2019-10-08 11:26 被阅读0次

    介绍

    从上一篇文章我们知道了如何从Model得到Data。拿到Data数据后还要经过解码和转码才能得到我们app上需要的资源,这篇文章分析下解码,转换 ,转码的工作流程。
    先说明一下这几个过程的作用:

    • 解码过程就是通过Data(InputStream等)得到Resource(bitmap等)。
    • 转换过程就是将Bitmap经过ImageView 的ScaleTyp转为特定的样式。
    • 转码过程就是把BitmapResource转换为高层模块需要的DrawableResource。

    准备工作

    首先需要根据传入的Data(InputStream) Resource(Object) 和transcodeResource(Drawable)找到后边解码和转码需要的资源和解码器和转码器
    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();
          }
        }
      }
     /**从接收到的数据解码*/
      private void decodeFromRetrievedData() {
        Resource<R> resource = null;
         ...
        resource = decodeFromData(currentFetcher, currentData, currentDataSource);//1解码
         ...
        if (resource != null) {
          notifyEncodeAndRelease(resource, currentDataSource);//1 通知编码和释放资源
        } else {
          runGenerators();
        }
      }
      /**使用DataFetcher 根据Data解码资源*/
      private <Data> Resource<R> decodeFromData(
          DataFetcher<?> fetcher, Data data, DataSource dataSource) throws GlideException {
        try {
          ...
          Resource<R> result = decodeFromFetcher(data, dataSource);
          ...
          return result;
        } finally {
          fetcher.cleanup();
        }
      }
    
      private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
          throws GlideException {
        LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());//3查找LoadPath
        return runLoadPath(data, dataSource, path);
      }
      
      private <Data, ResourceType> Resource<R> runLoadPath(
          Data data, DataSource dataSource, LoadPath<Data, ResourceType, R> path)
          throws GlideException {
        Options options = getOptionsWithHardwareConfig(dataSource);
        DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);//1
       ....
        return path.load(
             rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));//2
      ....
      }
    

    我们看一下注释3处的查找LoadPath,这个在前边的SourceGenerator中startNext()方法内helper.hasLoadPath(loadData.fetcher.getDataClass()))的一样的,所以放到这里分析。DecodeHelper.java

     <Data> LoadPath<Data, ?, Transcode> getLoadPath(Class<Data> dataClass) {//Inputstream.class
        return glideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass);
      }
    

    根据dataClass, resourceClass, transcodeClass从注册表中查找所有需要的资源

    Registry#getLoadPath

      @Nullable
      public <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode> getLoadPath(
          @NonNull Class<Data> dataClass,//InputStream.class
          @NonNull Class<TResource> resourceClass,//Object.class
          @NonNull Class<Transcode> transcodeClass) {//Drawable.class
        LoadPath<Data, TResource, Transcode> result =
            loadPathCache.get(dataClass, resourceClass, transcodeClass);
        ...
          List<DecodePath<Data, TResource, Transcode>> decodePaths =
              getDecodePaths(dataClass, resourceClass, transcodeClass);
            ...
            result =
                new LoadPath<>(
                    dataClass, resourceClass, transcodeClass, decodePaths, throwableListPool);
          loadPathCache.put(dataClass, resourceClass, transcodeClass, result);
        }
        return result;
      }
    @NonNull
      private <Data, TResource, Transcode> List<DecodePath<Data, TResource, Transcode>> getDecodePaths(
          @NonNull Class<Data> dataClass,//inputstream
          @NonNull Class<TResource> resourceClass,//object
          @NonNull Class<Transcode> transcodeClass) {//Darwable
        List<DecodePath<Data, TResource, Transcode>> decodePaths = new ArrayList<>();
        List<Class<TResource>> registeredResourceClasses =
            decoderRegistry.getResourceClasses(dataClass, resourceClass);//1
    
        for (Class<TResource> registeredResourceClass : registeredResourceClasses) {
          List<Class<Transcode>> registeredTranscodeClasses =
              transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass);//2
    
          for (Class<Transcode> registeredTranscodeClass : registeredTranscodeClasses) {
    
            List<ResourceDecoder<Data, TResource>> decoders =
                decoderRegistry.getDecoders(dataClass, registeredResourceClass);//3
    
            ResourceTranscoder<TResource, Transcode> transcoder =
                transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);//4
            @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
            DecodePath<Data, TResource, Transcode> path =
                new DecodePath<>(
                    dataClass,
                    registeredResourceClass,
                    registeredTranscodeClass,
                    decoders,
                    transcoder,
                    throwableListPool);//5
            decodePaths.add(path);
          }
        }
        return decodePaths;
      }
    

    getDecodePaths方法根据参数从注册表中查询到所有符合条件的DecodePath集合。这里的参数分别是dataClass=InputStream.class resourceClass = Object.class transcodeClass = Drawable.class
    注释1处decoderRegistry根据dataClass 和resourceClass 找到所有的资源类.如下是相关代码ResourceDecoderRegistry.java

    ...
      @NonNull
      @SuppressWarnings("unchecked")
      public synchronized <T, R> List<Class<R>> getResourceClasses(
          @NonNull Class<T> dataClass, @NonNull Class<R> resourceClass) {
        List<Class<R>> result = new ArrayList<>();
        for (String bucket : bucketPriorityList) {
          List<Entry<?, ?>> entries = decoders.get(bucket);
          if (entries == null) {
            continue;
          }
          for (Entry<?, ?> entry : entries) {
            if (entry.handles(dataClass, resourceClass)
                && !result.contains((Class<R>) entry.resourceClass)) {//去重
              result.add((Class<R>) entry.resourceClass);
            }
          }
        }
        return result;
      }
    ...
    private static class Entry<T, R> {
      public boolean handles(@NonNull Class<?> dataClass, @NonNull Class<?> resourceClass) {
            return this.dataClass.isAssignableFrom(dataClass)
                && resourceClass.isAssignableFrom(this.resourceClass);//resourceClass是this.resourceClass基类或  者相同
        }  
    }
    

    这里的resourceClass 是Object.class,是所有类的基类,所以注册表中的所有类型都符合条件,由于前边去重的作用,所以registeredResourceClasses 集合中是GifDrawable.class Bitmap.class BitmapDrawable.class.
    Registry#getDecodePaths方法的注释2处根据注册的资源类和要求的转码类(Drawable.class)
    TranscoderRegistry#getTranscodeClasses方法

      @NonNull
      public synchronized <Z, R> List<Class<R>> getTranscodeClasses(
          @NonNull Class<Z> resourceClass, @NonNull Class<R> transcodeClass) {
        List<Class<R>> transcodeClasses = new ArrayList<>();
        // GifDrawable -> Drawable is just the UnitTranscoder, as is GifDrawable -> GifDrawable.
        if (transcodeClass.isAssignableFrom(resourceClass)) {//1
          transcodeClasses.add(transcodeClass);
          return transcodeClasses;
        }
    
        for (Entry<?, ?> entry : transcoders) {
          if (entry.handles(resourceClass, transcodeClass)) {//2
            transcodeClasses.add(transcodeClass);//3
          }
        }
    
        return transcodeClasses;
      }
    

    第一次GifDrawable.class 是 Drawable.class的子类,所以执行注释1,直接返回transcodeClasses 集合只有Drawable.class
    第二次执行注释2,根据resourceClass, transcodeClass从注册表中查找transcodeClass,Glide中的注册表中条件满足的是

    .register(Bitmap.class, BitmapDrawable.class, new BitmapDrawableTranscoder(resources))
    

    注意注释3处返回的是传进来的Drawable.class,所以第二次的集合只有一个是entry.resourceClass 即 Drawable.class

    第三次BitmapDrawable.class.是Drawable.class的子类所以执行注释1,直接返回transcodeClasses 集合只有Drawable.class
    接着是Registry#getDecodePaths方法的注释3的地方根据dataClass和注册的资源类从解码表中找到解码器集合。ResourceDecoderRegistry的代码如下:

      /**
       * 得到资源解码器
       * @param dataClass 数据类(byteBuffer,stream等)
       * @param resourceClass 资源类(bitmap,drawable等)
       * @param <T> T
       * @param <R> R
       * @return 解码器集合
       */
      @NonNull
      @SuppressWarnings("unchecked")
      public synchronized <T, R> List<ResourceDecoder<T, R>> getDecoders(
          @NonNull Class<T> dataClass, @NonNull Class<R> resourceClass) {
        List<ResourceDecoder<T, R>> result = new ArrayList<>();
        for (String bucket : bucketPriorityList) {
           ...
          for (Entry<?, ?> entry : entries) {
            if (entry.handles(dataClass, resourceClass)) {
              result.add((ResourceDecoder<T, R>) entry.decoder);
            }
          }
        }
    
        return result;
      }
    

    这里从Glide注册的资源解码器中查找符合条件的,结果如下:
    第一次:Inputstream.class->GifDrawable.class 解码器是StreamGifDecoder对象
    第二次:Inputstream.class->Bitmap.class 解码器是streamBitmapDecoder对象
    第三次:Inputstream.class->BitmapDrawable.class 解码器是BitmapDrawableDecoder对象
    Registry#getDecodePaths方法的注释4处根据注册的资源类和注册的解码类通过注册表查找资源转码器
    ResourceTranscoder。TranscoderRegistry#get

      @NonNull
      @SuppressWarnings("unchecked")
      public synchronized <Z, R> ResourceTranscoder<Z, R> get(
          @NonNull Class<Z> resourceClass, @NonNull Class<R> transcodedClass) {
        if (transcodedClass.isAssignableFrom(resourceClass)) {//1 gifDrawable-Drawable
          return (ResourceTranscoder<Z, R>) UnitTranscoder.get();
        }
        for (Entry<?, ?> entry : transcoders) {
          if (entry.handles(resourceClass, transcodedClass)) {//2
            return (ResourceTranscoder<Z, R>) entry.transcoder;
          }
        }
    
        throw new IllegalArgumentException(
            "No transcoder registered to transcode from " + resourceClass + " to " + transcodedClass);
      }
    

    for循环结果是:
    第一次:GifDrawable->Drawable 满足注释1 得到是UnitTranscoder
    第二次:Bitmap->Drawable 满足注释2 从注册表中找到的是BitmapDrawableTranscoder
    第三次:BitmapDrawable->Drawable 满足注释1 得到是UnitTranscoder

    getDecodePaths方法的注释5根据前边查询的参数组装DecodePath对象并且放入集合decodePaths中
    所以总结一下最后的到的结果,伪代码是:

    List<DecodePath<Data, TResource, Transcode>> decodePaths = new ArrayList<>();
    decodePaths .add(new DecodePath<>(
    InputStream.class,
    GifDrawable.class,
    Drawable.class,
    StreamGifDecoder.class,
    UnitTranscoder.class,
    throwableListPool);
    
    decodePaths .add(new DecodePath<>(
    InputStream.class,
    Bitmap.class,
    Drawable.class,
    StreamBitmapDecoder.class,
    BitmapDrawableTranscoder.class,
    throwableListPool);
    
    decodePaths .add(new DecodePath<>(
    InputStream.class,
    BitmapDrawable.class,
    Drawable.class,
    BitmapDrawableDecoder.class,
    UnitTranscoder.class,
    throwableListPool);
    
    

    执行解码和编码

    回到DecodeJob的runLoadPath注释1处查找DataRewinder 得到是InputStreamRewinder对象。然后是主要的load工作,即注释2处会执行LoadPath#loadWithExceptionList

     private Resource<Transcode> loadWithExceptionList(
          DataRewinder<Data> rewinder,
          @NonNull Options options,
          int width,
          int height,
          DecodePath.DecodeCallback<ResourceType> decodeCallback,
          List<Throwable> exceptions)
          throws GlideException {
        Resource<Transcode> result = null;
        //noinspection ForLoopReplaceableByForEach to improve perf
        for (int i = 0, size = decodePaths.size(); i < size; i++) {
          DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
          try {
            result = path.decode(rewinder, width, height, options, decodeCallback);//1
          } catch (GlideException e) {
            exceptions.add(e);
          }
          if (result != null) {//2
            break;
          }
        }
    
        if (result == null) {
          throw new GlideException(failureMessage, new ArrayList<>(exceptions));
        }
    
        return result;
      }
    

    DecodePath#decode

      public Resource<Transcode> decode(
          DataRewinder<DataType> rewinder,
          int width,
          int height,
          @NonNull Options options,
          DecodeCallback<ResourceType> callback)
          throws GlideException {
        //解码资源
        Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
        //2 转换资源(CENTER_CROP等)
        Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
        //3.转码(Bitmap=BitmapDrawable)
        return transcoder.transcode(transformed, options);
      }
    

    解码资源的代码是:

     @NonNull
      private Resource<ResourceType> decodeResource(
          DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options)
          throws GlideException {
        List<Throwable> exceptions = Preconditions.checkNotNull(listPool.acquire());
        try {
          return decodeResourceWithList(rewinder, width, height, options, exceptions);
        } finally {
          listPool.release(exceptions);
        }
      }
    
      @NonNull
      private Resource<ResourceType> decodeResourceWithList(
          DataRewinder<DataType> rewinder,
          int width,
          int height,
          @NonNull Options options,
          List<Throwable> exceptions)
          throws GlideException {
        Resource<ResourceType> result = null;
        //noinspection ForLoopReplaceableByForEach to improve perf
        for (int i = 0, size = decoders.size(); i < size; i++) {
          ResourceDecoder<DataType, ResourceType> 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) {//3
            break;
          }
        }
    
        if (result == null) {
          throw new GlideException(failureMessage, new ArrayList<>(exceptions));
        }
        return result;
      }
    

    根据前边的分析decoders第一个是StreamGifDecoder,它的decode得到的是null。
    decoders第二个是StreamBitmapDecoder,它的decode的到result 是Bimap,即result!=null,所以注释3处结束循环,返回result。

    转换 裁剪缩放成需要的样式的资源

    DecodePath#decode的注释2处会回调decodeJob中的onResourceDecoded,方法内会对资源进行转换,并且把转换后的资源写入磁盘缓存中。

    解码

    DecodePath#decode的注释2处会将bitmap转为BitmapDrawable 资源类型
    转码器BitmapDrawableTranscoder#transcode方法

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

    LazyBitmapDrawableResource.java

      @Nullable
      public static Resource<BitmapDrawable> obtain(
          @NonNull Resources resources, @Nullable Resource<Bitmap> bitmapResource) {
        if (bitmapResource == null) {
          return null;
        }
        return new LazyBitmapDrawableResource(resources, bitmapResource);
      }
    
      private LazyBitmapDrawableResource(
          @NonNull Resources resources, @NonNull Resource<Bitmap> bitmapResource) {
        this.resources = Preconditions.checkNotNull(resources);
        this.bitmapResource = Preconditions.checkNotNull(bitmapResource);
      }
    

    最后通过转码得到的是LazyBitmapDrawableResource 资源类。加载的结果怎么处理,上一篇已经讲过了。

    相关文章

      网友评论

          本文标题:Glide最新源码解析(四)- 解码和转码流程

          本文链接:https://www.haomeiwen.com/subject/xfvructx.html