glide源码解析之GlideModule

来源:互联网 发布:万网网络空间购买 编辑:程序博客网 时间:2024/05/22 12:12
GlideModule是对glide全局配置相关的类,这里介绍相关配置的源码解析
一般的用法是这样
public class GlideConfiguration implements GlideModule {

    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
        MemorySizeCalculator calculator = new MemorySizeCalculator(context);
        int defaultMemoryCacheSize = calculator.getMemoryCacheSize();
        int defaultBitmapPoolSize = calculator.getBitmapPoolSize();
        int customMemoryCacheSize = (int) (1.2 * defaultMemoryCacheSize);
        int customBitmapPoolSize = (int) (1.2 * defaultBitmapPoolSize);
        builder.setMemoryCache(new LruResourceCache(customMemoryCacheSize)); //设置图片缓存策略
        builder.setBitmapPool(new LruBitmapPool(customBitmapPoolSize));
        int cacheSize100MegaBytes = 104857600;  //100M
        String downloadDirectoryPath = Environment.getExternalStorageDirectory().getPath();
        builder.setDiskCache(new DiskLruCacheFactory(downloadDirectoryPath, "cache/", cacheSize100MegaBytes));
    }

    @Override
    public void registerComponents(Context context, Glide glide) {
        glide.register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
    }
}
然后再manifest文件中加入这个配置
        <meta-data
            android:name=".GlideConfiguration"
            android:value="GlideModule"/>

先看看GlideMoudle接口源码
package com.bumptech.glide.module;

import android.content.Context;

import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;

/**
 * An interface allowing lazy configuration of Glide including setting options using
 * {@link com.bumptech.glide.GlideBuilder} and registering
 * {@link com.bumptech.glide.load.model.ModelLoader ModelLoaders}.
 *
 * <p>
 *     To use this interface:
 *     <ol>
 *         <li>
 *             Implement the GlideModule interface in a class with public visibility, calling
 *             {@link com.bumptech.glide.Glide#register(Class, Class, com.bumptech.glide.load.model.ModelLoaderFactory)}
 *             for each {@link com.bumptech.glide.load.model.ModelLoader} you'd like to register:
 *             <pre>
 *                 <code>
 *                      public class FlickrGlideModule implements GlideModule {
 *                          {@literal @}Override
 *                          public void applyOptions(Context context, GlideBuilder builder) {
 *                              buidler.setDecodeFormat(DecodeFormat.ALWAYS_ARGB_8888);
 *                          }
 *
 *                          {@literal @}Override
 *                          public void registerComponents(Context context, Glide glide) {
 *                              glide.register(Model.class, Data.class, new MyModelLoader());
 *                          }
 *                      }
 *                  </code>
 *             </pre>
 *         </li>
 *         <li>
 *              Add your implementation to your list of keeps in your proguard.cfg file:
 *              <pre>
 *                  {@code
 *                      -keepnames class * com.bumptech.glide.samples.flickr.FlickrGlideModule
 *                  }
 *              </pre>
 *         </li>
 *         <li>
 *             Add a metadata tag to your AndroidManifest.xml with your GlideModule implementation's fully qualified
 *             classname as the key, and {@code GlideModule} as the value:
 *             <pre>
 *                 {@code
 *                     <meta-data
 *                          android:name="com.bumptech.glide.samples.flickr.FlickrGlideModule"
 *                          android:value="GlideModule" />
 *                 }
 *             </pre>
 *         </li>
 *     </ol>
 </p>
 *
 * <p>
 *     All implementations must be publicly visible and contain only an empty constructor so they can be instantiated
 *     via reflection when Glide is lazily initialized.
 * </p>
 *
 * <p>
 *     There is no defined order in which modules are called, so projects should be careful to avoid applying
 *     conflicting settings in different modules. If an application depends on libraries that have conflicting
 *     modules, the application should consider avoiding the library modules and instead providing their required
 *     dependencies in a single application module.
 * </p>
 */
public interface GlideModule {

