图片加载框架Glide解析

对比Picasso内存占用

Glide是一个高效、开源、 Android设备上的媒体管理框架,它遵循BSD、MIT以及Apache 2.0协议发布。Glide具有获取、解码和展示视频剧照、图片、动画等功能,它还有灵活的API,这些API使开发者能够将Glide应用在几乎任何网络协议栈里。创建Glide的主要目的有两个,一个是实现平滑的图片列表滚动效果,另一个是支持远程图片的获取、大小调整和展示。近日,Glide 3.0发布,现已提供jar包下载,同时还支持使用Gradle以及Maven进行构建。该版本包括很多值得关注的新功能,如支持Gif 动画和视频剧照解码、智能的暂停和重新开始请求、支持缩略图等,具体新增功能如下如下:GIF动画的解码:通过调用Glide.with(context).load(“图片路径“)方法,GIF动画图片可以自动显示为动画效果。如果想有更多的控制,还可以使用Glide.with(context).load(“图片路径“).asBitmap()方法加载静态图片,使用Glide.with(context).load(“图片路径“).asGif()方法加载动画图片



1.本地视频剧照的解码:通过调用Glide.with(context).load(“图片路径“)方法,Glide能够支持Android设备中的所有视频剧照的加载和展示

2.缩略图的支持:为了减少在同一个view组件里同时加载多张图片的时间,可以调用Glide.with(context).load(“图片路径“).thumbnail(“缩略比例“).into(“view组件“)方法加载一个缩略图,还可以控制thumbnail()中的参数的大小,以控制显示不同比例大小的缩略图

3.Activity生命周期的集成:当Activity暂停和重启时,Glide能够做到智能的暂停和重新开始请求,并且当Android设备的连接状态变化时,所有失败的请求能够自动重新请求
4.转码的支持:Glide的toBytes() 和transcode() 两个方法可以用来获取、解码和变换背景图片,并且transcode() 方法还能够改变图片的样式
5.动画的支持:新增支持图片的淡入淡出动画效果(调用crossFade()方法)和查看动画的属性的功能
6.OkHttp和Volley的支持:默认选择HttpUrlConnection作为网络协议栈,还可以选择OkHttp和Volley作为网络协议

7.其他功能:如在图片加载过程中,使用Drawables对象作为占位符、图片请求的优化、图片的宽度和高度可重新设定、缩略图和原图的缓存等功能


Glide.class


利用这个方法可以获得一个RequestManager对象.


RequestManager.class

这个类主要是用来处理低内存的时候的处理;管理请求,比如请求的开始,暂停,结束;还有通过加载方法获得一个DrawableTypeRequest对象。

低内存处理方法:


管理请求的方法:与activity或者fragment的生命周期同步。


加载的方法:


load方法都是设置了一个泛型,最后都调用了:

DrawableRequestBuilder.class

[html]  view plain  copy
  1. @Override  
  2.    public DrawableRequestBuilder<ModelType> load(ModelType model) {  
  3.        super.load(model);  
  4.        return this;  
  5.    }  
接着调用了GenericRequestBuilder类里面的方法,就是设置加载的参数到GenericRequestBuilder的this.model里:

GenericRequestBuilder.class

[html]  view plain  copy
  1. public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> load(ModelType model) {  
  2.        this.model = model;  
  3.        isModelSet = true;  
  4.        return this;  
  5.    }  


这里接触了两个类,一个是GenericRequestBuilder,一个是DrawableRequestBuilder。

由GenericRequestBuilder得四种泛型参数可以看出, 这个类提供了加载各种资源的方法,是各种资源请求构建类的父类:


通过以上方法来加载缩略图。



第一个是对原数据的解码。

第二个是对磁盘缓存的数据进行解码。

第三个是对请求的数据进行编码,然后存到缓存里,之后从缓存中拿出的时候要通过解码。


设置磁盘缓存的内容,DiskCacheStrategy类是个枚举类,他提供了四个参数:

[html]  view plain  copy
  1. /** Caches with both {@link #SOURCE} and {@link #RESULT}. */  
  2.     ALL(true, true),  
  3.     /** Saves no data to cache. */  
  4.     NONE(false, false),  
  5.     /** Saves just the original data to cache. */  
  6.     SOURCE(true, false),  
  7.     /** Saves the media item after all transformations to cache. */  
  8.     RESULT(false, true);  
all:缓存源资源和转换后的资源

none:不作任何磁盘缓存

source:缓存源资源

result:缓存转换后的资源

[html]  view plain  copy
  1. priority(Priority priority)  
设置当次请求的优先级。

[html]  view plain  copy
  1. transform(Transformation<ResourceType>... transformations)  
设置对资源进行转换的接口:

对于加载一般的图片到Imageview中,默认提供了CenterCrop和FitCenter两种实现。

[html]  view plain  copy
  1. dontTransform()  
移除当前的转换。

[html]  view plain  copy
  1. transcoder(ResourceTranscoder<ResourceType, TranscodeType> transcoder)  


设置资源加载完成后的动画,不包括从内存缓存中获取。

[html]  view plain  copy
  1. dontAnimate()  
移除设置的动画。

[html]  view plain  copy
  1. placeholder(int resourceId)  
  2. placeholder(Drawable drawable)  
设置加载的时候的图片。

[html]  view plain  copy
  1. error(int resourceId)  
  2. error(Drawable drawable)  
设置加载失败后显示的图片

[html]  view plain  copy
  1. listener(RequestListener<? super ModelType, TranscodeType> requestListener)  
设置加载监听。

这个监听接口中有两个回调方法:

[html]  view plain  copy
  1. skipMemoryCache(boolean skip)  
设置是否跳过内存缓存。

[html]  view plain  copy
  1. override(int width, int height)  
设置加载资源图片的像素宽高。

[html]  view plain  copy
  1. signature(Key signature)  


[html]  view plain  copy
  1. load(ModelType model)  
设置要加载的内容。

通过上面的方法开启加载,中间的方法直接加载到view上,前后的方法用于预加载。


预加载,真正调用的是上面的两个into方法。


接着看子类DrawableRequestBuilder里面特有的方法:

DrawableRequestBuilder类不仅继承了父类GenericRequestBuilder里面的方法,还显示了BitmapOptions和DrawableOptions两个接口:



一个是控制bitmap的显示的位置或者大小(就是bitmap的转换),一个是控制图片显示出来时候的动画效果(不包括从内存中获取的图片资源)。接着看这些方法的具体实现:

[html]  view plain  copy
  1. public DrawableRequestBuilder<ModelType> centerCrop() {  
  2.        return transform(glide.getDrawableCenterCrop());  
  3.    }  
[html]  view plain  copy
  1. public DrawableRequestBuilder<ModelType> fitCenter() {  
  2.         return transform(glide.getDrawableFitCenter());  
  3.     }  
将Glide中两个BitmapTransformation进行包装后,通过间接调用父类的transform设置到父类的transformation属性中。

[html]  view plain  copy
  1. public final DrawableRequestBuilder<ModelType> crossFade() {  
  2.         super.animate(new DrawableCrossFadeFactory<GlideDrawable>());  
  3.         return this;  
  4.     }  

而设置淡入淡出效果其实是设置了默认动画实现的DrawableCrossFadeFactory类到父类animationFactory属性中。


[html]  view plain  copy
  1. public <Y extends Target<TranscodeType>> Y into(Y target) {  
  2.        Util.assertMainThread();  
  3.        if (target == null) {  
  4.            throw new IllegalArgumentException("You must pass in a non null Target");  
  5.        }  
  6.        if (!isModelSet) {  
  7.            throw new IllegalArgumentException("You must first set a model (try #load())");  
  8.        }  
  9.   
  10.        Request previous = target.getRequest();  
  11.   
  12.        if (previous != null) {  
  13.            previous.clear();  
  14.            requestTracker.removeRequest(previous);  
  15.            previous.recycle();  
  16.        }  
  17.   
  18.        Request request = buildRequest(target);  
  19.        target.setRequest(request);  
  20.        lifecycle.addListener(target);  
  21.        requestTracker.runRequest(request);  
  22.   
  23.        return target;  
  24.    }  
