ImageLoader详解(简单使用,源码分析,策略理解)
整体流程
简单使用
ImageLoaderConfiguration:全局配置:主要有线程类、缓存大小、磁盘大小、图片下载与解析的配置。
DisplayImageOptions:与展示图片相关的配置
ImageLoader:具体执行类,最终通过displayImage方法执行
//DisplayImageOptions相关设置
DisplayImageOptions options = new DisplayImageOptions.Builder().showImageOnLoading(R.drawable.loading)
// 设置图片在下载期间显示的图片
.showImageForEmptyUri(R.drawable.ic_launcher)
// 设置图片Uri为空或是错误的时候显示的图片
.showImageOnFail(R.drawable.error)
// 设置图片加载/解码过程中错误时候显示的图片
.cacheInMemory(true)
// 设置下载的图片是否缓存在内存中
.cacheOnDisk(true)
// 设置下载的图片是否缓存在SD卡中
.considerExifParams(true)
// 是否考虑JPEG图像EXIF参数(旋转,翻转)
.imageScaleType(ImageScaleType.IN_SAMPLE_INT)
// 设置图片以如何的编码方式显示
.bitmapConfig(Bitmap.Config.RGB_565)
// 设置图片的解码类型
// .decodingOptions(BitmapFactory.Options decodingOptions)
// 设置图片的解码配置
.delayBeforeLoading(0)
// int delayInMillis为你设置的下载前的延迟时间
// 设置图片加入缓存前,对bitmap进行设置
// .preProcessor(BitmapProcessor preProcessor)
.resetViewBeforeLoading(true)
// 设置图片在下载前是否重置,复位
.displayer(new RoundedBitmapDisplayer(20))
// 不推荐用!!!!是否设置为圆角,弧度为多少
.displayer(new FadeInBitmapDisplayer(100))
// 是否图片加载好后渐入的动画时间,可能会出现闪动
.build();// 构建完成
//ImageLoaderConfiguration相关设置
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
// 线程池内加载的数量
.threadPoolSize(3)
// 线程优先级
.threadPriority(Thread.NORM_PRIORITY - 2)
// 不允许缓存一张图片的不同尺寸
.denyCacheImageMultipleSizesInMemory()
// 设置内存缓存,可以自定义实现
.memoryCache(new UsingFreqLimitedMemoryCache(2 * 1024 * 1024))
// 内存缓存最大值
.memoryCacheSize(2 * 1024 * 1024)
// 磁盘缓存最大值
.diskCacheSize(50 * 1024 * 1024)
// 磁盘缓存文件名生成器
.diskCacheFileNameGenerator(new Md5FileNameGenerator())
// 设置任务的执行顺序
.tasksProcessingOrder(QueueProcessingType.LIFO)
// 缓存的文件数量
.diskCacheFileCount(100)
// 默认的显示设置
.defaultDisplayImageOptions(DisplayImageOptions.createSimple())
// 设置加载器
.imageDownloader(new BaseImageDownloader(context, 5 * 1000, 30 * 1000))
// log
.writeDebugLogs()
// 开始构建
.build();
//使用
ImageLoader.getInstance().init(config);
ImageLoader.getInstance().displayImage(bannerModel.getImageUrl(), imageView,options);
源码解析
包结构大致如下:仅对部分功能讲解
download包:封装了图片下载相关的方式
//ImageDownloader类中
public static enum Scheme {
HTTP("http"),
HTTPS("https"),
FILE("file"),
CONTENT("content"),
ASSETS("assets"),
DRAWABLE("drawable"),
UNKNOWN("");
该接口中可以看到可以从网络,文件,content,assets目录,drawable目录去下载文件
BaseImageDownloader为具体实现类(SlowNetworkImageDownloader NetworkDeniedImageDownloader在其他目录,后面再讲)
public InputStream getStream(String imageUri, Object extra) throws IOException {
switch(Scheme.ofUri(imageUri)) {
case HTTP:
case HTTPS:
//网络资源从网络下载,重定向小于5次
return this.getStreamFromNetwork(imageUri, extra);
case FILE:
//File资源从文件下载(为视频URi时使用视频缩略图)
return this.getStreamFromFile(imageUri, extra);
case CONTENT:
//content资源通过contentresolver获得(为视频URi时使用视频缩略图,联系人的URI获取联系人缩略图)
return this.getStreamFromContent(imageUri, extra);
case ASSETS://Assert,drawable 资源正常获取。
return this.getStreamFromAssets(imageUri, extra);
case DRAWABLE:
return this.getStreamFromDrawable(imageUri, extra);
case UNKNOWN:
default:
return this.getStreamFromOtherSource(imageUri, extra);
}
}
display包:封装简单的对展示图片的属性的处理(变圆形,圆角矩形,渐入动画,内阴影等)
通过实现自定义的Drawable,重写draw方法,使用BitmapShader将bitmap与paint绑定,将其绘制在自定义的不同形状的Canvas上,重写onBoundsChange方法,来控制绘制区域的大小,使其更合理的展示出来。
public class RoundedDrawable extends Drawable {
protected final float cornerRadius;
protected final int margin;
protected final RectF mRect = new RectF();
protected final RectF mBitmapRect;
protected final BitmapShader bitmapShader;
protected final Paint paint;
/**
*
* @param bitmap
* @param cornerRadius 圆角px
* @param margin
*/
public RoundedDrawable(Bitmap bitmap, int cornerRadius, int margin) {
this.cornerRadius = (float) cornerRadius;
this.margin = margin;
this.bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
this.mBitmapRect = new RectF((float) margin, (float) margin, (float) (bitmap.getWidth() - margin), (float) (bitmap.getHeight() - margin));
this.paint = new Paint();
this.paint.setAntiAlias(true); //开启抗锯齿,让图形和文字的边缘更加平滑
this.paint.setShader(this.bitmapShader);
this.paint.setFilterBitmap(true); //使用双线性过滤来绘制 Bitmap
this.paint.setDither(true); //设置图像的抖动
}
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
this.mRect.set((float) this.margin, (float) this.margin, (float) (bounds.width() - this.margin), (float) (bounds.height() - this.margin));
Matrix shaderMatrix = new Matrix();
shaderMatrix.setRectToRect(this.mBitmapRect, this.mRect, Matrix.ScaleToFit.FILL);
this.bitmapShader.setLocalMatrix(shaderMatrix);
}
public void draw(Canvas canvas) {
canvas.drawRoundRect(this.mRect, this.cornerRadius, this.cornerRadius, this.paint);
}
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
public void setAlpha(int alpha) {
this.paint.setAlpha(alpha);
}
public void setColorFilter(ColorFilter cf) {
this.paint.setColorFilter(cf);
}
}
以圆角矩形(RoundedDrawable )为例,重写Drawable 的draw方法,通过canvas绘制一个圆角矩形,通过为画笔paint设置bitmapShader的方式,用bitmap来填充画布,来完成圆角矩形的绘制。其中,onBoundsChange方法用来对对图片的大小进行转换,使其可以合适的填充到mRect中。paint的setDither,setFilterBitmap,setAntiAlias可以简单的理解为在对图片进行美化,使其更加柔和的展示。
imageWare包:对ImageView的一层包装,方便内部扩展,如动画的效果。
protected void setImageDrawableInto(Drawable drawable, View view) {
((ImageView)view).setImageDrawable(drawable);
if (drawable instanceof AnimationDrawable) {
//此处开始动画
((AnimationDrawable)drawable).start();
}
}
相关策略
网络下载策略
ImageLoader对不同的网络状态下处理下载策略是不同的,主要分为三种网络(正常、慢速、无网络权限),针对三种网络,实现了三种策略(BaseImageDownloader,SlowNetworkImageDownloader,NetworkDeniedImageDownloader )
BaseImageDownloader如上描述,处理正常网络情况以及其他的情况
SlowNetworkImageDownloader
private static class SlowNetworkImageDownloader implements ImageDownloader {
private final ImageDownloader wrappedDownloader;
public SlowNetworkImageDownloader(ImageDownloader wrappedDownloader) {
this.wrappedDownloader = wrappedDownloader;
}
public InputStream getStream(String imageUri, Object extra) throws IOException {
InputStream imageStream = this.wrappedDownloader.getStream(imageUri, extra);
switch(Scheme.ofUri(imageUri)) {
case HTTP:
case HTTPS:
return new FlushedInputStream(imageStream);
default:
return imageStream;
}
}
}
//FlushedInputStream类
public class FlushedInputStream extends FilterInputStream {
public FlushedInputStream(InputStream inputStream) {
super(inputStream);
}
public long skip(long n) throws IOException {
long totalBytesSkipped;
long bytesSkipped;
for(totalBytesSkipped = 0L; totalBytesSkipped < n; totalBytesSkipped += bytesSkipped) {
bytesSkipped = this.in.skip(n - totalBytesSkipped);
if (bytesSkipped == 0L) {
int by_te = this.read();
if (by_te < 0) {
break;
}
bytesSkipped = 1L;
}
}
return totalBytesSkipped;
}
}
可见慢网情况下使用了FilterInputStream,重写了skip方法,具体的好处大家可以百度,这里就不过多解释了
NetworkDeniedImageDownloader
private static class NetworkDeniedImageDownloader implements ImageDownloader {
private final ImageDownloader wrappedDownloader;
public NetworkDeniedImageDownloader(ImageDownloader wrappedDownloader) {
this.wrappedDownloader = wrappedDownloader;
}
public InputStream getStream(String imageUri, Object extra) throws IOException {
switch(Scheme.ofUri(imageUri)) {
case HTTP:
case HTTPS:
throw new IllegalStateException();
default:
return this.wrappedDownloader.getStream(imageUri, extra);
}
}
}
可见,在无权限的情况下,访问网络的直接抛异常,其他类型的则不影响处理
线程池策略
未完待续。。。