    /**
     * Lazily apply options to a {@link com.bumptech.glide.GlideBuilder} immediately before the Glide singleton is
     * created.
     *
     * <p>
     *     This method will be called once and only once per implementation.
     * </p>
     *
     * @param context An Application {@link android.content.Context}.
     * @param builder The {@link com.bumptech.glide.GlideBuilder} that will be used to create Glide.
     */
    void applyOptions(Context contextGlideBuilder builder);

    /**
     * Lazily register components immediately after the Glide singleton is created but before any requests can be
     * started.
     *
     * <p>
     *     This method will be called once and only once per implementation.
     * </p>
     *
     * @param context An Application {@link android.content.Context}.
     * @param glide The newly created Glide singleton.
     */
    void registerComponents(Context contextGlide glide);
}

上面一堆注释介绍是怎么用的,最终就是两个方法需要实现。
一个是设置属性,一个是注册组件。
看设置属性的,先看参数GlideBuilder
/**
 * A builder class for setting default structural classes for Glide to use.
 */
public class GlideBuilder {

里边含有一些方法
/**
 * Sets the {@link com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool} implementation to use to store and
 * retrieve reused {@link android.graphics.Bitmap}s.
 *
 * @param bitmapPool The pool to use.
 * @return This builder.
 */
public GlideBuilder setBitmapPool(BitmapPool bitmapPool) {
    this.bitmapPool = bitmapPool;
    return this;
}

这个是设置bitmap池的,是在内存中存储以bitmap形式,大家应该都知道这个是占用内存比较大的,但是速度是最快的。

/**
 * Sets the {@link com.bumptech.glide.load.engine.cache.MemoryCache} implementation to store
 * {@link com.bumptech.glide.load.engine.Resource}s that are not currently in use.
 *
 * @param memoryCache The cache to use.
 * @return This builder.
 */
public GlideBuilder setMemoryCache(MemoryCache memoryCache) {
    this.memoryCache = memoryCache;
    return this;
}

这个是内存缓存的是以文件或者流的方式存在内存中的,没有上面那个处理速度快,但是比在磁盘读写和加载网络要快很多的。
上面两种都是在内存中存储的,在默认实现中,他们两种总共占内存的0.33-0.4的空间,如果在低内存下还有其他的处理,他们俩各占这部分内存的一半。

/**
 * Sets the {@link com.bumptech.glide.load.engine.cache.DiskCache.Factory} implementation to use to construct
 * the {@link com.bumptech.glide.load.engine.cache.DiskCache} to use to store
 * {@link com.bumptech.glide.load.engine.Resource} data on disk.
 *
 * @param diskCacheFactory The disk cche factory to use.
 * @return This builder.
 */
public GlideBuilder setDiskCache(DiskCache.Factory diskCacheFactory) {
    this.diskCacheFactory = diskCacheFactory;
    return this;
}
讲网络中下载的数据存储到磁盘中。不用重复下载,这个也可以自己去实现,如何存储以及存储的方式算法。

Glide是支持扩展性比较高的,基于接口去配置,可以实现对其灵活的扩展,上面是有一些默认实现的,如果需要更高级的用法,可以自己实现缓存策略,和硬盘存储策略。


/**
 * Sets the {@link com.bumptech.glide.load.DecodeFormat} that will be the default format for all the default
 * decoders that can change the {@link android.graphics.Bitmap.Config} of the {@link android.graphics.Bitmap}s they
 * decode.
 *
 * <p>
 *     Decode format is always a suggestion, not a requirement. See {@link com.bumptech.glide.load.DecodeFormat} for
 *     more details.
 * </p>
 *
 * <p>
 *     If you instantiate and use a custom decoder, it will use
 *     {@link com.bumptech.glide.load.DecodeFormat#DEFAULT} as its default.
 * </p>
 *
 * <p>
 *     Calls to this method are ignored on KitKat and Lollipop. See #301.
 * </p>
 *
 * @param decodeFormat The format to use.
 * @return This builder.
 */
public GlideBuilder setDecodeFormat(DecodeFormat decodeFormat) {
    this.decodeFormat = decodeFormat;
    return this;
}

设置全局的bitmap解码格式
解码格式源码在这里
package com.bumptech.glide.load;

/**
 * Options for setting the value of {@link android.graphics.Bitmap#getConfig()} for {@link android.graphics.Bitmap}s
 * returned by a {@link com.bumptech.glide.load.resource.bitmap.BitmapDecoder}.
 *
 * <p>
 *     Note - In some cases it may not be possible to obey the requested setting, not all
 *     {@link com.bumptech.glide.load.resource.bitmap.BitmapDecoder}s support setting formats and certain images may
 *     not be able to be loaded as certain configurations. Therefore this class represents a preference rather than a
 *     requirement.
 * </p>
 */
public enum DecodeFormat {
    /**
     * All bitmaps returned by the {@link com.bumptech.glide.load.resource.bitmap.BitmapDecoder} should return
     * {@link android.graphics.Bitmap.Config#ARGB_8888} for {@link android.graphics.Bitmap#getConfig()}.
     *
     * @deprecated Use the equivalent but less misleadingly named {@link #PREFER_ARGB_8888}. Scheduled to be removed
     * in Glide 4.0
     */
    @Deprecated
    ALWAYS_ARGB_8888,