这里构建好request的是个GenericRequest类型,然后就通过requestTracker.runRequest(request);来调用GenericRequest里的begin方法:

[html]  view plain  copy
  1. @Override  
  2.   public void begin() {  
  3.       startTime = LogTime.getLogTime();  
  4.       if (model == null) {  
  5.           onException(null);  
  6.           return;  
  7.       }  
  8.   
  9.       status = Status.WAITING_FOR_SIZE;  
  10.       if (Util.isValidDimensions(overrideWidth, overrideHeight)) {  
  11.           onSizeReady(overrideWidth, overrideHeight);  
  12.       } else {  
  13.           target.getSize(this);  
  14.       }  
  15.   
  16.       if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {  
  17.           target.onLoadStarted(getPlaceholderDrawable());  
  18.       }  
  19.       if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  20.           logV("finished run method in " + LogTime.getElapsedMillis(startTime));  
  21.       }  
  22.   }  
这里的target.getSize(this);最终也是调用onSizeReady()方法:

[html]  view plain  copy
  1. public void onSizeReady(int width, int height) {  
  2.        if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  3.            logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));  
  4.        }  
  5.        if (status != Status.WAITING_FOR_SIZE) {  
  6.            return;  
  7.        }  
  8.        status = Status.RUNNING;  
  9.   
  10.        width = Math.round(sizeMultiplier * width);  
  11.        height = Math.round(sizeMultiplier * height);  
  12.   
  13.        ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();  
  14.        final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);  
  15.   
  16.        if (dataFetcher == null) {  
  17.            onException(new Exception("Got null fetcher from model loader"));  
  18.            return;  
  19.        }  
  20.        ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();  
  21.        if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  22.            logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));  
  23.        }  
  24.        loadedFromMemoryCache = true;  
  25.        loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,  
  26.                priority, isMemoryCacheable, diskCacheStrategy, this);  
  27.        loadedFromMemoryCache = resource != null;  
  28.        if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  29.            logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));  
  30.        }  
  31.    }  
接着调用了Engine类的load方法,这个类就是加载数据的引擎类:

[html]  view plain  copy
  1. public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,  
  2.            DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,  
  3.            Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {  
  4.        Util.assertMainThread();  
  5.        long startTime = LogTime.getLogTime();  
  6.   
  7.        final String id = fetcher.getId();  
  8.        EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),  
  9.                loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),  
  10.                transcoder, loadProvider.getSourceEncoder());  
  11.   
  12.        EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);  
  13.        if (cached != null) {  
  14.            cb.onResourceReady(cached);  
  15.            if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  16.                logWithTimeAndKey("Loaded resource from cache", startTime, key);  
  17.            }  
  18.            return null;  
  19.        }  
  20.   
  21.        EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);  
  22.        if (active != null) {  
  23.            cb.onResourceReady(active);  
  24.            if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  25.                logWithTimeAndKey("Loaded resource from active resources", startTime, key);  
  26.            }  
  27.            return null;  
  28.        }  
  29.   
  30.        EngineJob current = jobs.get(key);  
  31.        if (current != null) {  
  32.            current.addCallback(cb);  
  33.            if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  34.                logWithTimeAndKey("Added to existing load", startTime, key);  
  35.            }  
  36.            return new LoadStatus(cb, current);  
  37.        }  
  38.   
  39.        EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);  
  40.        DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,  
  41.                transcoder, diskCacheProvider, diskCacheStrategy, priority);  
  42.        EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);  
  43.        jobs.put(key, engineJob);  
  44.        engineJob.addCallback(cb);  
  45.        engineJob.start(runnable);  
  46.   
  47.        if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  48.            logWithTimeAndKey("Started new load", startTime, key);  
  49.        }  
  50.        return new LoadStatus(cb, engineJob);  
  51.    }  
