对比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()方法加载动画图片
2.缩略图的支持:为了减少在同一个view组件里同时加载多张图片的时间,可以调用Glide.with(context).load(“图片路径“).thumbnail(“缩略比例“).into(“view组件“)方法加载一个缩略图,还可以控制thumbnail()中的参数的大小,以控制显示不同比例大小的缩略图
3.Activity生命周期的集成:当Activity暂停和重启时,Glide能够做到智能的暂停和重新开始请求,并且当Android设备的连接状态变化时,所有失败的请求能够自动重新请求7.其他功能:如在图片加载过程中,使用Drawables对象作为占位符、图片请求的优化、图片的宽度和高度可重新设定、缩略图和原图的缓存等功能
Glide.class
利用这个方法可以获得一个RequestManager对象.
RequestManager.class
这个类主要是用来处理低内存的时候的处理;管理请求,比如请求的开始,暂停,结束;还有通过加载方法获得一个DrawableTypeRequest对象。
低内存处理方法:
管理请求的方法:与activity或者fragment的生命周期同步。
加载的方法:
load方法都是设置了一个泛型,最后都调用了:
DrawableRequestBuilder.class
@Override
public DrawableRequestBuilder load(ModelType model) {
super.load(model);
return this;
}
接着调用了GenericRequestBuilder类里面的方法,就是设置加载的参数到GenericRequestBuilder的this.model里:
GenericRequestBuilder.class
public GenericRequestBuilder load(ModelType model) {
this.model = model;
isModelSet = true;
return this;
}
由GenericRequestBuilder得四种泛型参数可以看出, 这个类提供了加载各种资源的方法,是各种资源请求构建类的父类:
通过以上方法来加载缩略图。
第一个是对原数据的解码。
第二个是对磁盘缓存的数据进行解码。
第三个是对请求的数据进行编码,然后存到缓存里,之后从缓存中拿出的时候要通过解码。
设置磁盘缓存的内容,DiskCacheStrategy类是个枚举类,他提供了四个参数:
/** Caches with both {@link #SOURCE} and {@link #RESULT}. */
ALL(true, true),
/** Saves no data to cache. */
NONE(false, false),
/** Saves just the original data to cache. */
SOURCE(true, false),
/** Saves the media item after all transformations to cache. */
RESULT(false, true);
all:缓存源资源和转换后的资源
none:不作任何磁盘缓存
source:缓存源资源
result:缓存转换后的资源
priority(Priority priority)
设置当次请求的优先级。
transform(Transformation... transformations)
设置对资源进行转换的接口:
对于加载一般的图片到Imageview中,默认提供了CenterCrop和FitCenter两种实现。
dontTransform()
移除当前的转换。
transcoder(ResourceTranscoder transcoder)
设置资源加载完成后的动画,不包括从内存缓存中获取。
dontAnimate()
移除设置的动画。
placeholder(int resourceId)
placeholder(Drawable drawable)
设置加载的时候的图片。
error(int resourceId)
error(Drawable drawable)
设置加载失败后显示的图片
listener(RequestListener super ModelType, TranscodeType> requestListener)
设置加载监听。
这个监听接口中有两个回调方法:
skipMemoryCache(boolean skip)
设置是否跳过内存缓存。
override(int width, int height)
设置加载资源图片的像素宽高。
signature(Key signature)
load(ModelType model)
设置要加载的内容。
通过上面的方法开启加载,中间的方法直接加载到view上,前后的方法用于预加载。
预加载,真正调用的是上面的两个into方法。
接着看子类DrawableRequestBuilder里面特有的方法:
DrawableRequestBuilder类不仅继承了父类GenericRequestBuilder里面的方法,还显示了BitmapOptions和DrawableOptions两个接口:
一个是控制bitmap的显示的位置或者大小(就是bitmap的转换),一个是控制图片显示出来时候的动画效果(不包括从内存中获取的图片资源)。接着看这些方法的具体实现:
public DrawableRequestBuilder centerCrop() {
return transform(glide.getDrawableCenterCrop());
}
public DrawableRequestBuilder fitCenter() {
return transform(glide.getDrawableFitCenter());
}
将Glide中两个BitmapTransformation进行包装后,通过间接调用父类的transform设置到父类的transformation属性中。
public final DrawableRequestBuilder crossFade() {
super.animate(new DrawableCrossFadeFactory());
return this;
}
而设置淡入淡出效果其实是设置了默认动画实现的DrawableCrossFadeFactory类到父类animationFactory属性中。
public > Y into(Y target) {
Util.assertMainThread();
if (target == null) {
throw new IllegalArgumentException("You must pass in a non null Target");
}
if (!isModelSet) {
throw new IllegalArgumentException("You must first set a model (try #load())");
}
Request previous = target.getRequest();
if (previous != null) {
previous.clear();
requestTracker.removeRequest(previous);
previous.recycle();
}
Request request = buildRequest(target);
target.setRequest(request);
lifecycle.addListener(target);
requestTracker.runRequest(request);
return target;
}
这里构建好request的是个GenericRequest类型,然后就通过requestTracker.runRequest(request);来调用GenericRequest里的begin方法:
@Override
public void begin() {
startTime = LogTime.getLogTime();
if (model == null) {
onException(null);
return;
}
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
这里的target.getSize(this);最终也是调用onSizeReady()方法:
public void onSizeReady(int width, int height) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
width = Math.round(sizeMultiplier * width);
height = Math.round(sizeMultiplier * height);
ModelLoader modelLoader = loadProvider.getModelLoader();
final DataFetcher dataFetcher = modelLoader.getResourceFetcher(model, width, height);
if (dataFetcher == null) {
onException(new Exception("Got null fetcher from model loader"));
return;
}
ResourceTranscoder transcoder = loadProvider.getTranscoder();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
loadedFromMemoryCache = true;
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
priority, isMemoryCacheable, diskCacheStrategy, this);
loadedFromMemoryCache = resource != null;
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
接着调用了Engine类的load方法,这个类就是加载数据的引擎类:
public LoadStatus load(Key signature, int width, int height, DataFetcher fetcher,
DataLoadProvider loadProvider, Transformation transformation, ResourceTranscoder transcoder,
Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
final String id = fetcher.getId();
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
EngineResource> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
EngineResource> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
EngineJob current = jobs.get(key);
if (current != null) {
current.addCallback(cb);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
DecodeJob decodeJob = new DecodeJob(key, width, height, fetcher, loadProvider, transformation,
transcoder, diskCacheProvider, diskCacheStrategy, priority);
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(runnable);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
这个方法会先从内存缓存中去获取,没有就构建一个EngineJob引擎任务类,这个类会将一个任务(runnable对象)提交到线程池中,然后线程池经过一系列的调度,调用了EngineRunnable对象的run方法:
public void run() {
if (isCancelled) {
return;
}
Exception exception = null;
Resource> resource = null;
try {
resource = decode();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception decoding", e);
}
exception = e;
}
if (isCancelled) {
if (resource != null) {
resource.recycle();
}
return;
}
if (resource == null) {
onLoadFailed(exception);
} else {
onLoadComplete(resource);
}
}
这里的decode()方法会先从磁盘缓存中去取数据,如果没有就源资源(网络中下载的资源)中获取:
private Resource> decode() throws Exception {
if (isDecodingFromCache()) {
return decodeFromCache();
} else {
return decodeFromSource();
}
}
1.首先来看从磁盘缓存中获取资源:
private Resource> decodeFromCache() throws Exception {
Resource> result = null;
try {
result = decodeJob.decodeResultFromCache();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Exception decoding result from cache: " + e);
}
}
if (result == null) {
result = decodeJob.decodeSourceFromCache();
}
return result;
}
这里有两个方法:第一个,decodeJob.decodeResultFromCache(),如果设置缓存的是DiskCacheStrategy.RESULT,那么磁盘缓存中缓存的是transformed后的结果,所以就会调用该方法:
public Resource decodeResultFromCache() throws Exception {
if (!diskCacheStrategy.cacheResult()) {
return null;
}
long startTime = LogTime.getLogTime();
Resource transformed = loadFromCache(resultKey);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded transformed from cache", startTime);
}
startTime = LogTime.getLogTime();
Resource result = transcode(transformed);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Transcoded transformed from cache", startTime);
}
return result;
}
这里首先通过loadFromCache(resultKey)方法从磁盘缓存文件中decode已经经过transformed的资源(默认是使用UnitTransformation,在这里面没有做任何的资源转换),然后调用transcode(transformed)方法:该方法是把从磁盘decode出来的资源通过transcoder.transcode(transformed)来进行转换(这个方法是用来转换资源的类型用的,默认是使用UnitTranscoder,没有做任何操作)
第二个,decodeJob.decodeSourceFromCache(),如果设置缓存的是DiskCacheStrategy.SOURCE,那么磁盘缓存中缓存的是原始数据,所以就会调用该方法:
public Resource decodeSourceFromCache() throws Exception {
if (!diskCacheStrategy.cacheSource()) {
return null;
}
long startTime = LogTime.getLogTime();
Resource decoded = loadFromCache(resultKey.getOriginalKey());
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded source from cache", startTime);
}
return transformEncodeAndTranscode(decoded);
}
这里首先从磁盘缓存中decode原数据,然后再调用transformEncodeAndTranscode(decoded)方法先进行transform,transform后会通过writeTransformedToCache(transformed);将经过转换的资源存入磁盘缓存,然后再进行transcode:
private Resource transformEncodeAndTranscode(Resource decoded) {
long startTime = LogTime.getLogTime();
Resource transformed = transform(decoded);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Transformed resource from source", startTime);
}
writeTransformedToCache(transformed);
startTime = LogTime.getLogTime();
Resource result = transcode(transformed);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Transcoded transformed from source", startTime);
}
return result;
}
2.再来看从源资源获取资源:
private Resource> decodeFromSource() throws Exception {
return decodeJob.decodeFromSource();
}
public Resource decodeFromSource() throws Exception {
Resource decoded = decodeSource();
return transformEncodeAndTranscode(decoded);
}
这里调用了decodeSource(),这个方法是从网络中获取data资源:
private Resource decodeSource() throws Exception {
Resource decoded = null;
try {
long startTime = LogTime.getLogTime();
final A data = fetcher.loadData(priority);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Fetched data", startTime);
}
if (isCancelled) {
return null;
}
decoded = decodeFromSourceData(data);
} finally {
fetcher.cleanup();
}
return decoded;
}
获取到网络资源后调用decodeFromSourceData(data)方法:
private Resource decodeFromSourceData(A data) throws IOException {
final Resource decoded;
if (diskCacheStrategy.cacheSource()) {
decoded = cacheAndDecodeSourceData(data);
} else {
long startTime = LogTime.getLogTime();
decoded = loadProvider.getSourceDecoder().decode(data, width, height);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded from source", startTime);
}
}
return decoded;
}
这个方法里通过diskCacheStrategy.cacheSource()判断资源是否是将源数据保存在磁盘里,如果是则会调用cacheAndDecodeSourceData(A data):
private Resource cacheAndDecodeSourceData(A data) throws IOException {
long startTime = LogTime.getLogTime();
SourceWriter writer = new SourceWriter(loadProvider.getSourceEncoder(), data);
diskCacheProvider.getDiskCache().put(resultKey.getOriginalKey(), writer);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Wrote source to cache", startTime);
}
startTime = LogTime.getLogTime();
Resource result = loadFromCache(resultKey.getOriginalKey());
if (Log.isLoggable(TAG, Log.VERBOSE) && result != null) {
logWithTimeAndKey("Decoded source from cache", startTime);
}
return result;
}
这里先将原数据保存在磁盘缓存里,然后再decode出来。
但是如果不需要保存源数据,就直接decode源数据:
private Resource decodeFromSourceData(A data) throws IOException {
final Resource decoded;
if (diskCacheStrategy.cacheSource()) {
decoded = cacheAndDecodeSourceData(data);
} else {
long startTime = LogTime.getLogTime();
decoded = loadProvider.getSourceDecoder().decode(data, width, height);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded from source", startTime);
}
}
return decoded;
}
private Resource transformEncodeAndTranscode(Resource decoded) {
long startTime = LogTime.getLogTime();
Resource transformed = transform(decoded);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Transformed resource from source", startTime);
}
writeTransformedToCache(transformed);
startTime = LogTime.getLogTime();
Resource result = transcode(transformed);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Transcoded transformed from source", startTime);
}
return result;
}
这里是将直接从源数据decode出来资源后,经过transform的转换,然后将transform后的资源存进磁盘缓存,然后在经过transcode方法来进行资源类型的转换(默认是使用UnitTranscoder,没有做任何操作)。还是提供逻辑图,一目了然吧:
这个是基本流程图:使用方法:
1.设置的不是imageview,可以用SimpleTarget来接受,然后在回调里面设置view的现实(但是缺少目标view的设置,似乎在列表中会出现错乱)
Glide.with(StaggeredGridViewDemo.this).load(mImages.get(arg0).getPic()).asBitmap().override(250, 250).into(new SimpleTarget() {
@Override
public void onResourceReady(Bitmap arg0, GlideAnimation super Bitmap> arg1) {
Log.i("llj", "arg0.getWidth()+arg0.getHeight()" + arg0.getWidth() + arg0.getHeight());
holder.textView.setBackgroundDrawable(new BitmapDrawable(arg0));
}
});
但是override(250, 250)方法没有起作用:
也可以通过SimpleTarget的构造函数传参数:
Glide.with(StaggeredGridViewDemo.this).load(mImages.get(arg0).getPic()).asBitmap().into(new SimpleTarget(250, 250) {
@Override
public void onResourceReady(Bitmap arg0, GlideAnimation super Bitmap> arg1) {
Log.i("llj", "arg0.getWidth()+arg0.getHeight()" + arg0.getWidth() + ";" + arg0.getHeight());
// holder.textView.setBackgroundDrawable(new BitmapDrawable(arg0));
}
});
2.设置预加载:(可以提前将资源加载到磁盘缓存中,默认的缓存目录是data\data下的250M空间,自己可以根据需求更改)
final PreloadTarget target = PreloadTarget.obtain(250, 250);
Glide.with(StaggeredGridViewDemo.this).load(mImages.get(arg0).getPic()).asBitmap().listener(new RequestListener() {
@Override
public boolean onException(Exception e, String model, Target target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Bitmap resource, String model, Target target, boolean isFromMemoryCache, boolean isFirstResource) {
Log.i("llj", "arg0.getWidth()+arg0.getHeight()" + resource.getWidth() + resource.getHeight());
return false;
}
}).into(target);
这里在PreloadTarget中设置的像素的缩放还是没有用:
final PreloadTarget target = PreloadTarget.obtain(0, 0);
Glide.with(StaggeredGridViewDemo.this).load(mImages.get(arg0).getPic()).asBitmap().override(250, 250).listener(new RequestListener() {
@Override
public boolean onException(Exception e, String model, Target target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Bitmap resource, String model, Target target, boolean isFromMemoryCache, boolean isFirstResource) {
Log.i("llj", "arg0.getWidth()+arg0.getHeight()" + resource.getWidth() + resource.getHeight());
return false;
}
}).into(target);
这里在override(250, 250)设置依然没有作用:
3.加载到imageview上
Glide.with(StaggeredGridViewDemo.this).load(mImages.get(arg0).getPic()).asBitmap().override(250, 250).listener(new RequestListener() {
@Override
public boolean onException(Exception e, String model, Target target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Bitmap resource, String model, Target target, boolean isFromMemoryCache, boolean isFirstResource) {
Log.i("llj", "arg0.getWidth()+arg0.getHeight()" + resource.getWidth() + resource.getHeight());
return false;
}
}).into(holder.imageview);
拿到的Bitmap的宽高经过了转换,但转换后的比例并不是250:250:
但是如果设置了centerCrop():
而用Picasso转换后的图片是250:250:
Picasso.with(StaggeredGridViewDemo.this).load(mImages.get(arg0).getPic()).resize(250, 250).into(new com.squareup.picasso.Target() {
@Override
public void onPrepareLoad(Drawable arg0) {
}
@Override
public void onBitmapLoaded(Bitmap arg0, LoadedFrom arg1) {
Log.i("llj", "arg0.getWidth()+arg0.getHeight()" + arg0.getWidth() + ";" + arg0.getHeight());
}
@Override
public void onBitmapFailed(Drawable arg0) {
}
});
说明两者的按比例缩放的方法不一样!