    /**
     * Bitmaps decoded from most image formats (other than GIFs with hidden configs), will be decoded with the
     * ARGB_8888 config.
     *
     * <p>
     *     {@link android.graphics.BitmapFactory} does not allow us to guarantee that all returned Bitmaps will
     *     be of a requested config without resorting to expensive copying. As a result, this is a preference only.
     *     Most GIFs, for example, will still produce {@link android.graphics.Bitmap}s with null
     *     {@link android.graphics.Bitmap.Config}s.
     * </p>
     */
    PREFER_ARGB_8888,

    /**
     * Bitmaps decoded from image formats that support and/or use alpha (some types of PNGs, GIFs etc) should
     * return {@link android.graphics.Bitmap.Config#ARGB_8888} for {@link android.graphics.Bitmap#getConfig()}. Bitmaps
     * decoded from formats that don't support or use alpha should return
     * {@link android.graphics.Bitmap.Config#RGB_565} for {@link android.graphics.Bitmap#getConfig()}.
     *
     */
    PREFER_RGB_565;

    /** The default value for DecodeFormat. */
    public static final DecodeFormat DEFAULT PREFER_RGB_565;
}
这里默认是用Bitmap.option中国的565格式,我们也可以设置成888的格式,如下这样:
builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);

在GlideBuilder中的默认设置在此:
Glide createGlide() {
    if (sourceService == null) {
        final int cores = Math.max(1Runtime.getRuntime().availableProcessors());
        sourceService new FifoPriorityThreadPoolExecutor(cores);
    }
    if (diskCacheService == null) {
        diskCacheService new FifoPriorityThreadPoolExecutor(1);
    }

    MemorySizeCalculator calculator = new MemorySizeCalculator(context);
    if (bitmapPool == null) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            int size = calculator.getBitmapPoolSize();
            bitmapPool new LruBitmapPool(size);
       else {
            bitmapPool new BitmapPoolAdapter();
        }
    }

    if (memoryCache == null) {
        memoryCache new LruResourceCache(calculator.getMemoryCacheSize());
    }

    if (diskCacheFactory == null) {
        diskCacheFactory new InternalCacheDiskCacheFactory(context);
    }

    if (engine == null) {
        engine new Engine(memoryCachediskCacheFactorydiskCacheServicesourceService);
    }

    if (decodeFormat == null) {
        decodeFormat = DecodeFormat.DEFAULT;
    }

    return new Glide(enginememoryCachebitmapPoolcontextdecodeFormat);
}

下面是类的成员变量
private final Context context;