这个方法会先从内存缓存中去获取,没有就构建一个EngineJob引擎任务类,这个类会将一个任务(runnable对象)提交到线程池中,然后线程池经过一系列的调度,调用了EngineRunnable对象的run方法:

[html]  view plain  copy
  1. public void run() {  
  2.     if (isCancelled) {  
  3.         return;  
  4.     }  
  5.   
  6.     Exception exception = null;  
  7.     Resource<?> resource = null;  
  8.     try {  
  9.         resource = decode();  
  10.     } catch (Exception e) {  
  11.         if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  12.             Log.v(TAG, "Exception decoding", e);  
  13.         }  
  14.         exception = e;  
  15.     }  
  16.   
  17.     if (isCancelled) {  
  18.         if (resource != null) {  
  19.             resource.recycle();  
  20.         }  
  21.         return;  
  22.     }  
  23.   
  24.     if (resource == null) {  
  25.         onLoadFailed(exception);  
  26.     } else {  
  27.         onLoadComplete(resource);  
  28.     }  
  29. }  
这里的decode()方法会先从磁盘缓存中去取数据,如果没有就源资源(网络中下载的资源)中获取:

[html]  view plain  copy
  1. private Resource<?> decode() throws Exception {  
  2.        if (isDecodingFromCache()) {  
  3.            return decodeFromCache();  
  4.        } else {  
  5.            return decodeFromSource();  
  6.        }  
  7.    }  
1.首先来看从磁盘缓存中获取资源:
[html]  view plain  copy
  1. private Resource<?> decodeFromCache() throws Exception {  
  2.        Resource<?> result = null;  
  3.        try {  
  4.            result = decodeJob.decodeResultFromCache();  
  5.        } catch (Exception e) {  
  6.            if (Log.isLoggable(TAG, Log.DEBUG)) {  
  7.                Log.d(TAG, "Exception decoding result from cache: " + e);  
  8.            }  
  9.        }  
  10.   
  11.        if (result == null) {  
  12.            result = decodeJob.decodeSourceFromCache();  
  13.        }  
  14.        return result;  
  15.    }  
这里有两个方法:第一个,decodeJob.decodeResultFromCache(),如果设置缓存的是DiskCacheStrategy.RESULT,那么磁盘缓存中缓存的是transformed后的结果,所以就会调用该方法:

[html]  view plain  copy
  1. public Resource<Z> decodeResultFromCache() throws Exception {  
  2.       if (!diskCacheStrategy.cacheResult()) {  
  3.           return null;  
  4.       }  
  5.   
  6.       long startTime = LogTime.getLogTime();  
  7.       Resource<T> transformed = loadFromCache(resultKey);  
  8.       if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  9.           logWithTimeAndKey("Decoded transformed from cache", startTime);  
  10.       }  
  11.       startTime = LogTime.getLogTime();  
  12.       Resource<Z> result = transcode(transformed);  
  13.       if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  14.           logWithTimeAndKey("Transcoded transformed from cache", startTime);  
  15.       }  
  16.       return result;  
  17.   }  
这里首先通过loadFromCache(resultKey)方法从磁盘缓存文件中decode已经经过transformed的资源(默认是使用UnitTransformation,在这里面没有做任何的资源转换),然后调用transcode(transformed)方法:该方法是把从磁盘decode出来的资源通过transcoder.transcode(transformed)来进行转换(这个方法是用来转换资源的类型用的,默认是使用UnitTranscoder,没有做任何操作)

第二个,decodeJob.decodeSourceFromCache(),如果设置缓存的是DiskCacheStrategy.SOURCE,那么磁盘缓存中缓存的是原始数据,所以就会调用该方法:

