美文网首页
glide源码解析

glide源码解析

作者: 刘佳阔 | 来源:发表于2020-04-27 10:13 被阅读0次

    title: glide-源码解析-图床版
    date: 2020-03-17 20:41:59
    tags: [android工具源码]
    typora-root-url: ./glide-源码解析
    typora-copy-images-to: ./glide-源码解析


    Glide.with(this)
            .load("http://p3.pstatp.com/origin/pgc-image/15220293999066398493c24")
            .into(imageView);
    

    With

    主要工作.给activity 创建了一个SupportRequestManagerFragment,这是个fragment.但是没有view显示.并且给这个fragment 绑定了一个RequestManager 和一个lifecycle. lifecycle 是用来观察fragment的生命周期的.他会回调所有注册的LifecycleListener. 而这个RequestManager也是一个LifecycleListener. 也就是说使RequestManager可以检测到声明周期的变化.

    load

    主要是创建RequestBuilder.并把url赋值给他.

    into

    请求会抽象成 SingleRequest . 而目标对象抽象成target, 定义配置抽象成BaseRequestOptions.

    Engine 是最后执行的引擎. RequestManager 负责管理发出请求.

    获取的资源抽象为 EngineResource

    加载任务是DecodeJob和 EngineJob

    EngineJob 内部维护了线程池,用来管理资源加载,已经当资源加载完毕的时候通知回调。 DecodeJob 继承了 Runnable,是线程池当中的一个任务。就像上面那样,我们通过调用 engineJob.start(decodeJob) 来开始执行图片加载的任务

    SourceGenerator是源资源生成器 .他是通过URLConnect从网络下载. 同样的还有

    DataCacheGenerator ,从文件中读取图片资源

    ,ResourceCacheGenerator,MultiModelLoader 表示从不同的源拿到图片.

    这里的主要逻辑是构建一个用于将数据缓存到磁盘上面的 DataCacheGenerator。DataCacheGenerator 的流程基本与 SourceGenerator 一致,也就是根据资源文件的类型找到 ModelLoader,然后使用 DataFetcher 加载缓存的资源。与之前不同的是,这次是用 DataFecher 来加载 File 类型的资源。也就是说,当我们从网络中拿到了数据之后 Glide 会先将其缓存到磁盘上面,然后再从磁盘上面读取图片并将其显示到控件上面。所以,当从网络打开了输入流之后 SourceGenerator 的任务基本结束了,而后的显示的任务都由 DataCacheGenerator 来完成。

    Downsampler 用来解析图片.可以研究下.

    https://blog.csdn.net/qq_41979349/article/details/102627939

    http://frodoking.github.io/2015/10/10/android-glide/

    内存缓存原理

    glide在内存中会产生两种缓存.一种是使用弱引用存储的图片缓存, 一种是lru缓存.使用时.先从弱引用中找到缓存,如果不存在.再去lru中取,然后从lru中把图片保存到弱引用中.因此,总是总弱引用中找到图片.当弱引用被gc回收后.会把图片在加入到lru中. 然后下次使用再从lru中把图片放到弱引用中.

    这里就有个问题. 弱引用指向的对象.在被gc回收后.应该已经不存在了.为何还能加入到lru中.这是因为弱引用在包装引用对象的同时,内部有个属性直接指向了引用对象内部的图片资源.当引用对象被gc后. 再从弱引用中取出这个图片资源.加入到lru中.

    首先 图片资源抽象为Resource类. 而在加入弱引用的则是EngineResource.他是对resource的包装

    class EngineResource<Z> implements Resource<Z> {
      private final Resource<Z> resource;
    }
    

    在看弱引用如何包装EngineResource, 弱引用类是 ResourceWeakReference, 他通过一个key来标识一个资源.同时内部的resource 则指向被包装的引用的资源.

    static final class ResourceWeakReference extends WeakReference<EngineResource<?>> {
       @Synthetic final Key key; //标识一个资源的key. lru中也会使用他
      Resource<?> resource;
      ResourceWeakReference(
          @NonNull Key key,
          @NonNull EngineResource<?> referent,
          @NonNull ReferenceQueue<? super EngineResource<?>> queue,
          boolean isActiveResourceRetentionAllowed) {
        super(referent, queue);
        this.key = key;
        this.resource = Preconditions.checkNotNull(referent.getResource())
        //这里最重要, 弱引用的resource ,指向引用对象内的图片资源.当引用对象被回收.这个resource 则使引用对象的图片资源不会被回收.
         }
      }
    

    弱引用管理ActiveResources

    弱引用的管理类是ActiveResources. 内部维护一个弱引用的hashMap.通过key找到对应的弱引用对象.这个key是在弱引用和lru中公用的用来标识一个资源的对象.

    final class ActiveResources {
      final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
      private final ReferenceQueue<EngineResource<?>> resourceReferenceQueue = new ReferenceQueue<>();
     }
    

    弱引用添加

    比较简单,创建弱引用.传入资源和key.然后查找map.如果有旧的弱引用.删除掉.

     synchronized void activate(Key key, EngineResource<?> resource) {
     
        ResourceWeakReference toPut =
            new ResourceWeakReference(
                key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);
    
        ResourceWeakReference removed = activeEngineResources.put(key, toPut);
        if (removed != null) {
          removed.reset();
        }
      }
    

    弱引用获取

    从map中查找key对应的弱引用. 如果弱引用包装的对象 active为null了.表示被回收了.那么需要吧弱引用去掉.

     synchronized EngineResource<?> get(Key key) {
        ResourceWeakReference activeRef = activeEngineResources.get(key);
        if (activeRef == null) {
          return null;
        }
    
        EngineResource<?> active = activeRef.get();
        if (active == null) {
          cleanupActiveReference(activeRef);
        }
        return active;
      }
    

    清理弱引用

    清理弱引用时,如果弱引用中的resurce 还有值,就要把这个图片资源加载到lruCache中,

    先是从 map中移除掉key对应的弱引用.然后 如果弱引用的resource还有值,就新建一个EngineResource资源.包装这个 resource资源.最后的listener则是Engine. 他会把这个newResource加入到LruCache中.这就实现了.当弱引用包装的对象被gc回收后. 把这个图片资源在保存到lru中.

      void cleanupActiveReference(@NonNull ResourceWeakReference ref) {
        synchronized (listener) {
          synchronized (this) {
            activeEngineResources.remove(ref.key);
    
            if (!ref.isCacheable || ref.resource == null) {
              return;
            }
            EngineResource<?> newResource =
                new EngineResource<>(ref.resource,  true,  false);
            newResource.setResourceListener(ref.key, listener);
            listener.onResourceReleased(ref.key, newResource);
          }
        }
      }
    

    取图片内存缓存

    方法是从Engine的load开始.

     public synchronized <R> LoadStatus load(...
     先生产图片资源对应的key.缓存key是实现内存和磁盘缓存的唯一标识
    EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
            resourceClass, transcodeClass, options);
            //从弱引用map中找到资源,如果有就返回
        EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
        if (active != null) {
          cb.onResourceReady(active, DataSource.MEMORY_CACHE);
          return null;
        }
        //从lrucache中读取图片资源.有就返回
        EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
        if (cached != null) {
          cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
          return null;
        }
        ...
    }
    

    loadFromActiveResources 很简单.就是从上边的ActiveResources中读取EngineResource.

    看看如何从lrucache中读取

      private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
            //从lru中找到资源.
        EngineResource<?> cached = getEngineResourceFromCache(key);
        //如果可已缓存,则把图片加入到 弱引用缓存中.
        if (cached != null) {
          cached.acquire();
          activeResources.activate(key, cached);
        }
        return cached;
      }
      
        private EngineResource<?> getEngineResourceFromCache(Key key) {
        Resource<?> cached = cache.remove(key);
            //可以看到,竟然是从lrucache中remove掉对应的资源. 然后进行封装.
        final EngineResource<?> result;
        if (cached == null) {
          result = null;
        } else if (cached instanceof EngineResource) {
          // Save an object allocation if we've cached an EngineResource (the typical case).
          result = (EngineResource<?>) cached;
        } else {
          result = new EngineResource<>(cached, true , true );
        }
        return result;
      }
    

    这里可以看到.一个资源.通过key来唯一标识. 并且在使用时总是在弱引用map中. 如果不在.就从lru缓存中把他删除.在加入到弱引用map中.而当从弱引用中清除时,在加入会lru中. 这就使一个图片在内存中只能有一份.

    LruResourceCache

    LruCache 是 最近最少使用 算法的缓存,它的核心思想是当缓存满时,会优先淘汰那些近期最少使用的缓存对象。

    LruCache内部维护一个LinkedHashMap.每添加一个 item.就计算下item的尺寸和当前容量.如果加起来超过最大容量 了.就先把item加进来.在跳转linkedHashMap的容量. 做法就是得到迭代器.从头到尾.删除元素,删除一个从新计算下当前容量.若果还大于最大容量.就继续删除

    public class LruCache<T, Y> {
    //保存数据的集合.
      private final Map<T, Y> cache = new LinkedHashMap<>(100, 0.75f, true);
      private final long initialMaxSize;  初始化容量和最大容量
      private long maxSize;
      private long currentSize; 当前容量
      }
    

    添加数据

    计算item的容量.删除旧的item.看看是否要优化linkedHashMap的容量

     public synchronized Y put(@NonNull T key, @Nullable Y item) {
        final int itemSize = getSize(item); 
        if (itemSize >= maxSize) { //item超过最大容量.不让添加
          onItemEvicted(key, item);
          return null;
        }
    
        if (item != null) {
          currentSize += itemSize;
        }  //保存新的item. 替换旧的item
        @Nullable final Y old = cache.put(key, item);
        if (old != null) {
          currentSize -= getSize(old);
    
          if (!old.equals(item)) {
            onItemEvicted(key, old);
          }
        }
        //优化容量.
        evict();
    
        return old;
      }
    

    优化容量

      protected synchronized void trimToSize(long size) {
        Map.Entry<T, Y> last;
        Iterator<Map.Entry<T, Y>> cacheIterator;
        while (currentSize > size) {
        //拿到linkedhashMap的迭代器. 
          cacheIterator  = cache.entrySet().iterator();
          //从头开始取出一项,计算尺寸, 然后删除.直到当前容量小于最大容量.
          last = cacheIterator.next();
          final Y toRemove = last.getValue();
          currentSize -= getSize(toRemove);
          final T key = last.getKey();
          cacheIterator.remove();
          onItemEvicted(key, toRemove);
        }
      }
        //优化至最大容量
      private void evict() {
        trimToSize(maxSize);
      }
    

    config. Size current(保留的数量)

    1 多 多

    bitmap对象池

    LruBitmapPool 是bitmap对象池,他保存许多bitmap.提供给外界使用.然后在外边使用完成后在加入到对象池中,减少bitmap的创建和销毁.因为bitmap就是一段图片的内存.几个类介绍如下

    Key. 一个key来标识一个bitmap. key则是由bitmap的字节大小和config封装而成.

    SizeConfigStrategy 是bitmap的查找和存储规则类,他负责根据提供的字节大小和config.选出合适的bitmap.

    这里大体的流程是LruBitmapPool 通过SizeConfigStrategy来管理所有的bitmap的添加查找. 并且通过lruCache机制控制所以bitmap占用内存的大小.超过了就删除最佳最少使用的bitmap.

    SizeConfigStrategy

    内部有两个结合. 一个是通过key保存所有的bitmap. 一个是通过bitmap的config 来保存这种config类型下.有多少个尺寸的bitmap及bitmap 的个数

    private final GroupedLinkedMap<Key, Bitmap> groupedMap = new GroupedLinkedMap<>();
    private final Map<Bitmap.Config, NavigableMap<Integer, Integer>> sortedSizes = new HashMap<>();
    

    添加

    原理比较简单. 先得到bitmap的内存大小.然后size和 config 生成一个唯一的key. 并把这个bitmap加入到groupedMap中. 最后在通过config 找到该config下所有的size 的数量.

    keypool 是一个生产key的对象池.很简单.

    config 的类型有如下几种

    private static Config sConfigs[] = {
        null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888, RGBA_F16, HARDWARE
    };
    

    找到某个config 下的所有尺寸的匹配数量的map.然后再把该数量加1.

    private NavigableMap<Integer, Integer> getSizesForConfig(Bitmap.Config config) {
      NavigableMap<Integer, Integer> sizes = sortedSizes.get(config);
      if (sizes == null) {
        sizes = new TreeMap<>();
        sortedSizes.put(config, sizes);
      }
      return sizes;
    }
    
    public void put(Bitmap bitmap) {
      int size = Util.getBitmapByteSize(bitmap);
      Key key = keyPool.get(size, bitmap.getConfig());
    
      groupedMap.put(key, bitmap);
    //可以看到这里是以bitmap的size作为key.来保存统一size下
      NavigableMap<Integer, Integer> sizes = getSizesForConfig(bitmap.getConfig());
      Integer current = sizes.get(key.size);
      sizes.put(key.size, current == null ? 1 : current + 1);
    }
    

    这里.加入我们添加一个bitmap. 他的config是RGB_565,size是100,则在groupedMap里有个RGB_(565,100)为key.的bitmap .

    而在 sortedSizes里.通过config 为RGB_565 可以得到一个 size 的map/ 里边有数据(100,1).

    当我们添加一个config相同. size是200的bitmap. groupedMap 就会有两个bitmap了.而sortedSizes里.通过config 为RGB_565,就有 (100,1)(200,1) 两组数据. 也就是保留了同一种config下.不同size的数量了.

    查找

    通过 指定的bitmap.Config和宽高.来得到合适的图片.

    先是计算出要得到的size. 做发就是宽 * 高 * 图片每位的字节数.

    通过findBestKey 在sortedSizes查找到合适的组合,然后封装成key. 在用这个key去得到对应的bitmap.这个bitmap的尺寸可能和需求的不一样.就需要reconfigure 来从新配置该bitmap

    这里的精华在findBestKey

    public Bitmap get(int width, int height, Bitmap.Config config) {
      int size = Util.getBitmapByteSize(width, height, config);
      Key bestKey = findBestKey(size, config);
    
      Bitmap result = groupedMap.get(bestKey);
      if (result != null) {
        // Decrement must be called before reconfigure.
        decrementBitmapOfSize(bestKey.size, result);
        result.reconfigure(width, height, config);
      }
      return result;
    }
    

    先根据需要的尺寸生产一个key.

    然后通过传入的config.在sortedSizes里找到所有的<size,num>的map.也就是拿到这种config里所有可用的size及对应的个数.也就是上文的(100,1)(200,1)的数据.

    sizesForPossibleConfig.ceilingKey(size); 则是找到比size大的最接近的key, 比如 size 是99.那么因为map里只有(100,1)(200,1), 就找到100这个尺寸.. 其实也就是找到比需要的bitmap尺寸大一点的合适的bitmap的size.

    最后得到的就是 原有保存了.最进行需要的尺寸的bitmap.的key.

    private Key findBestKey(int size, Bitmap.Config config) {
      Key result = keyPool.get(size, config);
      for (Bitmap.Config possibleConfig : getInConfigs(config)) {
        NavigableMap<Integer, Integer> sizesForPossibleConfig = getSizesForConfig(possibleConfig);
        //找到合适的size.生成key. 最大的possibleSize不能大于size * 8
        Integer possibleSize = sizesForPossibleConfig.ceilingKey(size);
        if (possibleSize != null && possibleSize <= size * MAX_SIZE_MULTIPLE) {
          if (possibleSize != size
              || (possibleConfig == null ? config != null : !possibleConfig.equals(config))) {
            keyPool.offer(result);
            result = keyPool.get(possibleSize, possibleConfig);
          }
          break;
        }
      }
      return result;
    }
    

    这里看到. 查找时先匹配config.在匹配尺寸.找一个大于等于需求尺寸的size.然后在得到对应的bitmap. 在把这个bitmap从新配置成需求的宽高.提供出去.

    比如传入的是 88,RGB_565 的尺寸, 我们会匹配到 100,RGB_565.的bitmap. 然后通过bitmap.reconfigure. 把 100的尺寸改完传入的88. 这样就复用了这个bitmap.

    删除

    删除最后一个bitmap. 并删除对应的size 计数.如果对于的size的技术为0.把这个size的key也删掉.

    public Bitmap removeLast() {
      Bitmap removed = groupedMap.removeLast();
      if (removed != null) {
        int removedSize = Util.getBitmapByteSize(removed);
        decrementBitmapOfSize(removedSize, removed);
      }
      return removed;
    }
    

    LruBitmapPool

    bitmap对象池内部.通过SizeConfigStrategy增删图片. 他只负责控制对象池的整体大小.

    添加

    把图片添加到 SizeConfigStrategy中,并计算依据添加的容量大小.通过 evict控制总容量

    public synchronized void put(Bitmap bitmap) {
    
      final int size = strategy.getSize(bitmap);
      strategy.put(bitmap);
      tracker.add(bitmap);
    
      puts++;
      currentSize += size;
    
      evict();
    }
    

    获取

    找到能复用的bitmap. 清除为透明色,否则就创建一个固定宽高的.

    public Bitmap get(int width, int height, Bitmap.Config config) {
      Bitmap result = getDirtyOrNull(width, height, config);
      if (result != null) {
        result.eraseColor(Color.TRANSPARENT);
      } else {
        result = createBitmap(width, height, config);
      }
      return result;
    }
    

    从 sizeConfigStrategy里找到合适的bitmap..然后减少当前保存的bitmap容量.

    private synchronized Bitmap getDirtyOrNull(
        int width, int height, @Nullable Bitmap.Config config) {
      assertNotHardwareConfig(config);
      final Bitmap result = strategy.get(width, height, config != null ? config : DEFAULT_CONFIG);
      if (result == null) {
        misses++;
      } else {
        hits++;
        currentSize -= strategy.getSize(result);
        tracker.remove(result);
        normalize(result);
      }
      return result;
    }
    

    容量控制

    从后向前删除bitmap.直到当前容量小于要求的容量. 然后用bitmap.recycle.来回收.很简单.

    private synchronized void trimToSize(long size) {
      while (currentSize > size) {
        final Bitmap removed = strategy.removeLast();
        if (removed == null) {
          currentSize = 0;
          return;
        }
        tracker.remove(removed);
        currentSize -= strategy.getSize(removed);
        evictions++;
        removed.recycle();
      }
    }
    

    https://www.jianshu.com/p/d6cae68175f2 这个讲解比较细.

    https://zhuanlan.zhihu.com/p/60426316 这个也很不错.

    https://www.codercto.com/a/53115.html

    bitmap 采样

    https://www.codercto.com/a/52496.html 这个牛逼.

    bitmapOpeions的参数详解

    public Bitmap inBitmap;  //是否重用该Bitmap,注意使用条件,Bitmap的大小必须等于inBitmap,inMutable为true
    public boolean inMutable;  //设置Bitmap是否可以更改
    public boolean inJustDecodeBounds; // true时,decode不会创建Bitmap对象,但是可以获取图片的宽高
    public int inSampleSize;  // 压缩比例,比如=4,代表宽高压缩成原来的1/4,注意该值必须>=1
    public Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;  //Bitmap.Config,默认为ARGB_8888
    public boolean inPremultiplied; //默认为true,一般不需要修改,如果想要修改图片原始编码数据,那么需要修改
    public boolean inDither; //是否抖动,默认为false
    public int inDensity; //Bitmap的像素密度
    public int inTargetDensity; //Bitmap最终的像素密度(注意,inDensity,inTargetDensity影响图片的缩放度)
    public int inScreenDensity; //当前屏幕的像素密度
    public boolean inScaled; //是否支持缩放,默认为true,当设置了这个,Bitmap将会以inTargetDensity的值进行缩放
    public boolean inPurgeable; //当存储Pixel的内存空间在系统内存不足时是否可以被回收
    public boolean inInputShareable; //inPurgeable为true情况下才生效,是否可以共享一个InputStream
    public boolean inPreferQualityOverSpeed; //为true则优先保证Bitmap质量其次是解码速度
    public int outWidth; //Bitmap最终的宽
    public int outHeight;  //Bitmap最终的高
    public String outMimeType; //
    public byte[] inTempStorage; //解码时的临时空间,建议16*1024
    
    

    Registry

    所有的模块都在这里注册

    private final ModelLoaderRegistry modelLoaderRegistry;
    private final EncoderRegistry encoderRegistry;
    private final ResourceDecoderRegistry decoderRegistry;
    private final ResourceEncoderRegistry resourceEncoderRegistry;
    private final DataRewinderRegistry dataRewinderRegistry;
    private final TranscoderRegistry transcoderRegistry;
    private final ImageHeaderParserRegistry imageHeaderParserRegistry;
    
    private final ModelToResourceClassCache modelToResourceClassCache =
        new ModelToResourceClassCache();
    private final LoadPathCache loadPathCache = new LoadPathCache();
    private final Pool<List<Throwable>> throwableListPool = FactoryPools.threadSafeList();
    

    这个类里有大量的append.register.prepend.用来把各种模块追加到上边的各种注册器里边.

    image-20200324213130912

    这里的每个registry都是负责一个模块的功能的注册加载的.下边简单分别看看

    ModelLoaderRegistry

    负责注册 数据加载模块.重要属性是MultiModelLoaderFactory,注册的模块都是注册到MultiModelLoaderFactory中了.

    private final MultiModelLoaderFactory multiModelLoaderFactory;
      private final ModelLoaderCache cache = new ModelLoaderCache();
    public synchronized <Model, Data> void append(
        @NonNull Class<Model> modelClass,
        @NonNull Class<Data> dataClass,
        @NonNull ModelLoaderFactory<? extends Model, ? extends Data> factory) {
      multiModelLoaderFactory.append(modelClass, dataClass, factory);
      cache.clear();
    }
    

    MultiModelLoaderFactory

    MultiModelLoaderFactory内部,可以看到所以的模块都封装成了 Entry.

        private final List<Entry<?, ?>> entries = new ArrayList<>();
      private <Model, Data> void add(
          @NonNull Class<Model> modelClass,
          @NonNull Class<Data> dataClass,
          @NonNull ModelLoaderFactory<? extends Model, ? extends Data> factory,
          boolean append) {
        Entry<Model, Data> entry = new Entry<>(modelClass, dataClass, factory);
        entries.add(append ? entries.size() : 0, entry);
      }
    

    他的build 方法.会遍历 所以entry. 找到与modleclass匹配的entry然后执行这个entry的build方法.最后返回出所有匹配的modelLoder集合

      synchronized <Model> List<ModelLoader<Model, ?>> build(@NonNull Class<Model> modelClass) {
        try {
          List<ModelLoader<Model, ?>> loaders = new ArrayList<>();
          for (Entry<?, ?> entry : entries) {
            if (entry.handles(modelClass)) {
              alreadyUsedEntries.add(entry);
              //调用entry的build
              loaders.add(this.<Model, Object>build(entry));
              alreadyUsedEntries.remove(entry);
            }
          }
          return loaders;
        } catch (Throwable t) {
        }
      }
    

    这里看着和抽象.看个他的实例.

    HttpGlideUrlLoader

    HttpUriLoader有个静态内部类Factory继承自MultiModelLoaderFactory.因此他的build方法会执行.可以看到.一个Factory和一个ModleLoader 是绑定的.这应该算工厂模式.但是我觉得有点复杂了.其实没必要加这个工厂.

    public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
      return new HttpUriLoader(multiFactory.build(GlideUrl.class, InputStream.class));
    }
    

    HttpGlideUrlLoader继承自 ModelLoader.最重要的就是BuildLoadData方法. 也就是加载想要的数据,

    先找缓存.如果没有.就新建一个loadData返回. 这里又创建了HttpUrlFetcher.

     public LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height,
          @NonNull Options options) {
        GlideUrl url = model;
        if (modelCache != null) {
          url = modelCache.get(model, 0, 0);
          if (url == null) {
            modelCache.put(model, 0, 0, model);
            url = model;
          }
        }
        return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
      }
    

    HttpUrlFetcher

    HttpUrlFetcher又继承自DataFetcher.是获取数据的真正类.重要方法是loadData.参数有一个callback.通过callback把数据返回, 这里是用url 创建一个HttpURLConnection. 连接后获取一个inputStream提供给callback

    public void loadData(@NonNull Priority priority,
        @NonNull DataCallback<? super InputStream> callback) {
        InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
        callback.onDataReady(result);
    }
    

    总结

    ModelLoaderRegistry里注册了很多的ModelLoaderFactory.而ModelLoaderFactory的实现类又往往和一个ModelLoader的实现类绑定起来. ModelLoaderFactory就是工厂模式.他的build.就向往提供这个modelLoader.

    ModelLoader最重要的方法是buildLoadData,也就是根据参数返回一个loadData(加载的数据).不同的ModelLoader产生不同的loadData.但是最终都是由DataFetcher 来完成数据的真正获取.

    大概就是这么个关系. 其实就是个加载数据的模块.根据泛型来判断.但是使用了工厂模式.并进行了抽象

    image-20200324223604817

    EncoderRegistry

    这是把数据编码保存到文件中的注册器. 工作就是把内存中的各种图片.保存到文件中.

      private final List<Entry<?>> encoders = new ArrayList<>();
     public synchronized <T> void append(@NonNull Class<T> dataClass, @NonNull Encoder<T> encoder) {
        encoders.add(new Entry<>(dataClass, encoder));
      }
    

    可以看到添加的都是Encoder. 有如下几个子类. 主要方法是encode.这个比较简单.看下BitmapEncoder

    image-20200324224438010

    BitmapEncoder

    很简单.就是通过流把bitmap保存.

    public boolean encode( Resource<Bitmap> resource,  File file,Options options) {
        final Bitmap bitmap = resource.get();
        Bitmap.CompressFormat format = getFormat(bitmap, options);
          int quality = options.get(COMPRESSION_QUALITY);
          OutputStream os = null;
          try {
            os = new FileOutputStream(file);
            bitmap.compress(format, quality, os);
            os.close();
            success = true;
            }
          }
    
     }
      
    

    ResourceDecoderRegistry

    按名称.应该是从文件或者其他地方解码出图片到内存中. 所有的registry都是一个用来保存改功能的集合.主要还是看里边的实现. 这里主要是ResourceDecoder,有如下这些,主要的方法就是decode.返回一个resource资源.

    image-20200324225136996

    看下 ByteBufferBitmapDecoder的处理.这个是比较复杂的

    ByteBufferBitmapDecoder

    先把byteBuffer 包装成了ByteBufferStream. 又通过Downsampler 来decode

    public Resource<Bitmap> 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);
    }
    

    Downsampler

    这里显示通过options 的各种默认参数. 来封装BitMapFactory.options,可以看到的是.连读取流使用byte[] 缓冲都是使用了对象池. glide对象池的引用针对是很广了.

    继续通过decodeFromWrappedStreams 来获取bitmap.

    public Resource<Bitmap> decode(InputStream is, int requestedWidth, int requestedHeight,
        Options options, DecodeCallbacks callbacks) throws IOException {
    
      byte[] bytesForOptions = byteArrayPool.get(ArrayPool.STANDARD_BUFFER_SIZE_BYTES, byte[].class);
      BitmapFactory.Options bitmapFactoryOptions = getDefaultOptions();
      bitmapFactoryOptions.inTempStorage = bytesForOptions;
    
      DecodeFormat decodeFormat = options.get(DECODE_FORMAT);
      
      try {
        Bitmap result = decodeFromWrappedStreams(is, bitmapFactoryOptions,
            downsampleStrategy, decodeFormat, isHardwareConfigAllowed, requestedWidth,
            requestedHeight, fixBitmapToRequestedDimensions, callbacks);
            //这里还绑定了bitmap 和bigmap对象池. 用来在bitmap用完时进行回收.
        return BitmapResource.obtain(result, bitmapPool);
      } finally {
        releaseOptions(bitmapFactoryOptions);
        byteArrayPool.put(bytesForOptions);
      }
    }
    
    

    这里的逻辑更复杂. 删掉一些不重要代码

    private Bitmap decodeFromWrappedStreams(InputStream is,
        BitmapFactory.Options options, DownsampleStrategy downsampleStrategy,
        DecodeFormat decodeFormat, boolean isHardwareConfigAllowed, int requestedWidth,
        int requestedHeight, boolean fixBitmapToRequestedDimensions,
        DecodeCallbacks callbacks) throws IOException {
    //得到 图片的宽高.
      int[] sourceDimensions = getDimensions(is, options, callbacks, bitmapPool);
      int sourceWidth = sourceDimensions[0];
      int sourceHeight = sourceDimensions[1];
    
        //解析inputStream. 得到图片的选旋转方向和旋转角度
      int orientation = ImageHeaderParserUtils.getOrientation(parsers, is, byteArrayPool);
      int degreesToRotate = TransformationUtils.getExifOrientationDegrees(orientation);
        //如果有请求设定的宽高.就是用他.否则使用图片宽高. 通常glide都会把图片缩放成imageview的宽高.
        int targetWidth = requestedWidth == Target.SIZE_ORIGINAL ? sourceWidth : requestedWidth;
      int targetHeight = requestedHeight == Target.SIZE_ORIGINAL ? sourceHeight : requestedHeight;
    
        //进行图片缩放比例的确定, 这里设置了各种BitMapFactory.Options的参数
      calculateScaling(
          imageType,
          is,
          callbacks,
          bitmapPool,
          downsampleStrategy,
          degreesToRotate,
          sourceWidth,
          sourceHeight,
          targetWidth,
          targetHeight,
          options);
       //这里设置的 BitMapFactory.Options.inPreferredConfig 也就是图片格式   
      calculateConfig(
          is,
          decodeFormat,
          isHardwareConfigAllowed,
          isExifOrientationRequired,
          options,
          targetWidth,
          targetHeight);
        //这里根据inSampleSize 的值 和图片的dpi.要显示view的dpi.来确定最后的图片尺寸expectWidth expectHeight
      if ((options.inSampleSize == 1 || isKitKatOrGreater) && shouldUsePool(imageType)) {
        int expectedWidth;
        int expectedHeight;
          expectedWidth = targetWidth;
          expectedHeight = targetHeight;
        } else {
          float densityMultiplier = isScaling(options)
              ? (float) options.inTargetDensity / options.inDensity : 1f;
          int sampleSize = options.inSampleSize;
          int downsampledWidth = (int) Math.ceil(sourceWidth / (float) sampleSize);
          int downsampledHeight = (int) Math.ceil(sourceHeight / (float) sampleSize);
          expectedWidth = Math.round(downsampledWidth * densityMultiplier);
          expectedHeight = Math.round(downsampledHeight * densityMultiplier);
    
          }
        }
    //设置BitMapFactory.options. inBitmap. 用期望的尺寸去bitmapPool对象池找到合适的bitmap来复用
          setInBitmap(options, bitmapPool, expectedWidth, expectedHeight);
          //BitMapFactory 来decodeStream .生成bitmap,这里没什么了.因为前边把options的参数都确定了.
      Bitmap downsampled = decodeStream(is, options, callbacks, bitmapPool);
        //旋转图片
       Bitmap   rotated = TransformationUtils.rotateImageExif(bitmapPool, downsampled, orientation);
    
            //回收bitmap
        if (!downsampled.equals(rotated)) {
          bitmapPool.put(downsampled);
        }
      }
        返回最后的结果,旋转后的bitmap
      return rotated;
    }
    

    总结

    decoder 就是把参数转换成bitmap资源的地方. 这里有多个方式可以转换, 上班的DownSampler.里通过设置BitMapFactory.options.和bitmap对象池的复用.比较高效的转换了bitmap. 其他还有从文件中加载等等.

    ResourceEncoderRegistry

    也是一个管理类.重要的是里边的ResourceEncoder. 他和之前的EncoderRegistry 是一样的. 都是encode方法吧资源保存到文件中.

    DataRewinderRegistry

    这也是负责管理这模块的所有类的集合. 主要的类是DataRewinder,主要方法是rewindAndGet(),

    这个功能的翻译是 数据回卷器. 其实就是进行数据读取回到原位置..

    image-20200325105633335

    InputStreamRewinder

    对输入流的回卷操作. 可以看到 mark和reset 是对应的.也就是mark最大读取的字节数是5kb.然后回卷后可以重新读取这5kb的数据. 这是流的数据的复用.也就是可以重复读取这5kb的数据.

    这个mark保持有效。比如说mark(10),那么在read()10个以内的字符时,reset()操作后可以重新读取已经读出的数据,如果已经读取的数据超过10个,那reset()操作后,就不能正确读取以前的数据了,因为此时mark标记已经失效

      private static final int MARK_LIMIT = 5 * 1024 * 1024;
    
    @Synthetic
      InputStreamRewinder(InputStream is, ArrayPool byteArrayPool) {
        bufferedStream = new RecyclableBufferedInputStream(is, byteArrayPool);
        bufferedStream.mark(MARK_LIMIT);
      }
    
      @NonNull
      @Override
      public InputStream rewindAndGet() throws IOException {
        bufferedStream.reset();
        return bufferedStream;
      }
    
    

    可以看出这就是反复读取同一段数据的一个操作类.

    TranscoderRegistry

    这是一个数据转换类. 内部的Entry 结构很清楚, 从什么格式,转换到什么格式. 使用什么ResourceTranscoder转换器.

    private final Class<Z> fromClass;
    private final Class<R> toClass;
    @Synthetic final ResourceTranscoder<Z, R> transcoder;
    

    ResourceTranscoder 只有一个transcode 方法.Resource<R> transcode(Resource<Z> toTranscode,Options options); 就是把格式Z转换为格式R,实现类如下

    image-20200325111339778

    BitmapBytesTranscoder

    把一个bitmap 转成byte 数组返回. 其实是使用bitmap.compress 来实现.

    public Resource<byte[]> transcode(@NonNull Resource<Bitmap> toTranscode,
        @NonNull Options options) {
      ByteArrayOutputStream os = new ByteArrayOutputStream();
      toTranscode.get().compress(compressFormat, quality, os);
      toTranscode.recycle();
      return new BytesResource(os.toByteArray());
    }
    

    总结

    被注册组件的集合(包括默认被 Glide 注册的和在 Module 中被注册的),会被用于定义一个加载路径集合。每个加载路径都是从提供给 load 方法的数据模型到 as 方法指定的资源类型的一个逐步演进的过程。一个加载路径(粗略地)由下列步骤组成:

    1. 模型 (Model) -> 数据 (Data) (由模型加载器 (ModelLoader) 处理)
    2. 数据 (Data) -> 资源 (Resource) (由资源解析器 (ResourceDecoder) 处理)
    3. 资源 (Resource) -> 转码后的资源 (Transcoded Resource) (可选;由资源转码器 (ResourceTranscoder) 处理)

    编码器 (Encoder) 可以在步骤 2 之前往 Glide 的磁盘缓存中写入数据。资源编码器 (ResourceEncoder) 可以在步骤 3 之前往 Glide 的磁盘缓存写入资源。

    相关文章

      网友评论

          本文标题:glide源码解析

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