Glide之GlideModule

GlideModule接口:为了延迟配置Glide(包括用GlideBuilder设置选项,为Glide注册ModelLoader
所有的GlideModule实现类必须是public的,并且只拥有一个空的构造器,以便在Glide延迟初始化时,可以通过反射将它们实例化。注册ModelLoader时可调用Glideregister(Class<T> modelClass, Class<Y> resourceClass, ModelLoaderFactory<T, Y> factory)方法进行注册,如:

    public class MyGlideModule implements GlideModule {

        @Override
        public void applyOptions(Context context, GlideBuilder builder) {
            builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
        }

        @Override
        public void registerComponents(Context context, Glide glide) {
            glide.register(Model.class, Data.class, new MyModelLoaderFactory());
        }
    }

配置proguard.cfg以允许你的GlideModule可以通过反射实例化:

    -keepnames class * com.mypackage.MyGlideModule

或者把所有的Module都一次性配置好:

    -keep public class * implements com.bumptech.glide.module.GlideModule

然后将你的GlideModule加入AndroidManifest.xml文件中的meta-data标签,key是实现类的全限定名,value是GlideModule,如:

    <meta-data
        android:name="com.mypackage.MyGlideModule"
        android:value="GlideModule" />

如果GlideModule没有在AndroidManifest.xml中被引用,那它就不会被加载或使用。所以可以把不用的GlideModuleAndroidManifest.xml中删掉就可以了,不一定非要删除对定的java文件。
GlideModule不能指定调用顺序,所以应该避免不同的GlideModule之间有冲突的选项设置,可以考虑将所有的设置都放到一个GlideModule里面,或者排除掉某个manifest文件的某个Module,像这样:

<meta-data android:name=”com.mypackage.MyGlideModule” tools:node=”remove” />

======================================

好了,现在来看接口的第一个方法void applyOptions(Context context, GlideBuilder builder)。用来在Glide单例创建之前应用所有的选项配置,该方法每次实现只会被调用一次。通过GlideBuilder(Glide的创造者)我们可以对Glide进行各种配置:

  • setBitmapPool(BitmapPool bitmapPool).

    Bitmap池用来允许不同尺寸的Bitmap被重用,这可以显著地减少因为图片解码像素数组分配内存而引发的垃圾回收。默认情况下Glide使用LruBitmapPool作为Bitmap池,LruBitmapPool采用LRU算法保存最近使用的尺寸的Bitmap。我们可以通过它的构造器设置最大缓存内存大小:

        builder.setBitmapPool(new LruBitmapPool(sizeInBytes));
  • setMemoryCache(MemoryCache memoryCache).

    MemoryCache用来把resources 缓存在内存里,以便能马上能拿出来显示。默认情况下Glide使用LruResourceCache,我们可以通过它的构造器设置最大缓存内存大小:

        builder.setMemoryCache(new LruResourceCache(yourSizeInBytes));

    MemoryCache和BitmapPool的默认大小由MemorySizeCalculator类决定,MemorySizeCalculator会根据给定屏幕大小可用内存算出合适的缓存大小,这也是推荐的缓存大小,我们可以根据这个推荐大小做出调整:

        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));
  • setDiskCache(final DiskCache diskCache).

    设置一个用来存储Resource数据和缩略图的DiskCache实现。注意:在主线程中创建磁盘缓存目录会导致严格模式下的违规,用setDiskCache(DiskCache.Factory)替代,Glide4.0打算移除该方法。

  • setDiskCache(DiskCache.Factory diskCacheFactory).

    设置一个用来创建DiskCache的工厂。默认情况下Glide使用InternalCacheDiskCacheFactory内部工厂类创建DiskCache,缓存目录为程序内部缓存目录/data/data/your_package_name/image_manager_disk_cache/(不能被其它应用访问)且缓存最大为250MB。当然,可以通过InternalCacheDiskCacheFactory构造器更改缓存的目录和最大缓存大小,如:

        builder.setDiskCache(new InternalCacheDiskCacheFactory(context,
            cacheDirectoryName, yourSizeInBytes));

    还可以指定缓存到外部磁盘SD卡上:

        builder.setDiskCache(new ExternalCacheDiskCacheFactory(context,
            cacheDirectoryName, yourSizeInBytes));

    缓存架构的接口包括:

    • com.bumptech.glide.load.engine.cache.DiskCache
    • com.bumptech.glide.load.engine.cache.DiskCache.Factory
    • com.bumptech.glide.load.engine.cache.DiskCache.Writer
    • com.bumptech.glide.load.engine.cache.DiskLruCacheFactory.CacheDirectoryGetter
    • com.bumptech.glide.load.engine.cache.MemoryCache
    • com.bumptech.glide.load.engine.cache.MemoryCache.ResourceRemovedListener

    缓存架构的类关系为:

    • com.bumptech.glide.load.engine.cache.DiskCacheAdapter (implements com.bumptech.glide.load.engine.cache.DiskCache)
    • com.bumptech.glide.load.engine.cache.DiskLruCacheFactory (implements com.bumptech.glide.load.engine.cache.DiskCache.Factory)
      • com.bumptech.glide.load.engine.cache.ExternalCacheDiskCacheFactory
      • com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory
    • com.bumptech.glide.load.engine.cache.DiskLruCacheWrapper (implements com.bumptech.glide.load.engine.cache.DiskCache)
    • com.bumptech.glide.util.LruCache<T,Y>
      • com.bumptech.glide.load.engine.cache.LruResourceCache (implements com.bumptech.glide.load.engine.cache.MemoryCache)
    • com.bumptech.glide.load.engine.cache.MemoryCacheAdapter (implements com.bumptech.glide.load.engine.cache.MemoryCache)
    • com.bumptech.glide.load.engine.cache.MemorySizeCalculator

    因此,可以继承DiskLruCacheFactory或者直接实现DiskCache.Factory接口创建自己想要的cache:

        builder.setDiskCache(
            new DiskLruCacheFactory(getMyCacheLocationWithoutIO(), yourSizeInBytes));
    
        // 或者
    
        builder.setDiskCache(
            new DiskCache.Factory() {
                @Override
                public DiskCache build() {
                    File cacheLocation = getMyCacheLocationBlockingIO();
                    cacheLocation.mkdirs();
                    return DiskLruCacheWrapper.get(cacheLocation, yourSizeInBytes);
                }
            });
  • setResizeService(ExecutorService service).

    设置一个用来检索cache中没有的Resource的ExecutorService 实现。为了使缩略图请求正确工作,实现类必须把请求根据Priority优先级排好序。

  • setDiskCacheService(ExecutorService service).

    设置一个用来检索cache中没有的Resource的ExecutorService 实现。为了使缩略图请求正确工作,实现类必须把请求根据Priority优先级排好序。

  • setDecodeFormat(DecodeFormat decodeFormat).

    为所有的默认解码器设置解码格式。如DecodeFormat.PREFER_ARGB_8888。默认是DecodeFormat.PREFER_RGB_565,因为相对于ARGB_8888的4字节/像素可以节省一半的内存,但不支持透明度且某些图片会出现条带。