[html]  view plain  copy
  1. public Resource<Z> decodeSourceFromCache() throws Exception {  
  2.         if (!diskCacheStrategy.cacheSource()) {  
  3.             return null;  
  4.         }  
  5.   
  6.         long startTime = LogTime.getLogTime();  
  7.         Resource<T> decoded = loadFromCache(resultKey.getOriginalKey());  
  8.         if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  9.             logWithTimeAndKey("Decoded source from cache", startTime);  
  10.         }  
  11.         return transformEncodeAndTranscode(decoded);  
  12.     }  
这里首先从磁盘缓存中decode原数据,然后再调用transformEncodeAndTranscode(decoded)方法先进行transform,transform后会通过writeTransformedToCache(transformed);将经过转换的资源存入磁盘缓存,然后再进行transcode:

[html]  view plain  copy
  1. private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {  
  2.        long startTime = LogTime.getLogTime();  
  3.        Resource<T> transformed = transform(decoded);  
  4.        if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  5.            logWithTimeAndKey("Transformed resource from source", startTime);  
  6.        }  
  7.   
  8.        writeTransformedToCache(transformed);  
  9.   
  10.        startTime = LogTime.getLogTime();  
  11.        Resource<Z> result = transcode(transformed);  
  12.        if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  13.            logWithTimeAndKey("Transcoded transformed from source", startTime);  
  14.        }  
  15.        return result;  
  16.    }  
2.再来看从源资源获取资源:

[html]  view plain  copy
  1. private Resource<?> decodeFromSource() throws Exception {  
  2.         return decodeJob.decodeFromSource();  
  3.     }  

[html]  view plain  copy
  1. public Resource<Z> decodeFromSource() throws Exception {  
  2.        Resource<T> decoded = decodeSource();  
  3.        return transformEncodeAndTranscode(decoded);  
  4.    }  
这里调用了decodeSource(),这个方法是从网络中获取data资源:

[html]  view plain  copy
  1. private Resource<T> decodeSource() throws Exception {  
  2.        Resource<T> decoded = null;  
  3.        try {  
  4.            long startTime = LogTime.getLogTime();  
  5.            final A data = fetcher.loadData(priority);  
  6.            if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  7.                logWithTimeAndKey("Fetched data", startTime);  
  8.            }  
  9.            if (isCancelled) {  
  10.                return null;  
  11.            }  
  12.            decoded = decodeFromSourceData(data);  
  13.        } finally {  
  14.            fetcher.cleanup();  
  15.        }  
  16.        return decoded;  
  17.    }  
获取到网络资源后调用decodeFromSourceData(data)方法:

[html]  view plain  copy
  1. private Resource<T> decodeFromSourceData(A data) throws IOException {  
  2.         final Resource<T> decoded;  
  3.         if (diskCacheStrategy.cacheSource()) {  
  4.             decoded = cacheAndDecodeSourceData(data);  
  5.         } else {  
  6.             long startTime = LogTime.getLogTime();  
  7.             decoded = loadProvider.getSourceDecoder().decode(data, width, height);  
  8.             if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  9.                 logWithTimeAndKey("Decoded from source", startTime);  
  10.             }  
  11.         }  
  12.         return decoded;  
  13.     }  
这个方法里通过diskCacheStrategy.cacheSource()判断资源是否是将源数据保存在磁盘里,如果是则会调用cacheAndDecodeSourceData(A data):

[html]  view plain  copy
  1. private Resource<T> cacheAndDecodeSourceData(A data) throws IOException {  
  2.         long startTime = LogTime.getLogTime();  
  3.         SourceWriter<A> writer = new SourceWriter<A>(loadProvider.getSourceEncoder(), data);  
  4.         diskCacheProvider.getDiskCache().put(resultKey.getOriginalKey(), writer);  
  5.         if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  6.             logWithTimeAndKey("Wrote source to cache", startTime);  
  7.         }  
  8.   
  9.         startTime = LogTime.getLogTime();  
  10.         Resource<T> result = loadFromCache(resultKey.getOriginalKey());  
  11.         if (Log.isLoggable(TAG, Log.VERBOSE) && result != null) {  
  12.             logWithTimeAndKey("Decoded source from cache", startTime);  
  13.         }  
  14.         return result;  
  15.     }  