private Engine engine;
private BitmapPool bitmapPool;
private MemoryCache memoryCache;
private ExecutorService sourceService;
private ExecutorService diskCacheService;
private DecodeFormat decodeFormat;
private DiskCache.Factory diskCacheFactory;

也就是除了context以外都是可以设置的
sourceService是从服务器来去图片资源的时候的处理策略
diskCacheService是从磁盘缓存是的策略
这两个默认的都是带有优先级的先进先出方式
用的是这个
public class FifoPriorityThreadPoolExecutor extends ThreadPoolExecutor {

下面是bitmapPool处理方式
MemorySizeCalculator calculator = new MemorySizeCalculator(context);
if (bitmapPool == null) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        int size = calculator.getBitmapPoolSize();
        bitmapPool new LruBitmapPool(size);
   else {
        bitmapPool new BitmapPoolAdapter();
    }
}

默认是根据SDK的版本处理不同的方式 Build.VERSION_CODES.HONEYCOMB = 11
MemorySizeCalculator 这个类是池大小计算的类,有感兴趣的可以读下,看看怎么计算的。
实现大概就是图片缓存占用整个APP内存的0.33-0.4的,其中的一半是给BitmapPool用的,另一半是给cache用的
这个bitmapPool要是自己实现要实现BitmapPool接口就可以了。
/**
 * An interface for a pool that allows users to reuse {@link android.graphics.Bitmap} objects.
 */
public interface BitmapPool {

那个小于sdk11版本的默认是实现,就是什么都没有实现,下面是源码
/**
 * An {@link com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool BitmapPool} implementation that rejects all
 * {@link android.graphics.Bitmap Bitmap}s added to it and always returns {@code null} from get.
 */
public class BitmapPoolAdapter implements BitmapPool {
    @Override
    public int getMaxSize() {
        return 0;
    }

    @Override
    public void setSizeMultiplier(float sizeMultiplier) {
        // Do nothing.
    }

    @Override
    public boolean put(Bitmap bitmap) {
        return false;
    }

    @Override
    public Bitmap get(int width, int heightBitmap.Config config) {
        return null;
    }

    @Override
    public Bitmap getDirty(int width, int heightBitmap.Config config) {
        return null;
    }

    @Override
    public void clearMemory() {
        // Do nothing.
    }

    @Override
    public void trimMemory(int level) {
        // Do nothing.
    }

然后看下LRU算法实现的bitmapPool。
/**
 * Constructor for LruBitmapPool.
 *
 * @param maxSize The initial maximum size of the pool in bytes.
 */
public LruBitmapPool(int maxSize) {
    this(maxSizegetDefaultStrategy()getDefaultAllowedConfigs());
}

第一个是缓存池大小单位bytes
第二个是默认的缓存策略,
第三个是允许的配置,如ARGB565、ARGB888等
然后第二个缓存策略
private static LruPoolStrategy getDefaultStrategy() {
    final LruPoolStrategy strategy;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        strategy = new SizeConfigStrategy();
   else {
        strategy = new AttributeStrategy();
    }
    return strategy;
}

也是根据sdk版本不同的实现

@TargetApi(Build.VERSION_CODES.KITKAT)
public class SizeConfigStrategy implements LruPoolStrategy {

class AttributeStrategy implements LruPoolStrategy {

他们两个都实现了LruPoolStrategy接口
看下这个接口
interface LruPoolStrategy {
    void put(Bitmap bitmap);
    Bitmap get(int width, int heightBitmap.Config config);
    Bitmap removeLast();
    String logBitmap(Bitmap bitmap);
    String logBitmap(int width, int heightBitmap.Config config);
    int getSize(Bitmap bitmap);
}

看第get方法,是获得key的方式,width,height config
下面是AttributeStrategy 的实现
下面是key的代码
static class Key implements Poolable {
    private final KeyPool pool;
    private int width;
    private int height;
    // Config can be null :(
    private Bitmap.Config config;

    public Key(KeyPool pool) {
        this.pool = pool;
    }

