常用的4种图片加载框架:Fresco、Glide、Picasso、Image-loader
他们都有一个共同的优点:
使用简单 :都可以通过一句代码可实现图片获取和显示。
可配置度高,自适应程度高 :
多级缓存 :都至少有两级缓存、提高图片加载速度。
支持多种数据源 :支持多种数据源,网络、本地、资源、Assets 等
Fresco是Facebook开源Android平台上一个强大的图片加载库,也是迄今为止Android平台上最强大的图片加载库。
优点:相对于其他开源的第三方图片加载库,Fresco拥有更好的内存管理和强大的功能,基本上能满足所有的日常使用场景。
大大减少了OOM,对于OOM的问题,既然没发在java层处理,Facebook另辟途径,在更底层的Native堆进行处理,Fresco将图片放到一个特别的内存区域叫Ashmem区,图片不在占用APP的内存,这个属于C++的地盘,所以能大大的减少OOM。
缺点:用法比较复杂,包更加庞大(2-3M),底层涉及了C++领域,想要读源码深入学习比较困难。从代码层面来说侵入性太强,体现在要使用它需要用Fresco的组件SimpleDraweeView替换掉Android原生图片显示组件ImageView,这也是很多人不愿意在项目中接入Fresco的主要原因。
应用场景:对于大量使用图片加载需要更更更高的性能要求,如Instagram等图片社交App;
特性:
1、内存管理
解压后的图片,即Android中的Bitmap,占用大量的内存。大的内存占用势必引发更加频繁的GC。在5.0以下,GC将会显著地引发界面卡顿。
在5.0以下系统,Fresco将图片放到一个特别的内存区域。当然,在图片不显示的时候,占用的内存会自动被释放。这会使得APP更加流畅,减少因图片内存占用而引发的OOM。
2、Image Pipeline
Fresco中设计有一个叫做 Image Pipeline 的模块。它负责从网络,从本地文件系统,本地资源加载图片和管理。为了最大限度节省空间和CPU时间,它含有3级缓存设计(2级内存,1级磁盘)。两个内存缓存为Bitmap缓存和未解码的图片缓存,这样既可以加快图片的加载速度,又能节省内存的占用(解码后的图片就是Bitmap,其占用内存相对未解码的图片数据而言会大很多)。
4、Fresco目前所支持的图片格式
a、静态图:png、jpg、web
b、动态图:gif、web格式的gif
需要到的依赖:
compile'com.facebook.fresco:fresco:0.14.1'
//在 API < 14 上的机器支持 WebP 时,需要添加以下依赖
compile'com.facebook.fresco:animated-base-support:0.14.1'
//支持GIF动图,需要添加以下依赖
compile'com.facebook.fresco:animated-gif:0.14.1'
//支持WebP,需要添加以下依赖
compile'com.facebook.fresco:webpsupport:0.14.1'
//支持WebP动图,需要添加以下依赖
compile'com.facebook.fresco:animated-webp:0.14.1'
//网络实现层想使用okhttp3,需要添加以下依赖
compile'com.facebook.fresco:imagepipeline-okhttp3:0.14.1'
ImagePipeline配置
a、磁盘缓存目录,推荐缓存到应用本身的缓存文件夹,这么做的好处是:当应用被用户卸载后能自动清除缓存,增加用户好感(可能以后用得着时,还会想起我);一些内存清理软件可以扫描出来,进行内存的清理。
File fileCacheDir = context.getApplicationContext().getCacheDir();
b、配置磁盘缓存,大部分的应用有一个磁盘缓存就够了,但是在一些情况下,你可能需要两个缓存。比如你想把小文件放在一个缓存中(50*50及以下尺寸),大文件放在另外一个文件中,这样小文件就不会因大文件的频繁变动而被从缓存中移除。
DiskCacheConfigmainDiskCacheConfig=DiskCacheConfig.newBuilder(context)
.setBaseDirectoryName(IMAGE_PIPELINE_CACHE_DIR)
.setBaseDirectoryPath(fileCacheDir)
.build();
DiskCacheConfigsmallDiskCacheConfig= DiskCacheConfig.newBuilder(context)
.setBaseDirectoryPath(fileCacheDir)
.setBaseDirectoryName(IMAGE_PIPELINE_SMALL_CACHE_DIR)
.setMaxCacheSize(MAX_DISK_SMALL_CACHE_SIZE)
.setMaxCacheSizeOnLowDiskSpace(MAX_DISK_SMALL_ONLOWDISKSPACE_CACHE_SIZE)
.build();
推荐的使用方法:
1、第一种用法
初始化:Phoenix.init(this); // Context
从网络加载一张图片
String url = "http://ww3.sinaimg.cn/large/610dc034jw1f6m4aj83g9j20zk1hcww3.jpg";
SimpleDraweeView simpleDraweeView = (SimpleDraweeView)findViewById(R.id.sdv_1);
Phoenix.with(simpleDraweeView).load(url);
从本地加载一张图片
String filePath = "/sdcard/image/test.jpg";
SimpleDraweeView simpleDraweeView = (SimpleDraweeView)findViewById(R.id.sdv_1);
Phoenix.with(simpleDraweeView).load(filePath);
从res下面加载一张图片
SimpleDraweeView simpleDraweeView = (SimpleDraweeView)findViewById(R.id.sdv_1);
Phoenix.with(simpleDraweeView).load(R.drawable.meizi);
加载并显示gif格式的图片
String url = "http://img4.178.com/acg1/201506/227753817857/227754566617.gif";
SimpleDraweeView simpleDraweeView = (SimpleDraweeView)findViewById(R.id.sdv_1);
Phoenix.with(simpleDraweeView)
.load(url);
2、第二种用法
初始化
Fresco.initialize(context, ImageLoaderConfig.getImagePipelineConfig(context));
从网络加载一张图片
String url = "http://ww3.sinaimg.cn/large/610dc034jw1f6m4aj83g9j20zk1hcww3.jpg";
SimpleDraweeView simpleDraweeView = (SimpleDraweeView)findViewById(R.id.sdv_1);
ImageLoader.loadImage(simpleDraweeView, url);
从本地加载一张图片
String filePath = "/sdcard/image/test.jpg";
SimpleDraweeView simpleDraweeView = (SimpleDraweeView)findViewById(R.id.sdv_1);
ImageLoader.loadFile(simpleDraweeView, filePath);
从res下面加载一张图片
SimpleDraweeView simpleDraweeView = (SimpleDraweeView)findViewById(R.id.sdv_1);
ImageLoader.loadDrawable(simpleDraweeView, R.drawable.meizi, 100, 100);
How to use this?
Glide.with(context).load(url).into(imageview);
With()方法
with()方法是Glide类中的一组静态方法,它有好几个方法重载
with()方法的重载种类非常多,既可以传入Activity,也可以传入Fragment或者是Context。每一个with()方法重载的代码都非常简单,都是先调用RequestManagerRetriever的静态get()方法得到一个RequestManagerRetriever对象,这个静态get()方法就是一个单例实现,然后再调用RequestManagerRetriever的实例get()方法,去获取RequestManager对象。
Load()方法
load()方法是在RequestManager类当中的。
先调用了fromString()方法,再调用load()方法,然后把传入的图片URL地址传进去。而fromString()方法也极为简单,就是调用了loadGeneric()方法,并且指定参数为String.class,因为load()方法传入的是一个字符串参数。那么看上去,好像主要的工作都是在loadGeneric()方法中进行的了。
其实loadGeneric()方法也没几行代码,这里分别调用了Glide.buildStreamModelLoader()方法和Glide.buildFileDescriptorModelLoader()方法来获得ModelLoader对象。ModelLoader对象是用于加载图片的,而我们给load()方法传入不同类型的参数,这里也会得到不同的ModelLoader对象。
loadGeneric()方法是要返回一个DrawableTypeRequest对象的。
Into()方法
into()方法的具体逻辑都是在DrawableRequestBuilder的父类当中也就是GenericRequestBuilder类。
GenericRequestBuilder类调用了glide.buildImageViewTarget()方法,这个方法会构建出一个Target对象,Target对象则是用来最终展示图片用的。
Into方法中有buildRequest()方法,buildRequest()方法的内部其实又调用了buildRequestRecursive()方法,这里调用了obtainRequest()方法来获取一个Request对象,而obtainRequest()方法中又去调用了GenericRequest的obtain()方法。注意这个obtain()方法需要传入非常多的参数,而其中很多的参数我们都是比较熟悉的,像什么placeholderId、errorPlaceholder、diskCacheStrategy等等。因此,在load()方法中调用的所有API,其实都是在这里组装到Request对象当中的。
into()方法调用了requestTracker.runRequest()方法来去执行这个Request。
load激活了缓存机制,在其内部的核心方法是loadGeneric做了处理,然后你还会注意到一个方法GenericRequestBuilder#obtainRequest在开始请求是无论什么情况都会调用该方法。
Glide的网络请求:
首先,在decode()方法中,又去调用了另外一个decode()方法的重载。然后调用了decodeStream()方法,准备从服务器返回的流当中读取数据。decodeStream()方法中会先从流中读取2个字节的数据,来判断这张图是GIF图还是普通的静图,如果是GIF图就调用decodeGifWrapper()方法来进行解码,如果是普通的静图就用调用decodeBitmapWrapper()方法来进行解码。
Glide出现过的错误:
当使用glide进行图片加载成功之后,替换服务器的图片资源,但是请求地址不发生改变,当再次请求时,只显示上次请求的图片,不显示当前服务器所储存的图片。
解决方法:
这个错误是由于glide底层是进行一系列的缓存处理而导致的,所以解决方式就是跳过他的缓存进行图片加载。就是添加以下方法:
signature(new StringSignature(String.valueOf(System.currentTimeMillis())));
在这里声明一下,这个方法在glide4.0之后的版本是没有的。
推荐使用:compile 'com.github.bumptech.glide:glide:3.7.0'
优点:
自带统计监控功能
支持图片缓存使用的监控,包括缓存命中率、已使用内存大小、节省的流量等。
支持优先级处理(高 低 正常)
每次任务调度前会选择优先级高的任务,比如 App 页面中 Banner 的优先级高于 Icon 时就很适用。
支持飞行模式、并发线程数根据网络类型而变
手机切换到飞行模式或网络类型变换时会自动调整线程池最大并发数,比如 wifi 最大并发为 4, 4g 为 3,3g 为 2。
这里 Picasso 根据网络类型来决定最大并发数,而不是 CPU 核数。
“无”本地缓存
无”本地缓存,不是说没有本地缓存,而是 Picasso 自己没有实现,交给了 Square 的另外一个网络库 okhttp 去实现,这样的好处是可以通过请求 Response Header 中的 Cache-Control 及 Expired 控制图片的过期时间。
在adapter中取消不在视野范围的ImageView图片资源加载,解决了图片错位的问题;
使用最少的内存完成图片的转换;
实现的内存缓存和磁盘缓存。
图形的转换,比如大小、旋转等;
加载网络图片和本地图片。
Picasso 比 Glide 体积小很多且图像质量比 Glide 高
获取Picasso的实例并设置setIndicatorsEnabled(true),Picasso在加载图片的时候,会在图片的左上角显示不同的指示颜色,红色说明当前图片加载自网络,绿色说明当前图片加载自内存,蓝色说明当前图片加载自硬盘
缺点:
With(只能使用context)
// .resize(200, 200)// 指定图片的尺寸
// .fit()// 指定图片缩放类型为fit 会根据view的大小拉伸 不能和resize同时使用
// .centerCrop()// 指定图片缩放类型为centerCrop 必须和resize同时使用
// .centerInside()// 指定图片缩放类型为centerInside 必须和resize同时使用
// .memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)// 指定内存缓存策略 加载大图时放弃缓存
// NO_CACHE:让Picasso跳过从内存中读取图片这一操作
// NO_STORE:如果你的图片只加载一次就没用了,就调用该值,这样的话Picasso就不会在内存及本地进行缓存了
//
// NO_CACHE:让Picasso跳过从本地读取资源这一过程
// NO_STORE:让Picasso不进行本地图片缓存
//
//
// .transform(new RoundedTransformation(360,0))
// .priority(Picasso.Priority.HIGH)// 指定优先级 分为:高 低 正常
传统的ImageVIew设置图片
Picasso.with(context)
.load(url)
.placeholder(R.drawable.tab_item_bg)
.into(imageView);
自动设置图片宽高像素的大小
Picasso
.with(context)
.load(url)
.resize(50, 50)
.centerCrop()
.into(imageView)
Picasso也是利用了Builder模式来组建Picasso,用Builder模式的好处之一就是可以通过Builder来清晰的知道Picasso可都可以提供哪些对外的配置接口供我们使用,同时我们自己在客户端配置这些组件的话,Builder也可以提供默认的组件来使用
private Downloader downloader;//配置自定义的义图片下载类
private ExecutorService service;//配置自定义的线程池
private Cache cache;//配置自定义缓存,特指内存换文
private Listener listener;//配置图片下载监听
private RequestTransformer transformer;//配置自定义的请求转换器
Picasso的with方法来初始化一个Picasso单例对象,
当ListView、GridView快速滑动时,使用Picasso不加载图片:
1. if (scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_TOUCH_SCROLL) {
2. //如果在暂停或者触摸的情况下开始加载
3. //PicassoUtils.newInstance(context).getPicasso()
4. // .resumeTag(context);
5. } else {
6. //停止加载
7. //PicassoUtils.newInstance(context).getPicasso()
8. // .pauseTag(context);
9. }
加载图片并设置图片加载进度监听
into(imageView,new Callback.EmptyCallback(){
@Override
public void onSuccess() {
super.onSuccess(); //在这里执行你想要执行的操作
}
@Override
public void onError() {
super.onError(); //在这里执行你想要执行的操作
} });
Imageloader可以多线程异步加载和显示图片(图片可以来源自网络,SD卡,assets文件夹,drawable文件夹(不能加载9patch),视频缩略图)。并且他可以给加载过程添加一个监听事件,可以用来暂停加载图片。它还可以根据自己的需求进行各种配置(比如配置一些线程池,图片下载器,内存缓存策略)。支持图片的内存缓存和SD卡缓存,并且在网络速度较慢的时候,还可以对图片进行加载并设置下载监听。
它有个不好的地方就是,它的作者在2015年年底后就不在进行维护了。
基本使用方法:
1. 在Gradle的dependency下加入
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.3'
2. 加入权限:
在全局Application中new一个ImageLoder对象,这个对象是单例模式的:
public class MyApplication extends Application {
@Override public void onCreate() {
super.onCreate();
//创建默认的ImageLoader配置参数
ImageLoaderConfiguration configuration = ImageLoaderConfiguration
.createDefault(this); //进行初始化操作 ImageLoader.getInstance().init(configuration);
}
}
注意:
00001. 只能配置一次,如多次配置,则默认第一次的配置参数。
00002. 这里主要是imageloader的配置参数是个重点。
一般用默认配置就ok。但也提供了一些特定的设置,只有真的需要的时候才用。
Imageloader可以手动修改内存缓存策略
在Application中初始化ImageLoaderConfiguration的时候,加上memoryCache就可以了。
ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder(this)
.memoryCache(new WeakMemoryCache()) .build();
我们常用的图片框架,一般有4种:Fresco、Glide、Picasso、Image-loader。它们使用简单,可以通过一句代码来实现图片的请求和显示,它们最少都有2级缓存,提高了图片的加载速度,并且可以设置各种配置,多种数据源的加载。
我们先从fresco说起。Fresco是Facebook出品的一个图片加载库,它是迄今为止Android平台上最强大的图片加载库。
它相对于其他开源的第三方图片加载库,Fresco拥有更好的内存管理和强大的功能,基本上能满足所有的日常使用场景。并且它大大减少了OOM,对于OOM的问题,他没有在java层处理,Facebook另辟途径,在更底层的Native堆进行处理,Fresco将图片放到一个特别的内存区域叫Ashmem区,图片不在占用APP的内存,这个属于C++的地盘,所以能大大的减少OOM。
它有个缺点就是用法比较复杂,包更加庞大(2-3M),底层涉及了C++领域,想要读源码深入学习比较困难。在使用的时候,它需要用Fresco的组件SimpleDraweeView替换掉Android原生图片显示组件ImageView,这也是很多人不愿意在项目中接入Fresco的主要原因。
它支持静态图和动态图的加载。
Glide是一个非常成熟的图片加载库,他可以从多个源加载图片,如:网路,本地,Uri等,更重要的是他内部封装了非常好的缓存机制并且在处理图片的时候能保持一个低的内存消耗。
当我们调整imageview的大小时,Picasso会不管imageview大小是什么,总是直接缓存整张图片,而Glide就不一样了,它会为每个不同尺寸的Imageview缓存一张图片,也就是说不管你的这张图片有没有加载过,只要imageview的尺寸不一样,那么Glide就会重新加载一次,这时候,它会在加载的imageview之前从网络上重新下载,然后再缓存。
较之Picasso,Glide加载图片速度是非常快的。
另外,Glide拥有Picasso所不具备的加载GIF图的功能。
缺点:Glide的Bitmap默认的格式是RGB_565,而Picasso用的是ARGB_8888,所以虽然质量上不如Picasso(其实在手机上也不明显),但是RGB_565格式的图片仅仅消耗ARGB_8888格式图片一半的内存。
Picasso的库大小大约是118KB,而Glide大约有430KB。
Picasso它自带统计监控的功能,就是在图片缓存的时候,可以监控图片的缓存命中率、已使用的内存大小、节省的流量等。它也可以设置优先级,先加载优先级高的图片,它可以通过监听你网络的变换来调整你的最大并发数,比如 wifi 最大并发为 4, 4g 为 3,3g 为 2。
Picasso自己没有实现本地缓存,交给了 Square 的另外一个网络库 okhttp 去实现,这样的好处是可以通过请求 Response Header 中的 Cache-Control 及 Expired 控制图片的过期时间。Picasso 比 Glide 体积小很多且图像质量比 Glide 高
Picasso在使用with()方法是时候里面只能传context
Imageloader可以多线程异步加载和显示图片(图片可以来源自网络,SD卡,assets文件夹,drawable文件夹(不能加载9patch),视频缩略图)。并且他可以给加载过程添加一个监听事件,可以用来暂停加载图片。它还可以根据自己的需求进行各种配置(比如配置一些线程池,图片下载器,内存缓存策略)。支持图片的内存缓存和SD卡缓存,并且在网络速度较慢的时候,还可以对图片进行加载并设置下载监听。
它有个不好的地方就是,它的作者在2015年年底后就不在进行维护了。