网络加载图片对比(Fresco/Glide)
Glide 与 Picasso对比:
https://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en
项目中使用Fresco已经一年多了,碰到了一些坑, 但总体来说还是很强大。我参考了上面的对比方式,将Fresco与Glide做了类似比较
Fresco vs Glide
功能
要从功能上来说, fresco基本满足了所有的网络图片展示需求,看一下官方页面的功能目录就知道他的强大:
下面介绍一些项目中经常用到的功能,与glide中的对比
圆角, 圆形
fresco实现
public void setRoundImageSrc(SimpleDraweeView draweeView, String src, float radius){
RoundingParams roundingParams = RoundingParams.fromCornersRadius(radius);
draweeView.setHierarchy(
new GenericDraweeHierarchyBuilder(draweeView.getResources())
.setRoundingParams(roundingParams)
.build());
draweeView.setImageURI(Uri.parse(src));
}
glide实现
需要自己实现圆角,继承自BitmapTransformation操作bitmap对象实现(圆形同理):
public static class GlideRoundTransform extends BitmapTransformation {
private static float radius = 0f;
public GlideRoundTransform(Context context) {
this(context, 4);
}
public GlideRoundTransform(Context context, int dp) {
super(context);
this.radius = Resources.getSystem().getDisplayMetrics().density * dp;
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
return roundCrop(pool, toTransform);
}
private static Bitmap roundCrop(BitmapPool pool, Bitmap source) {
if (source == null) return null;
Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
if (result == null) {
result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
paint.setAntiAlias(true);
RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight());
canvas.drawRoundRect(rectF, radius, radius, paint);
return result;
}
@Override public String getId() {
return getClass().getName() + Math.round(radius);
}
}
//使用
Glide.with(context).load(imageUrl).transform(new GlideRoundTransform(context)).into(imageView)
//注意:使用了transform以后,就不能使用centercrop,fitcenter等方法
缓存
Fresco缓存也是一大亮点, 三级缓存,分别是 Bitmap缓存,未解码图片缓存, 文件缓存。
这里提一点Bitmap缓存:在5.0以下系统,Bitmap缓存位于ashmem,这样Bitmap对象的创建和释放将不会引发GC,更少的GC会使你的APP运行得更加流畅。5.0及其以上系统,相比之下,内存管理有了很大改进,所以Bitmap缓存直接位于Java的heap上。
另外,磁盘缓存还可以通过代码来设置不同手机的缓存容量:
public void initFresco(Context context, String diskCacheUniqueName){
DiskCacheConfig diskCacheConfig = DiskCacheConfig.newBuilder(context)
.setMaxCacheSize(DISK_CACHE_SIZE_HIGH)
.setMaxCacheSizeOnLowDiskSpace(DISK_CACHE_SIZE_LOW)
.setMaxCacheSizeOnVeryLowDiskSpace(DISK_CACHE_SIZE_VERY_LOW)
.build();
ImagePipelineConfig config = ImagePipelineConfig.newBuilder(context)
.setMainDiskCacheConfig(diskCacheConfig)
.build();
Fresco.initialize(context, config);
}
Glide缓存
Glide虽然只有内存和磁盘缓存,在性能上比不上Fresco;但他也有另外的优点, Fresco缓存的时候,只会缓存原始图像,而Glide则会根据ImageView控件尺寸获得对应的大小的bitmap来展示,从而缓存也可以针对不同的对象:原始图像(source),结果图像(result); 可以通过.diskCacheStrategy()方法设置:
public enum 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);
}
bitmap操作
Glide与Picasso类似,通过简单的方法即可获得网络图片的bitmap对象:
Bitmap myBitmap = Glide.with(applicationContext)
.load(yourUrl)
.asBitmap() //必须
.centerCrop()
.into(500, 500)
.get()
相反,Fresco要获取bitmap更加复杂, 而且使用起来也并不是那么顺畅。首先,Fresco为了更好地管理bitmap 对象(bitmap对象申请和释放会引起频繁的GC操作,从而引起界面卡顿), 引入了可关闭的引用(CloseableReference), 持有者在离开作用域的时候需要关闭该引用,而我们要获取的bitmap 对象就是可关闭的引用。也就是说,我们不能像上面Glide那样把bitmap 对象取出来传递给其它地方使用, 只能在Fresco提供的作用域范围内使用,代码如下:
public void setDataSubscriber(Context context, Uri uri, int width, int height){
DataSubscriber dataSubscriber = new BaseDataSubscriber<CloseableReference<CloseableBitmap>>() {
@Override
public void onNewResultImpl(
DataSource<CloseableReference<CloseableBitmap>> dataSource) {
if (!dataSource.isFinished()) {
return;
}
CloseableReference<CloseableBitmap> imageReference = dataSource.getResult();
if (imageReference != null) {
final CloseableReference<CloseableBitmap> closeableReference = imageReference.clone();
try {
CloseableBitmap closeableBitmap = closeableReference.get();
Bitmap bitmap = closeableBitmap.getUnderlyingBitmap();
if(bitmap != null && !bitmap.isRecycled()) {
//you can use bitmap here
}
} finally {
imageReference.close();
closeableReference.close();
}
}
}
@Override
public void onFailureImpl(DataSource dataSource) {
Throwable throwable = dataSource.getFailureCause();
// handle failure
}
};
getBitmap(context, uri, width, height, dataSubscriber);
}
在实际使用过程中, 如果只有在作用域范围操作bitmap,明显不能满足需求。
项目中使用的方式是获取缓存的文件对象:
//同样在DataSubscriber中获取
FileBinaryResource resource = (FileBinaryResource) Fresco.getImagePipelineFactory().getMainFileCache().getResource(new SimpleCacheKey(url));
if (resource != null && resource.getFile() != null) {
setImage(ImageSource.uri(Uri.fromFile(resource.getFile())));
}
其他
除了以上内容,Fresco还具备以下一些常用的,但Glide没有的功能:
1,SimpleDraweeView控件可以指定图片的宽高比例(app:viewAspectRatio),对于手机适配非常重要;
2,图片加载进度;
3,先加载小尺寸图片,再加载大尺寸的(Glide只有占位图);
性能
除了在功能上对比, 网络图片显示是非常耗性能的, 下面就针对图片质量,内存使用等情况来对比。
图片显示质量
先看一张效果图:
效果图
上面是Glide, 下面是Fresco
天空那部分可以明显看到Glide的图片质量不如Fresco, 原因是Glide为了省内存, 采用了RGB_565的格式显示图片, 相对于"ARGB8888"要省了将近一半的内存。
但也可以通过代码将图片格式改为ARGB_8888。
加载图片速度
Fresco设计中有个有一个image pipeline模块, 可以最大限度节省网络下载图片, 在不考虑缓存的情况下, Fresco也比Glide快很多。 在demo中,同时加载上述图片, Fresco需要3s左右, Glide需要5s左右。
内存使用情况
加载同一张图内存使用情况
情况1(控件比图片大)
Fresco(25.68MB):
fresco
Glide(26.36MB):
glide
基本的内存使用量为:22.93MB,这种情况下,Glide比Fresco使用的内存要多;
情况2(控件比图片小)
上述图片(800*600), 在100dp宽高的控件中显示,内存使用情况:
Fresco(25.23MB):
fresco
Glide(24.60MB):
glide
当控件比较小的时候, Glide明显比情况1使用的内存少很多, 而Fresco则几户与情况1使用相同内存。因为Fresco在没有做过任何处理的情况下,会将原图片加入到内存中,然后做一些压缩处理显示在控件上; 而Glide则是直接准确获取了控件大小,然后得到一张与控件大小相当尺寸的图片,加载到内存并显示。
情况2当时在第一次引入Fresco的碰到过,有些高质量的图片显示在小尺寸的控件上,直接显示的话,很容易出现图片显示不出的情况。
Fresco也提供了方法将图片尺寸压缩,我们可以得到控件的宽高,然后进行压缩:
public void setImageSrc(final SimpleDraweeView draweeView, Uri uri, int width, int height) {
ImageRequestBuilder builder = ImageRequestBuilder.newBuilderWithSource(uri);
if(width > 0 && height > 0){
builder.setResizeOptions(new ResizeOptions(width, height));
}
ImageRequest request = builder.build();
PipelineDraweeController controller = (PipelineDraweeController) Fresco.newDraweeControllerBuilder()
.setImageRequest(request)
.setTapToRetryEnabled(true)
.setOldController(draweeView.getController())
.build();
draweeView.setController(controller);
}
虽然可以解决内存溢出,不必要的内存消耗等情况,但是这样做的前提是必须提前知道控件的宽高,当图片控件使用了wrap_content等方式,这样做就会比较繁琐了。
而Glide则无需考虑这个问题。
引申问题
Glide虽然在需要压缩问题上有优势,但也有另外一个问题, 缓存, 例如上述“情况2”加载了图片在小的控件上, Glide会将300*300的图片保存在缓存中,而当下次需要展现“情况1”(800x600)的时候, 会再次从网络中获取。
要解决这个问题, 需要另外进行缓存控制(参考上述 缓存):
.diskCacheStrategy(DiskCacheStrategy.ALL)
这样就会将原图也缓存下来,下次无需再次请求。
总结
本文只是列举了一些项目中经常碰到的图片加载的功能,作为参考, Fresco和Glide还有更强大的更灵活的功能,例如: Fresco可以自定义网络加载,与其它的Image Loader配合使用等等...可以移步下面的官网和源码地址深入学习。
引用
Fresco https://github.com/facebook/fresco
Glide https://github.com/bumptech/glide
Glide 教程 https://futurestud.io/blog/glide-getting-started
Fresco 中文官网 http://fresco-cn.org/docs/caching.html
如何获取Fresco的bitmaphttp://stackoverflow.com/questions/29476677/using-facebooks-fresco-to-load-a-bitmap/31563169#31563169