网络加载图片对比(Fresco/Glide)

项目中使用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 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

相关内容推荐