这里先将原数据保存在磁盘缓存里,然后再decode出来。

但是如果不需要保存源数据,就直接decode源数据:

[html]  view plain  copy
  1. private Resource<T> decodeFromSourceData(A data) throws IOException {  
  2.        final Resource<T> decoded;  
  3.        if (diskCacheStrategy.cacheSource()) {  
  4.            decoded = cacheAndDecodeSourceData(data);  
  5.        } else {  
  6.            long startTime = LogTime.getLogTime();  
  7.            decoded = loadProvider.getSourceDecoder().decode(data, width, height);  
  8.            if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  9.                logWithTimeAndKey("Decoded from source", startTime);  
  10.            }  
  11.        }  
  12.        return decoded;  
  13.    }  


decodeSource()方法执行完之后,接着执行transformEncodeAndTranscode(decoded):

[html]  view plain  copy
  1. private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {  
  2.        long startTime = LogTime.getLogTime();  
  3.        Resource<T> transformed = transform(decoded);  
  4.        if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  5.            logWithTimeAndKey("Transformed resource from source", startTime);  
  6.        }  
  7.   
  8.        writeTransformedToCache(transformed);  
  9.   
  10.        startTime = LogTime.getLogTime();  
  11.        Resource<Z> result = transcode(transformed);  
  12.        if (Log.isLoggable(TAG, Log.VERBOSE)) {  
  13.            logWithTimeAndKey("Transcoded transformed from source", startTime);  
  14.        }  
  15.        return result;  
  16.    }  

这里是将直接从源数据decode出来资源后,经过transform的转换,然后将transform后的资源存进磁盘缓存,然后在经过transcode方法来进行资源类型的转换(默认是使用UnitTranscoder,没有做任何操作)。还是提供逻辑图,一目了然吧:


这个是基本流程图:

使用方法:

1.设置的不是imageview,可以用SimpleTarget来接受,然后在回调里面设置view的现实(但是缺少目标view的设置,似乎在列表中会出现错乱)

[html]  view plain  copy
  1. Glide.with(StaggeredGridViewDemo.this).load(mImages.get(arg0).getPic()).asBitmap().override(250, 250).into(new SimpleTarget<Bitmap>() {  
  2.   
  3.                 @Override  
  4.                 public void onResourceReady(Bitmap arg0, GlideAnimation<? super Bitmap> arg1) {  
  5.                     Log.i("llj", "arg0.getWidth()+arg0.getHeight()" + arg0.getWidth() + arg0.getHeight());  
  6.                      holder.textView.setBackgroundDrawable(new BitmapDrawable(arg0));  
  7.                 }  
  8.             });  
但是override(250, 250)方法没有起作用:

也可以通过SimpleTarget的构造函数传参数:

[html]  view plain  copy
  1. Glide.with(StaggeredGridViewDemo.this).load(mImages.get(arg0).getPic()).asBitmap().into(new SimpleTarget<Bitmap>(250, 250) {  
  2.   
  3.                 @Override  
  4.                 public void onResourceReady(Bitmap arg0, GlideAnimation<? super Bitmap> arg1) {  
  5.                     Log.i("llj", "arg0.getWidth()+arg0.getHeight()" + arg0.getWidth() + ";" + arg0.getHeight());  
  6.                     // holder.textView.setBackgroundDrawable(new BitmapDrawable(arg0));  
  7.                 }  
  8.             });  

但是SimpleTarget的构造函数穿进去的参数也没有起作用:

2.设置预加载:(可以提前将资源加载到磁盘缓存中,默认的缓存目录是data\data下的250M空间,自己可以根据需求更改)

