Fresco图片加载框架使用方法完全指南

2024-04-03 21:08

本文主要是介绍Fresco图片加载框架使用方法完全指南,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

简介

Fresco 是Facebook开源的安卓上的图片加载框架,也可以说是至今为止安卓上最强大的图片加载框架.

相对于其他几个图片加载框架,Fresco主要的优点在于更好的内存管理和更强大的功能,更便捷的使用,缺点则是体积比较大,引入后会导致应用apk增加1.5M到2M的大小,但是相对于其便捷性来讲,我觉得这都不是事儿.

优点一:内存管理

对于5.0以下系统,fresco使用”ashmem”(匿名共享内存)区域存储Bitmap缓存,这样Bitmap对象的创建、释放将不会触发GC,不会占用javaheap.这个特点是其他图片加载框架所没有做到的.

5.0以上系统,由于安卓系统本身内存管理的优化,所以对于5.0以上的系统Fresco将Bitmap缓存直接放到了javaheap内存中.

并且,fresco实现了真正的三级缓存:两级的内存缓存+一个磁盘缓存.两个内存缓存为:bitmap缓存 和未解码的图片缓存,这样既可以加快图片的加载速度,又能节省内存的占用.这个两级的内存缓存也是其他图片加载框架所没有做到的.

另外提一点,在app切换到后台时,fresco会自动清理两级的内存缓存,无需手动.

通过以上几点,使用fresco加载图片时内存占用要比其他图片加载框架小一大半,基本很少发生oom的事情.

几个图片加载框架的内存占用测试结果对比请戳这里:Android Image Loader 第三方库对比测试

优点二:更便捷的使用:

初期入门,轻度的图片加载,甚至可以直接用:

Fresco.initiliaze(context);<com.facebook.drawee.view.SimpleDraweeViewandroid:id="@+id/my_image_view"android:layout_width="20dp"android:layout_height="20dp"fresco:placeholderImage="@drawable/news_default".../>simpleDraweeView.setImageURI(uri);

其他都不用管,fresco自动帮我们做缓存,图片缩略.

显示圆角或圆圈图片也只是xml中配置一下而已:

圆圈 - 设置roundAsCircle为true
圆角 - 设置roundedCornerRadius

另外再说一句,fresco还支持webp,所以,splash和引导界面的大图我一般都是用智图压成webp放在drawable中,用fresco加载就行了.

相关文档及开源库

官方文档中文版

Github开源库:facebook/fresco,已更新到0.10.0

中文的Fresco源码解读,分析版本:0.7.0

fresco里的photoview :用于查看大图并随手势缩放

bilibili开源的借助fresco加载图片的 spannable text view : Bilibili/drawee-text-view

fresco的bitmap后处理器封装,可以直接使用,关于后处理请看文档

使用心得及一些方法的封装

加载超级大图还是会卡

首先,终极的解决方法肯定是,客户端在图片请求中带上需要的宽和高,服务器将图片缩略到该规格后返回该小图.这个做得比较好的是七牛.

注意:不管服务器能不能返回缩略图,所存储的原图都不应该太大,有时图片太大,甚至都无法下载下来(报504之类的错误).

那么,如果服务器只能拿到原图或大图,fresco怎么缩略显示?

fresco中提供了三个功能来生成缩略图:

Scaling :画布操作,通常是由硬件加速的。图片实际大小保持不变,它只不过在绘制时被放大或缩小.使用时需要配置缩放类型fresco:actualImageScaleType,具体类型名与Imageview的ScaleType几乎一样.

Resizing 是一种软件执行的管道操作。它返回一张新的,尺寸不同的图片,也就是说直接改变bitmap的大小,可惜是单独使用时,只支持jpg,当然,结合Downsampling使用时,可以支持除gif以为的所有常见图片,包括webp.

Downsampling 同样是软件实现的管道操作。它不是创建一张新的图片,而是在解码时改变图片的大小。 同样是软件实现的管道操作。它不是创建一张新的图片,而是在解码时改变图片的大小。类似于Android中的BitmapFactory在decodefile时的inSampleSize,都是指定一个采样率,默认是关闭的,如果开启,那么需要结合Resizing来使用.

综上,要缩小内存占用,以及减少cpu计算量,减少卡顿,应该是Downsampling结合Resizing来使用.其中Downsampling是在Fresco初始化时开启,而Resizing则是通过构建ImageRequest时通过制定宽高来实现,所以可以定制每一张或每一类图片的宽高. 示例代码如下