    public void init(int width, int heightBitmap.Config config) {
        this.width = width;
        this.height = height;
        this.config = config;
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof Key) {
            Key other = (Key) o;
            return width == other.width
                    && height == other.height
                    && config == other.config;
        }
        return false;
    }

    @Override
    public int hashCode() {
        int result = width;
        result = 31 * result + height;
        result = 31 * result + (config != null config.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return getBitmapString(widthheightconfig);
    }

    @Override
    public void offer() {
        pool.offer(this);
    }
}

AttributeStrategy实现的get和put实现方法

private final KeyPool keyPool new KeyPool();
private final GroupedLinkedMap<KeyBitmap> groupedMap new GroupedLinkedMap<KeyBitmap>();

public void put(Bitmap bitmap) {
    final Key key = keyPool.get(bitmap.getWidth()bitmap.getHeight()bitmap.getConfig());

    groupedMap.put(keybitmap);
}

@Override
public Bitmap get(int width, int heightBitmap.Config config) {
    final Key key = keyPool.get(widthheightconfig);

    return groupedMap.get(key);
}

这个是用了一个 GroupedLinkedMap
来存储的,里边的实现是和LinkedHashMap相似的方式,LinkedHashMap本身是有对LRU算法相应的实现的,读一下他的构造方法就明白了。

然后再看下cache
if (memoryCache == null) {
    memoryCache new LruResourceCache(calculator.getMemoryCacheSize());
}

默认是LruResourceCache
/**
 * An LRU in memory cache for {@link com.bumptech.glide.load.engine.Resource}s.
 */
public class LruResourceCache extends LruCache<KeyResource<?>> implements MemoryCache {

咱们自己实现的cache要实现MemoryCache接口,把源码贴出来下
/**
 * An interface for adding and removing resources from an in memory cache.
 */
public interface MemoryCache {
    /**
     * An interface that will be called whenever a bitmap is removed from the cache.
     */
    interface ResourceRemovedListener {
        void onResourceRemoved(Resource<?> removed);
    }

    /**
     * Returns the sum of the sizes of all the contents of the cache in bytes.
     */
    int getCurrentSize();

    /**
     * Returns the current maximum size in bytes of the cache.
     */
    int getMaxSize();

    /**
     * Adjust the maximum size of the cache by multiplying the original size of the cache by the given multiplier.
     *
     * <p>
     *     If the size multiplier causes the size of the cache to be decreased, items will be evicted until the cache
     *     is smaller than the new size.
     * </p>
     *
     * @param multiplier A size multiplier >= 0.
     */
    void setSizeMultiplier(float multiplier);

    /**
     * Removes the value for the given key and returns it if present or null otherwise.
     *
     * @param key The key.
     */
    Resource<?> remove(Key key);

    /**
     * Add bitmap to the cache with the given key.
     *
     * @param key The key to retrieve the bitmap.
     * @param resource The {@link com.bumptech.glide.load.engine.EngineResource} to store.
     * @return The old value of key (null if key is not in map).
     */
    Resource<?> put(Key keyResource<?> resource);

    /**
     * Set the listener to be called when a bitmap is removed from the cache.
     *
     * @param listener The listener.
     */
    void setResourceRemovedListener(ResourceRemovedListener listener);

    /**
     * Evict all items from the memory cache.
     */
    void clearMemory();