第二个方法void registerComponents(Context context, Glide glide)。用来在Glide单例创建之后但请求发起之前注册组件,该方法每次实现只会被调用一次。通常在该方法中注册ModelLoader

注意:module 英[ˈmɒdju:l] 美[ˈmɑ:dʒul] 模块,model 英[ˈmɒdl] 美[ˈmɑ:dl]模型。发音很像,但要表达的意思不同,module一般指模块,而model一般指数据模型。

ModelLoader接口可以提供给我们要载入图片的View的尺寸,并允许我们通过这个尺寸选择合适的url下载一个合适尺寸的图片,使用合适大小的图片可以节省带宽和设备存储空间,也可以提升app的表现。ModelLoader可以将任意复杂的数据模型转化为具体的可被DataFetcher使用的数据类型。该接口有两个目标:
1. 把一个具体的model转化为可解码成resource的数据类型
2. 允许与View绑定的model获取指定尺寸的resource
该接口只有一个方法getResourceFetcher(T model, int width, int height),用来获取一个DataFetcher(获取由model表示的resource要解码的数据),如果这个resource已经被缓存了,这个DataFetcher就不会被使用了,如果无法创建有效的DataFetcher,将会返回null
使用ModelLoader
如果使用http或https下载图片,可以继承ModelLoader的实现类BaseGlideUrlLoader

//表示resource的数据模型
public interface MyDataModel {
    public String buildUrl(int width, int height);
} 

public class MyUrlLoader extends BaseGlideUrlLoader<MyDataModel> {
    @Override
    protected String getUrl(MyDataModel model, int width, int height) {
        // 根据不同尺寸构造不同的url
        return model.buildUrl(width, height);
    }
}

之后用Glide加载图片时,用这个ModelLoader就可以了:

Glide.with(yourFragment)
    .using(new MyUrlLoader())
    .load(yourModel)
    .into(yourView);

2014年Google I/O大会上的App就是用的glide,其中多尺寸图片的动态加载是这样用的:
首先服务器端要存储一张图片的全尺寸和各个尺寸的资源,可以用Bash脚本自动生成各个尺寸的图片,目录像这样:

/__w-200-400-600__/session1.jpg
/w200/session1.jpg
/w400/session1.jpg
/w600/session1.jpg

其中第一个是全尺寸的图片资源。
然后,我们App就可以根据要显示的ImageView的大小请求不同的资源了,比如我们ImageView宽300px,我们就可以请求/w400/session1.jpg,总之,请求一个最接近我们ImageView宽度的图片就行,当然不能比ImageView宽度小。实现时,利用正则表达式替换一下即可:

    private static class VariableWidthImageLoader extends BaseGlideUrlLoader<String> {
        private static final Pattern PATTERN = Pattern.compile("__w-((?:-?\\d+)+)__");

        public VariableWidthImageLoader(Context context) {
            super(context, urlCache);
        }

        /**
         * If the URL contains a special variable width indicator (eg "__w-200-400-800__")
         * we get the buckets from the URL (200, 400 and 800 in the example) and replace
         * the URL with the best bucket for the requested width (the bucket immediately
         * larger than the requested width).
         */
        @Override
        protected String getUrl(String model, int width, int height) {
            Matcher m = PATTERN.matcher(model);
            int bestBucket = 0;
            if (m.find()) {
                String[] found = m.group(1).split("-");
                for (String bucketStr : found) {
                    bestBucket = Integer.parseInt(bucketStr);
                    if (bestBucket >= width) {
                        // the best bucket is the first immediately bigger than the requested width
                        break;
                    }
                }
                if (bestBucket > 0) {
                    model = m.replaceFirst("w"+bestBucket);
                }
            }
            return model;
        }
    }

如果你不想每次请求都用.using(new MyUrlLoader())这句话,可以在GlideModule中进行注册:

public class MyGlideModule implements GlideModule {

    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        buidler.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
    }  

    @Override
    public void registerComponents(Context context, Glide glide) {
        glide.register(MyDataModel.class, InputStream.class, 
            new MyUrlLoader.Factory());
    }
}

References:
1. Norman Peitek's blog
2. Glide Wiki

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值