[html]  view plain  copy
  1. final PreloadTarget<Bitmap> target = PreloadTarget.obtain(250, 250);  
  2.             Glide.with(StaggeredGridViewDemo.this).load(mImages.get(arg0).getPic()).asBitmap().listener(new RequestListener<String, Bitmap>() {  
  3.   
  4.                 @Override  
  5.                 public boolean onException(Exception e, String model, Target<Bitmap> target, boolean isFirstResource) {  
  6.   
  7.                     return false;  
  8.                 }  
  9.   
  10.                 @Override  
  11.                 public boolean onResourceReady(Bitmap resource, String model, Target<Bitmap> target, boolean isFromMemoryCache, boolean isFirstResource) {  
  12.                     Log.i("llj", "arg0.getWidth()+arg0.getHeight()" + resource.getWidth() + resource.getHeight());  
  13.                     return false;  
  14.                 }  
  15.             }).into(target);  
这里在PreloadTarget中设置的像素的缩放还是没有用:

[html]  view plain  copy
  1. final PreloadTarget<Bitmap> target = PreloadTarget.obtain(0, 0);  
  2.             Glide.with(StaggeredGridViewDemo.this).load(mImages.get(arg0).getPic()).asBitmap().override(250, 250).listener(new RequestListener<String, Bitmap>() {  
  3.   
  4.                 @Override  
  5.                 public boolean onException(Exception e, String model, Target<Bitmap> target, boolean isFirstResource) {  
  6.   
  7.                     return false;  
  8.                 }  
  9.   
  10.                 @Override  
  11.                 public boolean onResourceReady(Bitmap resource, String model, Target<Bitmap> target, boolean isFromMemoryCache, boolean isFirstResource) {  
  12.                     Log.i("llj", "arg0.getWidth()+arg0.getHeight()" + resource.getWidth() + resource.getHeight());  
  13.                     return false;  
  14.                 }  
  15.             }).into(target);  
这里在override(250, 250)设置依然没有作用:

3.加载到imageview上

[html]  view plain  copy
  1. Glide.with(StaggeredGridViewDemo.this).load(mImages.get(arg0).getPic()).asBitmap().override(250, 250).listener(new RequestListener<String, Bitmap>() {  
  2.   
  3.                 @Override  
  4.                 public boolean onException(Exception e, String model, Target<Bitmap> target, boolean isFirstResource) {  
  5.   
  6.                     return false;  
  7.                 }  
  8.   
  9.                 @Override  
  10.                 public boolean onResourceReady(Bitmap resource, String model, Target<Bitmap> target, boolean isFromMemoryCache, boolean isFirstResource) {  
  11.                     Log.i("llj", "arg0.getWidth()+arg0.getHeight()" + resource.getWidth() + resource.getHeight());  
  12.                     return false;  
  13.                 }  
  14.             }).into(holder.imageview);  
拿到的Bitmap的宽高经过了转换,但转换后的比例并不是250:250:

但是如果设置了centerCrop():



而用Picasso转换后的图片是250:250:

[html]  view plain  copy
  1. Picasso.with(StaggeredGridViewDemo.this).load(mImages.get(arg0).getPic()).resize(250, 250).into(new com.squareup.picasso.Target() {  
  2.   
  3.                 @Override  
  4.                 public void onPrepareLoad(Drawable arg0) {  
  5.   
  6.                 }  
  7.   
  8.                 @Override  
  9.                 public void onBitmapLoaded(Bitmap arg0, LoadedFrom arg1) {  
  10.                     Log.i("llj", "arg0.getWidth()+arg0.getHeight()" + arg0.getWidth() + ";" + arg0.getHeight());  
  11.                 }  
  12.   
  13.                 @Override  
  14.                 public void onBitmapFailed(Drawable arg0) {  
  15.   
  16.                 }  
  17.             });  

说明两者的按比例缩放的方法不一样!



自定义缓存

好吧,既然是定制 Glide,我们就需要创建一个新的 Glide module。就如你在以前博客中看到的那样, applyOptions 方法使我们获取了 GlideBuilder 对象。该GlideBuilder 为我们提供了几个方法去定制 Glide 的缓存。首先,来看看内存缓存。

