Android图片加载
图片加载的开源库有很多,这里主要介绍一下Universal-ImageLoader,Volley ImageLoader,Freso,Picasso,Glide方式。
1. Universal-ImageLoader
ImageLoader 是最早开源的 Android 图片缓存库, 强大的缓存机制, 早期被广泛 Android应用使用, 至今仍然有很多 Android 开发者在使用。
1.1 加载图片原理
a. UI:请求数据,使用唯一的Key值索引Memory Cache中的Bitmap。
b. 内存缓存:缓存搜索,如果能找到Key值对应的Bitmap,则返回数据。否则执行c。
c. 硬盘存储:使用唯一Key值对应的文件名,检索SD Card上的文件。如果有对应文件,使用BitmapFactory.decode方法,解码Bitmap并返回数据,同时将数据写入缓存。如果没有对应文件,执行d。
d. 下载图片:启动异步线程,从数据源下载数据。
e. 若下载成功,将数据同时写入硬盘和缓存,并将Bitmap显示在UI中。
1.2使用特性
a. 多线程异步加载和显示图片
b. 支持二级缓存
c. 支持加载文件系统资源文件等图片
d. 可以实现图片下载过程的监听。且可以支持图片下载完成后,显示圆形或者圆角矩形的图片。
1.3 使用方法
(1)先要配置ImageLoaderConfiguration这个类实现全局ImageLoader的实现情况。可以选择在Application中初始化设置该类。
(2)加载图片
ImageLoader.getInstance().displayImage(imageUrl,mImageView, options); ImageLoader是具体下载图片,缓存图片,显示图片的具体执行类,它有两个具体的方法displayImage(...)、loadImage(...),但是其实最终他们的实现都是displayImage(...)。
图片类型不同URL就不同,例如:
content provider图片 URL = “content://media/external/audio/albumart/13"
assets图片String assetsUrl =Scheme.ASSETS.wrap("image.png");
drawable图片URL=Scheme.DRAWABLE.wrap("R.drawable.image")
DisplayImageOptions用于指导每一个Imageloader根据网络图片的状态(空白、下载错误、正在下载)显示对应的图片,是否将缓存加载到磁盘上,下载完后对图片进行怎么样的处理。DisplayImgeOptions配置如下:
DisplayImageOptions options = newDisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.ic)
.showImageForEmptyUri(R.drawable.ic_empty)
.showImageOnFail(R.drawable.ic_error)
.resetViewBeforeLoading(false)
.delayBeforeLoading(1000)
.cacheInMemory(false)
.cacheOnDisk(false)
.preProcessor(...)
.postProcessor(...)
.extraForDownloader(...)
.considerExifParams(false)
.imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2)
.bitmapConfig(Bitmap.Config.ARGB_8888)
1.4 避免OOM
若出现OOM问题,采用以下方法:
a. 禁用在内存中缓存cacheInMemory(false),使用MemoryAnalyzer来检测内存泄漏。
b. 减少线程池的个数。
c. 在DisplayImageOptions选项中配置bitmapConfig为Bitmap.Config.RGB_565,因为默认是ARGB_8888, 使用RGB_565会比使用ARGB_8888少消耗2倍的内存
d.在DisplayImageOptions选项中设置.imageScaleType(ImageScaleType.IN_SAMPLE_INT)或者imageScaleType(ImageScaleType.EXACTLY)。
e. 配置中使用 .memoryCache(newWeakMemoryCache())或者完全禁用在内存中缓存。
2. Volley ImageLoader
Volley ImageLoader是Google发布的一个开源库,它不能加载本地图片。
2.1 使用方法
2.1.1 ImageRequest
ImageLoader的内部使用ImageRequest来实现,不过ImageLoader明显要比ImageRequest更加高效,因为它不仅可以帮我们对图片进行缓存,还可以过滤掉重复的链接,避免重复发送请求。我们先看看ImageRequest的使用:
(1) 创建一个RequestQueue对象
(2) 创建一个Request对象
(3) 将Request对象添加到RequestQueue里面
ImageRequest imageRequest = newImageRequest(
imageURL, new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmapresponse) {
imageView.setImageBitmap(response);
}
}, 0, 0, Config.RGB_565, newResponse.ErrorListener() {
@Override
public voidonErrorResponse(VolleyError error) {
imageView.setImageResource(R.drawable.default_image);
}
});
mQueue.add(imageRequest);
2.1.2 ImageLoader
在此基础上,ImageLoader的使用有相似之处:
(1) 创建一个RequestQueue对象
(2) 创建一个ImageLoader对象
(3) 获取一个ImageListener对象
(4) 调用ImageLoader的get()方法加载网络上的图片
ImageLoader imageLoader = newImageLoader(mQueue, new ImageCache() {
@Override
public void putBitmap(String url, Bitmap bitmap) {
}
@Override
public Bitmap getBitmap(String url) {
return null;
}
});
ImageListener listener =ImageLoader.getImageListener(imageView, R.drawable.default_image, R.drawable.failed_image);
最后显示图片:
imageLoader.get("imageURL",listener);
或imageLoader.get("imageURL", listener,maxWidth,maxHeight);
这个方法中,我们可以通过设置最大宽高来限制加载到内存中的图片的大小,减少OOM的发生,当加载一些大图片时,效果还是非常明显的。显示图片后,ImageLoader还提供了图片缓存功能,这需要写一个ImageCache,借助于Android提供的LruCache,实现如下:
public class BitmapCache implementsImageCache {
private LruCache<String, Bitmap> mCache;
public BitmapCache() {
int maxSize = 10 * 1024 * 1024;
mCache = new LruCache<String,Bitmap>(maxSize) {
@Override
protected int sizeOf(String key,Bitmap bitmap) {
return bitmap.getRowBytes() *bitmap.getHeight();
}
};
}
@Override
public Bitmap getBitmap(String url) {
return mCache.get(url);
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
mCache.put(url, bitmap);
}
}
ImageLoader imageLoader = newImageLoader(mQueue, new BitmapCache());
这样就可以充分利用Volley的ImageLoader功能了。此外,Volley还提供了NetworkImageView来加载网络图片。
2.2 使用场景
如果你的工程项目是一个比较小的项目,或者要求不是很高,处理比较简单,可以使用这个库,使用这个库没有提供任何的图片处理的操作,在图片处理上还是不够完善,强大。
3. Freso
Freso 是Facebook推出的一款用于Android应用中展示图片的强大图片库,它能够从网络、本地存储和本地资源中加载图片。而且,Fresco使用的是公共内存区为了节省数据和CPU,它拥有三级缓存。当图片不再显示在屏幕上时,及时地释放内存和空间占用。
Fresco 支持Android2.3(API level 9) 及其以上系统。Fresco将图片放到一个特别的内存区域。当然,在图片不显示的时候,占用的内存会自动被释放。这会使得APP更加流畅,减少因图片内存占用而引发的OOM。
3.1 使用特性
(1) Fresco 的 Drawees 设计,带来的一些特性
a. 自定义居中焦点
b. 圆角图,圆圈也行。
c. 下载失败之后,点击重现下载
d. 自定义占位图,自定义overlay, 或者进度条
e. 指定用户按压时的overlay
(2) Fresco 的 image pipeline 设计,允许用户在多方面控制图片的加载
a. 为同一个图片指定不同的远程路径,或者使用已经存在本地缓存中的图片
b. 先显示一个低解析度的图片,等高清图下载完之后再显示高清图
c. 加载完成回调通知
d. 对于本地图,如有缩略图,在大图加载完成之前,可先显示缩略图
e. 缩放或者旋转图片
f. 处理已下载的图片
g. 图片的渐进式呈现,支持加载Gif图,支持WebP格式。
3.2 使用方法
(1)添加库依赖
dependencies
{
compile 'com.facebook.fresco:fresco:0.5.0+'
}
(2) application类onCreate方法中添加
Fresco.initialize(context);
(3)使用控件SimpleDraweeView
该控件可以设置:
占位图—placeholderImage
正在加载图—progressBarImage
失败图—failureImage
重试图—retryImage
淡入淡出动画—fadeDuration
背景图—backgroundImage
叠加图—overlayImage
圆形图—roundAsCircle
圆角图—roundedCornerRadius
圆形圆角边框宽度及颜色—roundingBorder
圆形或圆角图像底下的叠加颜色—roundWithOverlayColor
(4) 显示图片
Uri imageUri=Uri.Parse(“url”);
simpleDraweeView.setImageUri(imageUri);
4. Picasso
Picasso 是Square开源的一个用于Android系统下载和缓存图片的项目,使用4.0+系统上的HTTP缓存来代替磁盘缓存,可以加载网络文件和本地资源文件。
4.1 使用特性
-
Adapter的重用会被自动检测到,Picasso会取消上次的加载使用最少的内存
-
完成复杂的图片转换,自动添加磁盘和内存缓存转换图片以适合所显示的ImageView,来减少内存消耗,还可以设置自定义转换来实现高级效果。
4.2 使用场景
对于图片有一些具体的要求的话,建议使用这个库,但是这个库当你需要对图片作一些具体的操作比如加载圆角矩形图片、裁剪图片为圆形时,需要你自己写一些操作,如果基础不够好的会感觉很困难。这个库我们只能看到结果,无法关心图片的下载过程。
4.3使用方法
它的使用方法和Glide很相似,在Glide中一起在讲述,并作简单的比较。
5. Glide
Glide是Google推荐的一个开源的图片加载和缓存库,是一个高效、开源的媒体管理框架,它遵循BSD、MIT以及Apache 2.0协议发布。创建Glide的主要目的有两个,一个是实现平滑的图片列表滚动效果,另一个是支持远程图片的获取、大小调整和展示。Glide具有获取、解码和展示视频剧照、图片、动画等功能,它还有灵活的API,这些API使开发者能够将Glide应用在几乎任何网络协议栈里,同时还支持使用Gradle以及Maven进行构建。它有很多新功能,如支持Gif 动画和视频剧照解码、智能的暂停和重新开始请求、支持缩略图等。
5.1 使用特性
a. GIF 动画的解码: 通过调用 Glide.with(context).load(imageURL)方法 ,GIF动画图片可以自动显示为动画效果。如果想有更多的控制,还可以使用Glide.with(context).load(imageURL).asBitmap()方法加载静态图片,使用Glide.with(context).load(imageURL).asGif()方法加载动画图片。
b. 缩略图的支持: 为了减少在同一个view组件里同时加载多张图片的时间,可以调用 Glide.with(context).load(imageURL).thumbnail(“缩略比例“).into(view) 方法加载一个缩略图,还可以控制thumbnail()中的参数的大小,以控制显示不同比例大小的缩略图。
c. Activity 生命周期的集成: 当Activity暂停和重启时,Glide能够做到智能的暂停和重新开始请求,并且当Android设备的连接状态变化时,所有失败的请求能够自动重新请求。
d. 转码的支持: Glide的 toBytes() 和 transcode() 两个方法可以用来获取、解码和变换背景图片,并且transcode() 方法还能够改变图片的样式。
e. 动画的支持: 新增支持图片的淡入淡出动画效果(调用crossFade()方法)和查看动画的属性的功能。
f. OkHttp 和Volley 的支持: 默认选择HttpUrlConnection作为网络协议栈,还可以选择OkHttp和Volley作为网络协议栈。
g. 其他功能: 如在图片加载过程中,使用Drawables对象作为占位符、图片请求的优化、图片的宽度和高度可重新设定、缩略图和原图的缓存等功能。
5.2 使用方式
Glide和Picasso使用有很大的相似度,但细节上有一些区别
a. 导入库文件
-
Picasso:
-
dependencies {
-
compile 'com.squareup.picasso:picasso:2.5.1'
}
Glid:(还依赖于Support Library v4)
-
dependencies {
-
compile 'com.github.bumptech.glide:glide:3.5.2'
-
compile 'com.android.support:support-v4:22.0.0'
}
实际的项目只需要到Glide的releases页面把最新的jar包下载后导入到本地的libs里面即可直接使用。
b. 图片加载
Picasso.with(context).load(imgUrl).resize(50,50).centerCrop().into(imageView);
Glide.with(context).load(imgUrl).centerCrop().into(imageView);
5.3 磁盘缓存策略
Glide自身内部已经实现了缓存策略,使得开发者摆脱Android图片加载的琐碎事务,专注逻辑业务的代码。
5.4 Picasso VS Glide
(1)代码设计
Glide 的代码设计得更好,因为 Glide 的 with() 方法不光接受 Context,还接受 Activity 和 Fragment。此外,with() 方法还能自动地从你放入的各种东西里面提取出 Context,供它自己使用。图片加载会和Activity/Fragment的生命周期保持一致,比如 Paused状态在暂停加载,在Resumed的时候又自动重新加载。
(2) 加载图像
Glide加载图像以及磁盘缓存的方式都要优于Picasso,速度更快,并且Glide更有利于减少OutOfMemoryError的发生,不过Picasso的图片质量更高。因为Glide默认的Bitmap格式是RGB_565 ,比Picasso使用ARGB8888格式的内存开销要小一半。Glide可以自动计算出任意情况下的ImageView大小。
(3) 磁盘缓存
Picasso和Glide在磁盘缓存策略上有很大的不同。Glide 缓存的图片和 ImageView 的尺寸相同,而 Picasso 缓存的图片和原始图片的尺寸相同。Picasso只缓存一个全尺寸的。Glide会为每种大小的ImageView缓存一次。尽管一张图片已经缓存了一次,但是假如你要在另外一个地方再次以不同尺寸显示,需要重新下载,调整成新尺寸的大小,然后将这个尺寸的也缓存起来。可以让Glide既缓存全尺寸又缓存其他尺寸,下次在任何ImageView中加载图片的时候,全尺寸的图片将从缓存中取出,重新调整大小,然后缓存。Glide的这种方式优点是加载显示非常快。而Picasso的方式则因为需要在显示之前重新调整大小而导致一些延迟,Glide可以加载GIF动态图,你可以配置图片显示的动画,而Picasso不能,Picasso只有一种动画:fading in。但是Glide 显示动画会消耗很多内存,使用时要考虑开销。