初始化:

  /*** 初始化操作,建议在子线程中进行* 添加的依赖:*  compile 'com.facebook.fresco:fresco:0.10.0+'compile 'com.facebook.fresco:animated-webp:0.10.0'//加载webp必须添加compile 'com.facebook.fresco:animated-gif:0.10.0' //加载gif必须添加* @param context* @param cacheSizeInM  磁盘缓存的大小,以M为单位*/public static void init(final Context context,int cacheSizeInM){DiskCacheConfig diskCacheConfig = DiskCacheConfig.newBuilder(context).setMaxCacheSize(cacheSizeInM*1024*1024)//最大缓存.setBaseDirectoryName(PHOTO_FRESCO)//子目录.setBaseDirectoryPathSupplier(new Supplier<File>() {@Overridepublic File get() {return context.getCacheDir();//还是推荐缓存到应用本身的缓存文件夹,这样卸载时能自动清除,其他清理软件也能扫描出来}}).build();MyImageCacheStatsTracker imageCacheStatsTracker = new MyImageCacheStatsTracker();//缓存的监听接口,其方法空实现即可ImagePipelineConfig config = ImagePipelineConfig.newBuilder(context).setMainDiskCacheConfig(diskCacheConfig).setImageCacheStatsTracker(imageCacheStatsTracker).setDownsampleEnabled(true)//Downsampling,要不要向下采样,它处理图片的速度比常规的裁剪scaling更快,// 并且同时支持PNG,JPG以及WEP格式的图片,非常强大,与ResizeOptions配合使用.setBitmapsConfig(Bitmap.Config.RGB_565)//如果不是重量级图片应用,就用这个省点内存吧.默认是RGB_888.build();Fresco.initialize(context, config);}

利用SimpleDraweeView加载图片的一般姿势:

注意,我这里没有去设置DraweeHierarchy,因为依照fresco的设计思维,DraweeHierarchy属于view层次的东西,应该在xml中配置.当然如果非要设置,请看这里.

public static void load(Uri uri,SimpleDraweeView draweeView,BasePostprocessor processor,int width,int height,BaseControllerListener listener){ImageRequest request =ImageRequestBuilder.newBuilderWithSource(uri).setPostprocessor(processor).setResizeOptions(new ResizeOptions(width,height))//缩放,在解码前修改内存中的图片大小, 配合Downsampling可以处理所有图片,否则只能处理jpg,// 开启Downsampling:在初始化时设置.setDownsampleEnabled(true).setProgressiveRenderingEnabled(true)//支持图片渐进式加载.setAutoRotateEnabled(true) //如果图片是侧着,可以自动旋转.build();PipelineDraweeController controller =(PipelineDraweeController) Fresco.newDraweeControllerBuilder().setImageRequest(request).setControllerListener(listener).setOldController(draweeView.getController()).setAutoPlayAnimations(true) //自动播放gif动画.build();draweeView.setController(controller);}

显示图片时把人的头部给截掉了

这个就要用到Scaling了.图片的缩放拉伸以及裁剪模式.具体看文档

可用的缩放类型
类型描述
center居中,无缩放。
centerCrop保持宽高比缩小或放大,使得两边都大于或等于显示边界,且宽或高契合显示边界。居中显示。
focusCrop同centerCrop, 但居中点不是中点,而是指定的某个点。
centerInside缩放图片使两边都在显示边界内,居中显示。和 fitCenter 不同,不会对图片进行放大。如果图尺寸大于显示边界,则保持长宽比缩小图片。
fitCenter保持宽高比,缩小或者放大,使得图片完全显示在显示边界内,且宽或高契合显示边界。居中显示。
fitStart同上。但不居中,和显示边界左上对齐。
fitEnd同fitCenter, 但不居中,和显示边界右下对齐。
fitXY不保存宽高比,填充满显示边界。
none如要使用tile mode显示, 需要设置为none

这些缩放类型和Android ImageView 支持的缩放类型几乎一样.

图片默认是centerCrop,那么在用一个横向的SimpleDraweeView来显示一张竖着拍的人像时,就很可能把人的头部给截掉了,但对于在listview中展示的SimpleDraweeView来说,我们又无法用focusCrop直接指定其居中点在图片上半部分某个地方,因为其他图片可能是横着拍的或很正方形的自拍,这个时候怎么办?就需要根据人脸的检测来设置focusCrop的那个点了,而android从sdk 1.0开始就已经提供了一个人脸识别的类FaceDetector,原理是通过找眼睛来识别人脸,可以拿到眼睛的中心点坐标,那么根据该坐标,结合图片本身的宽高,计算出针对每张图片的focusCrop 需要设置的点,就能够解决这个问题了.

//todo 这个还没有去写方法,但有一个开源项目facecropper可以参考,他们的做法是将一个大的bitmap截图成小的bitmap.同样的代码还有中文注释版的,汗…

获取缓存的图片文件

对于内存的缓存,fresco根据图片Uri,以及图片的resising参数和processor参数综合生成缓存key来缓存bitmap,而磁盘文件缓存则是只根据Uri生成key,那么,如果要获取文件缓存,只需要知道uri和通过key取file的api就行了,原先的调用链较长,故封装成单个方法:

需要注意的是文件名后缀不是普通的图片后缀(.jpg之类的),而是.cnt,但都是二进制文件,可以将文件直接拷贝到指定路径重命名成正常图片后缀即可.当然如果只是读取到内存做其他用途,可以直接读取,无需拷贝更改后缀,不影响使用.

以下是读取缓存文件的方法.而拷贝到其他文件目录的方法也已封装好于FrescoUtils中.

public static File getFileFromDiskCache(String url){File localFile = null;if (!TextUtils.isEmpty(url)) {CacheKey cacheKey = DefaultCacheKeyFactory.getInstance().getEncodedCacheKey(ImageRequest.fromUri(url));if (ImagePipelineFactory.getInstance().getMainFileCache().hasKey(cacheKey)) {BinaryResource resource = ImagePipelineFactory.getInstance().getMainFileCache().getResource(cacheKey);localFile = ((FileBinaryResource) resource).getFile();} else if (ImagePipelineFactory.getInstance().getSmallImageFileCache().hasKey(cacheKey)) {BinaryResource resource = ImagePipelineFactory.getInstance().getSmallImageFileCache().getResource(cacheKey);localFile = ((FileBinaryResource) resource).getFile();}}return localFile;}

如果我想用fresco来下载图片,不需要显示,要怎么实现?

首先,如果是单纯的下载,建议使用专门的文件下载框架FileDownloader

如果非要用fresco,那么需要绕一点弯:

​ 通过imagePipeline.prefetchToDiskCache将图片缓存到disk,然后拷贝出来.缓存完成的监听采用fresco提供的BaseDataSubscriber,在文件缓存完成后回调,然后拷贝文件到指定目录.

注意:该方式不是很好用,偶尔会有失败的情况出现.

 /*** 文件下载到文件夹中:将图片缓存到本地后,将缓存的图片文件copy到另一个文件夹中** 容易发生如下异常,progress在100处停留时间长* dalvikvm: Could not find method android.graphics.Bitmap.getAllocationByteCount,* referenced from method com.facebook.imageutils.BitmapUtil.getSizeInBytes06-21 16:15:39.547 3043-3244/com.hss01248.tools W/dalvikvm: VFY:unable to resolve virtual method 569: Landroid/graphics/Bitmap;.getAllocationByteCount ()I* @param url* @param context* @param dir 保存图片的文件夹* @param listener 自己定义的回调*/public static void download(final String url, Context context, final File dir, final DownloadListener listener){ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url)).build();final ImagePipeline imagePipeline = Fresco.getImagePipeline();DataSource<Void> dataSource = imagePipeline.prefetchToDiskCache(imageRequest, context, Priority.HIGH);dataSource.subscribe(new BaseDataSubscriber<Void>() {@Overrideprotected void onNewResultImpl(DataSource<Void> dataSource) {File  file  =   copyCacheFileToDir(url,dir);clearCacheByUrl(url);//清除缓存if (file == null || !file.exists()){listener.onFail();}else {listener.onSuccess(file);}}@Overridepublic void onProgressUpdate(DataSource<Void> dataSource) {super.onProgressUpdate(dataSource);listener.onProgress(dataSource.getProgress());}@Overrideprotected void onFailureImpl(DataSource<Void> dataSource) {listener.onFail();}}, CallerThreadExecutor.getInstance());}

由于某种原因无法使用SimpleDraweeView来显示图片(比如说弹幕上显示头像),而需要直接操作bitmap,那么要怎么拿到url返回的bitmap?

自己构建图片请求,然后类似上面的文件下载,还是采用DataSubscriber来监听回调,只不过返回的不是void,而是CloseableImage的bitmap,

/*** 拿到指定宽高,并经过Processor处理的bitmap* @param url* @param context* @param width* @param height* @param processor 后处理器,可为null* @param listener**/public static void getBitmapWithProcessor(String url, Context context, int width, int height,BasePostprocessor processor,final BitmapListener listener){ResizeOptions resizeOptions = null;if (width !=0 && height != 0 ){resizeOptions = new ResizeOptions(width, height);}ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url)).setProgressiveRenderingEnabled(false) //我们是拿bitmap对象,不是显示,所以这里不需要渐进渲染.setPostprocessor(processor).setResizeOptions(resizeOptions).build();ImagePipeline imagePipeline = Fresco.getImagePipeline();DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline.fetchDecodedImage(imageRequest, context);dataSource.subscribe(new BaseBitmapDataSubscriber() {@Overrideprotected void onNewResultImpl(Bitmap bitmap) {listener.onSuccess(bitmap);}@Overrideprotected void onFailureImpl(DataSource<CloseableReference<CloseableImage>> dataSource) {listener.onFail();}}, CallerThreadExecutor.getInstance());}

注意,此bitmap对象的缓存由Fresco管理,所以不要去调用bitmap.recycle()之类的方法.

对此bitmap的一些处理,如果处理后的bitmap对象还是指向该bitmap引用,会影响到其他同样url,width height,并且同样Postprocessor的图片组件的显示,比如,将该bitmap高斯模糊了,就会影响到其他的这四个参数相同的SimpleDraweeView的显示.

那么,如果要不影响,怎么办?很简单,让四个参数任一一个不同就行.

Fresco中在listview之类的快速滑动时停止加载,滑动停止后恢复加载时调用的API是什么?

话不多说,直接看代码.

/*** 暂停网络请求* 在listview快速滑动时使用*/public static void pause(){Fresco.getImagePipeline().pause();}/*** 恢复网络请求* 当滑动停止时使用*/public static void resume(){Fresco.getImagePipeline().resume();}

gif图片无法显示成圆形,怎么办?(当然有的情况下,jpg也无法显示圆形) –加一层和图片的parentview 背景色一样的圆形遮罩即可:

public static void setCircle( SimpleDraweeView draweeView,int bgColor){RoundingParams roundingParams = RoundingParams.asCircle();//这个方法在某些情况下无法成圆,比如gifroundingParams.setOverlayColor(bgColor);//加一层遮罩,这个是关键方法draweeView.getHierarchy().setRoundingParams(roundingParams);}

当然,无法圆角化,也是同样加一层遮罩:

很多app都有清除磁盘缓存的功能,那么fresco怎么清除缓存呢?

/*** 清除磁盘缓存*/public static void clearDiskCache(){Fresco.getImagePipeline().clearDiskCaches();}/*** 清除单张图片的磁盘缓存* @param url*/public static void clearCacheByUrl(String url){ImagePipeline imagePipeline = Fresco.getImagePipeline();Uri uri = Uri.parse(url);// imagePipeline.evictFromMemoryCache(uri);imagePipeline.evictFromDiskCache(uri);//imagePipeline.evictFromCache(uri);//这个包含了从内存移除和从硬盘移除}

已经设置了downsampling和resize,但是加载很大的gif图还是很卡,怎么办

gif是一帧一帧的图组成,编码的方式跟jpg和png等图片有很大区别,downsampling和resize对gif是无效的.gif图太大了?

分辨率一大的话,那文件不得十几M几十M?下载都要等很久吧?

还是在服务器端放小一点的图吧…

高斯模糊

高斯模糊是app里设置一些背景效果 常用到的手段.

在fresco中,可以通过postprocessor来实现,也可以自己拿到bitmap后将bitmap模糊化后设置到ImageView或SimpleDraweeView(这个不建议,会消除掉SimpleDraweeView的层级结构,变成单纯的ImageView)上.

推荐前一种: 用到别人封装好的BlurPostprocessor:

    /*** 高斯模糊后显示* @param url* @param draweeView* @param width draweeView的宽* @param height draweeView的高* @param context* @param radius  高斯模糊的半径, 每一个像素都取周边(多少个)像素的平均值* @param sampling 采样率 原本是设置到BlurPostprocessor上的,因为高斯模糊本身对图片清晰度要求就不高,*                 所以此处直接设置到ResizeOptions上,直接让解码生成的bitmap就缩小,而BlurPostprocessor*                 内部sampling设置为1,无需再缩*/public static void loadUrlInBlur(String url,SimpleDraweeView draweeView,int width,int height,Context context,int radius,int sampling){if (sampling<2){sampling = 2;}loadUrl(url,draweeView,new BlurPostprocessor(context,radius,1),width/sampling,height/sampling,null);}

当然,拿到bitmap自己去模糊也有开源框架:NativeStackBlur

 private Bitmap fastBlur(Bitmap bkg, int radius,int downSampling) {if (downSampling < 2){downSampling = 2;}Bitmap smallBitmap =   Bitmap.createScaledBitmap(bkg,bkg.getWidth()/downSampling,bkg.getHeight()/downSampling,true);return   NativeStackBlur.process(smallBitmap, radius);}

也可以通过这个方法来封装自己的BlurPostprocessor,具体可参考上面封装好的BlurPostprocessor.

工具类FrescoUtils地址:https://github.com/glassLake/FrescoUtlis

这篇关于Fresco图片加载框架使用方法完全指南的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/873982

相关文章

Java图片压缩三种高效压缩方案详细解析

《Java图片压缩三种高效压缩方案详细解析》图片压缩通常涉及减少图片的尺寸缩放、调整图片的质量(针对JPEG、PNG等)、使用特定的算法来减少图片的数据量等,:本文主要介绍Java图片压缩三种高效... 目录一、基于OpenCV的智能尺寸压缩技术亮点:适用场景:二、JPEG质量参数压缩关键技术:压缩效果对比

Feign Client超时时间设置不生效的解决方法

《FeignClient超时时间设置不生效的解决方法》这篇文章主要为大家详细介绍了FeignClient超时时间设置不生效的原因与解决方法,具有一定的的参考价值,希望对大家有一定的帮助... 在使用Feign Client时,可以通过两种方式来设置超时时间:1.针对整个Feign Client设置超时时间

使用Python实现一键隐藏屏幕并锁定输入

《使用Python实现一键隐藏屏幕并锁定输入》本文主要介绍了使用Python编写一个一键隐藏屏幕并锁定输入的黑科技程序,能够在指定热键触发后立即遮挡屏幕,并禁止一切键盘鼠标输入,这样就再也不用担心自己... 目录1. 概述2. 功能亮点3.代码实现4.使用方法5. 展示效果6. 代码优化与拓展7. 总结1.

使用Python开发一个简单的本地图片服务器

《使用Python开发一个简单的本地图片服务器》本文介绍了如何结合wxPython构建的图形用户界面GUI和Python内建的Web服务器功能,在本地网络中搭建一个私人的,即开即用的网页相册,文中的示... 目录项目目标核心技术栈代码深度解析完整代码工作流程主要功能与优势潜在改进与思考运行结果总结你是否曾经

C/C++错误信息处理的常见方法及函数

《C/C++错误信息处理的常见方法及函数》C/C++是两种广泛使用的编程语言,特别是在系统编程、嵌入式开发以及高性能计算领域,:本文主要介绍C/C++错误信息处理的常见方法及函数,文中通过代码介绍... 目录前言1. errno 和 perror()示例:2. strerror()示例:3. perror(

Linux中的计划任务(crontab)使用方式

《Linux中的计划任务(crontab)使用方式》:本文主要介绍Linux中的计划任务(crontab)使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、前言1、linux的起源与发展2、什么是计划任务(crontab)二、crontab基础1、cro

kotlin中const 和val的区别及使用场景分析

《kotlin中const和val的区别及使用场景分析》在Kotlin中,const和val都是用来声明常量的,但它们的使用场景和功能有所不同,下面给大家介绍kotlin中const和val的区别,... 目录kotlin中const 和val的区别1. val:2. const:二 代码示例1 Java

CSS去除a标签的下划线的几种方法

《CSS去除a标签的下划线的几种方法》本文给大家分享在CSS中,去除a标签(超链接)的下划线的几种方法,本文给大家介绍的非常详细,感兴趣的朋友一起看看吧... 在 css 中,去除a标签(超链接)的下划线主要有以下几种方法:使用text-decoration属性通用选择器设置:使用a标签选择器,将tex

C++变换迭代器使用方法小结

《C++变换迭代器使用方法小结》本文主要介绍了C++变换迭代器使用方法小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1、源码2、代码解析代码解析:transform_iterator1. transform_iterat

C++中std::distance使用方法示例

《C++中std::distance使用方法示例》std::distance是C++标准库中的一个函数,用于计算两个迭代器之间的距离,本文主要介绍了C++中std::distance使用方法示例,具... 目录语法使用方式解释示例输出:其他说明:总结std::distance&n编程bsp;是 C++ 标准