内存缓存是在设备的 RAM 中去维护图片的。这里没有 IO 行为,所以这个操作是很快的。另一方面是 RAM(内存) 的大小是非常有限的。寻找一个大内存缓存的平衡点(大量图像空间)与一个小内存缓存(最大限度减少我们 App 的资源消耗)并不容易。Glide 内部使用了 MemorySizeCalculator 类去决定内存缓存大小以及 bitmap 的缓存池。bitmap 池维护了你 App 的堆中的图像分配。正确的 bitmpa 池是非常必要的,因为它避免很多的图像重复回收,这样可以确保垃圾回收器的管理更加合理。

幸运的是,你已经得到了 Glide 的 MemorySizeCalculator 类以及默认的计算:

MemorySizeCalculator calculator = new MemorySizeCalculator(context);  
int defaultMemoryCacheSize = calculator.getMemoryCacheSize();  
int defaultBitmapPoolSize = calculator.getBitmapPoolSize();  

上面这段代码相当有用,如果我们想要用默认值作为基准,然后调整它。比如,如果你认为你的 app 需要 20% 大的缓存作为 Glide 的默认值,用我们上面的变量去计算他们:

int customMemoryCacheSize = (int) (1.2 * defaultMemoryCacheSize);  
int customBitmapPoolSize = (int) (1.2 * defaultBitmapPoolSize);  

因为我们已经计算出了我们的内存缓存和 bitmap 池的大小,我们可以在我们的 Glide module 代码里去得到。在 applyOptions() 方法中,我们可以在GlideBuilder 对象中调用相应的方法。

public class CustomCachingGlideModule implements GlideModule {  
    @Override public void applyOptions(Context context, GlideBuilder builder) {
        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 );
    }

    @Override public void registerComponents(Context context, Glide glide) {
        // nothing to do here
    }
}

正如你看到的,在 applyOptions() 方法的最后两行,我们不能直接设置大小。我们需要创建一个 LruResourceCache 和 LruBitmapPool 的实例。这两个都是 Glide 的默认实现。因此,如果你仅仅想要调整大小,就可以继续使用它们通过传两个不同的大小的值给构造函数。

自定义磁盘缓存

调整磁盘缓存和和刚才的很像,但是我们有一个更大的决定去做,磁盘缓存可以位于应用的私有目录(换句话说,除了它自己,没有别的应用可以访问)。否则,磁盘缓存也可以位于外部存储,公有目录(更多信息,请看 Storage Options )。不能一起设置这两个为之。Glide 为这两个选项都提供了它的实现:InternalCacheDiskCacheFactory 和 ExternalCacheDiskCacheFactory。就像内存缓存的构造函数一样,在它们的构造函数内都传一个磁盘缓存的工厂类:

public class CustomCachingGlideModule implements GlideModule {  
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        // set size & external vs. internal
        int cacheSize100MegaBytes = 104857600;

        builder.setDiskCache(
            new InternalCacheDiskCacheFactory(context, cacheSize100MegaBytes)
        );

        //builder.setDiskCache(
        //new ExternalCacheDiskCacheFactory(context, cacheSize100MegaBytes));
    }

    @Override
    public void registerComponents(Context context, Glide glide) {
        // nothing to do here
    }
}

上面的代码将设置磁盘缓存到应用的内部目录,并且设置了最大的大小为 100M。下面注释的那行代码会设置磁盘缓存到外部存储(也设置了最大大小为 100M)。

这两个选项都不让你选一个特点的目录。如果你要让磁盘缓存到指定的目录,你要使用 DiskLruCacheFactory :

// or any other path
String downloadDirectoryPath = Environment.getDownloadCacheDirectory().getPath(); 

builder.setDiskCache(  
        new DiskLruCacheFactory( downloadDirectoryPath, cacheSize100MegaBytes )
);

// In case you want to specify a cache sub folder (i.e. "glidecache"):
//builder.setDiskCache(
//    new DiskLruCacheFactory( downloadDirectoryPath, "glidecache", cacheSize100MegaBytes ) 
//);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值