    /**
     * Trim the memory cache to the appropriate level. Typically called on the callback onTrimMemory.
     *
     * @param level This integer represents a trim level as specified in {@link android.content.ComponentCallbacks2}.
     */
    void trimMemory(int level);
}

默认实现的LruCache就是用的
private final LinkedHashMap<T, Y> cache = new LinkedHashMap<T, Y>(100, 0.75f, true);
/**
 * Constructs a new {@code LinkedHashMap} instance with the specified
 * capacity, load factor and a flag specifying the ordering behavior.
 *
 * @param initialCapacity
 *            the initial capacity of this hash map.
 * @param loadFactor
 *            the initial load factor.
 * @param accessOrder
 *            {@code true} if the ordering should be done based on the last
 *            access (from least-recently accessed to most-recently
 *            accessed), and {@code false} if the ordering should be the
 *            order in which the entries were inserted.
 * @throws IllegalArgumentException
 *             when the capacity is less than zero or the load factor is
 *             less or equal to zero.
 */
public LinkedHashMap(
        int initialCapacity, float loadFactor, boolean accessOrder) {
    super(initialCapacityloadFactor);
    init();
    this.accessOrder = accessOrder;
}
这个是LinkedHashMap的构造方法,lru方式或者默认插入顺序

if (diskCacheFactory == null) {
    diskCacheFactory new InternalCacheDiskCacheFactory(context);
}

磁盘缓存的处理方式 需要实现 DiskCache接口
Glide给出了默认实现,注意一提的是,读写文件要注意读写一致的问题。如果有相关的可以参考下这里。

if (engine == null) {
    engine new Engine(memoryCachediskCacheFactorydiskCacheServicesourceService);
}

这个Glide的引擎,目前的set方法还是test阶段,处理下载缓存等等的。
自己定义这个的话,就相当高级了,但是glide这边是支持扩展的哦。

if (decodeFormat == null) {
    decodeFormat = DecodeFormat.DEFAULT;
}

这个就是Bitmap的解码设置,默认是565的一个像素占2字节
/** The default value for DecodeFormat. */
public static final DecodeFormat DEFAULT = PREFER_RGB_565;

再来看下GlideModule怎么加载的,下面这个是Glide的创建
/**
 * Get the singleton.
 *
 * @return the singleton
 */
public static Glide get(Context context) {
    if (glide == null) {
        synchronized (Glide.class) {
            if (glide == null) {
                Context applicationContext = context.getApplicationContext();
                List<GlideModule> modules = new ManifestParser(applicationContext).parse();

                GlideBuilder builder = new GlideBuilder(applicationContext);
                for (GlideModule module : modules) {
                    module.applyOptions(applicationContextbuilder);
                }
                glide = builder.createGlide();
                for (GlideModule module : modules) {
                    module.registerComponents(applicationContextglide);
                }
            }
        }
    }

    return glide;
}

这GlideModule可以有多个,在多个模块进行加载。

/**
 * Parses {@link com.bumptech.glide.module.GlideModule} references out of the AndroidManifest file.
 */
public final class ManifestParser {
    private static final String GLIDE_MODULE_VALUE "GlideModule";

    private final Context context;

    public ManifestParser(Context context) {
        this.context = context;
    }

    public List<GlideModule> parse() {
        List<GlideModule> modules = new ArrayList<GlideModule>();
        try {
            ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
                    context.getPackageName()PackageManager.GET_META_DATA);
            if (appInfo.metaData != null) {
                for (String key : appInfo.metaData.keySet()) {
                    if (GLIDE_MODULE_VALUE.equals(appInfo.metaData.get(key))) {
                        modules.add(parseModule(key));
                    }
                }
            }
        } catch (PackageManager.NameNotFoundException e) {
            throw new RuntimeException("Unable to find metadata to parse GlideModules"e);
        }

        return modules;
    }

    private static GlideModule parseModule(String className) {
        Class<?> clazz;
        try {
            clazz = Class.forName(className);
       catch (ClassNotFoundException e) {
            throw new IllegalArgumentException("Unable to find GlideModule implementation"e);
        }

        Object module;
        try {
            module = clazz.newInstance();
       catch (InstantiationException e) {
            throw new RuntimeException("Unable to instantiate GlideModule implementation for " + clazze);
       catch (IllegalAccessException e) {
            throw new RuntimeException("Unable to instantiate GlideModule implementation for " + clazze);
        }

        if (!(module instanceof GlideModule)) {
            throw new RuntimeException("Expected instanceof GlideModule, but found: " + module);
        }
        return (GlideModule) module;
    }
}

这个是对Module进行获取信息和转换的处理。
0 0
原创粉丝点击