Android Universal-Image-Loader 解析
来源:互联网 发布:mac管理员名称是什么 编辑:程序博客网 时间:2024/04/30 06:44
任务流图
任务流图中的每一步都有自己的接口(在图片底部)来负责这部分的任务。大部分接口(除了BitmapProcessor)都拥有默认的实现从左到右依次是BaseImageDowloader、UnlimitedDiscCache、BaseImageDecoder、LruMemoryCache、SimpleBitmapDisplayer
ImageDownloader
接口
public interface ImageDownloader { InputStream getStream(String var1, Object var2) throws IOException; //返回Schema public static enum Schema{...}}
基本实现
public class BaseImageDownloader implements ImageDownloader { //... //通过URL来从不同地方获取图片资源 public InputStream getStream(String imageUri, Object extra) throws IOException { switch(...) { case 1: case 2: return this.getStreamFromNetwork(imageUri, extra); case 3: return this.getStreamFromFile(imageUri, extra); case 4: return this.getStreamFromContent(imageUri, extra); case 5: return this.getStreamFromAssets(imageUri, extra); case 6: return this.getStreamFromDrawable(imageUri, extra); case 7: default: return this.getStreamFromOtherSource(imageUri, extra); }} //网络请求逻辑 protected InputStream getStreamFromNetwork(String imageUri, Object extra) throws IOException { HttpURLConnection conn = this.createConnection(imageUri, extra); for(int redirectCount = 0; conn.getResponseCode() / 100 == 3 && redirectCount < 5; ++redirectCount) { conn = this.createConnection(conn.getHeaderField("Location"), extra); } InputStream imageStream; try { imageStream = conn.getInputStream(); } catch (IOException var7) { IoUtils.readAndCloseStream(conn.getErrorStream()); throw var7; } if(!this.shouldBeProcessed(conn)) { IoUtils.closeSilently(imageStream); throw new IOException("Image request failed with response code " + conn.getResponseCode()); } else { return new ContentLengthInputStream(new BufferedInputStream(imageStream, '耀'), conn.getContentLength()); }}//...//创建Http请求对象protected HttpURLConnection createConnection(String url, Object extra) throws IOException { String encodedUrl = Uri.encode(url, "@#&=*+-_.,:!?()/~\'%"); HttpURLConnection conn = (HttpURLConnection)(new URL(encodedUrl)).openConnection(); conn.setConnectTimeout(this.connectTimeout); conn.setReadTimeout(this.readTimeout); return conn;}}
磁盘缓存
接口
public interface DiskCache { File getDirectory(); File get(String var1); boolean save(String var1, InputStream var2, CopyListener var3) throws IOException; boolean save(String var1, Bitmap var2) throws IOException; boolean remove(String var1); void close(); void clear();}
基本实现
- BaseDiskCache (LimitedAgeDiskCache、UnlimitedDiskCache)
- LruDiskCache
图片解码
接口
public interface ImageDecoder { Bitmap decode(ImageDecodingInfo var1) throws IOException;}
图片解码信息类
public class ImageDecodingInfo { private final String imageKey; private final String imageUri; private final String originalImageUri; private final ImageSize targetSize; private final ImageScaleType imageScaleType; private final ViewScaleType viewScaleType; private final ImageDownloader downloader; private final Object extraForDownloader; private final boolean considerExifParams; private final Options decodingOptions; ...}
基本实现
public class BaseImageDecoder implements ImageDecoder { ... public BaseImageDecoder(boolean loggingEnabled) { this.loggingEnabled = loggingEnabled; } public Bitmap decode(ImageDecodingInfo decodingInfo) throws IOException { //通过decodingInfo里面的downloader获取图片流 InputStream imageStream = this.getImageStream(decodingInfo); if(imageStream == null) { L.e("No stream for image [%s]", new Object[]{decodingInfo.getImageKey()}); return null; } else { Bitmap decodedBitmap; BaseImageDecoder.ImageFileInfo imageInfo; try { imageInfo = this.defineImageSizeAndRotation(imageStream, decodingInfo); imageStream = this.resetStream(imageStream, decodingInfo); Options decodingOptions = this.prepareDecodingOptions(imageInfo.imageSize, decodingInfo); decodedBitmap = BitmapFactory.decodeStream(imageStream, (Rect)null, decodingOptions); } finally { IoUtils.closeSilently(imageStream); } if(decodedBitmap == null) { L.e("Image can\'t be decoded [%s]", new Object[]{decodingInfo.getImageKey()}); } else { decodedBitmap = this.considerExactScaleAndOrientatiton(decodedBitmap, decodingInfo, imageInfo.exif.rotation, imageInfo.exif.flipHorizontal); } return decodedBitmap; } } ....}
内存缓存
接口
public interface MemoryCache { boolean put(String var1, Bitmap var2); Bitmap get(String var1); Bitmap remove(String var1); Collection<String> keys(); void clear();}
基本实现
public class LruMemoryCache implements MemoryCache { private final LinkedHashMap<String, Bitmap> map; private final int maxSize; private int size; public LruMemoryCache(int maxSize) { if(maxSize <= 0) { throw new IllegalArgumentException("maxSize <= 0"); } else { this.maxSize = maxSize; this.map = new LinkedHashMap(0, 0.75F, true); } } public final Bitmap get(String key) { if(key == null) { throw new NullPointerException("key == null"); } else { synchronized(this) { return (Bitmap)this.map.get(key); } } } public final boolean put(String key, Bitmap value) { if(key != null && value != null) { synchronized(this) { this.size += this.sizeOf(key, value); Bitmap previous = (Bitmap)this.map.put(key, value); if(previous != null) { this.size -= this.sizeOf(key, previous); } } this.trimToSize(this.maxSize); return true; } else { throw new NullPointerException("key == null || value == null"); } } private void trimToSize(int maxSize) { while(true) { synchronized(this) { if(this.size < 0 || this.map.isEmpty() && this.size != 0) { throw new IllegalStateException(this.getClass().getName() + ".sizeOf() is reporting inconsistent results!"); } if(this.size > maxSize && !this.map.isEmpty()) { Entry toEvict = (Entry)this.map.entrySet().iterator().next(); if(toEvict != null) { String key = (String)toEvict.getKey(); Bitmap value = (Bitmap)toEvict.getValue(); this.map.remove(key); this.size -= this.sizeOf(key, value); continue; } } return; } } } public final Bitmap remove(String key) { if(key == null) { throw new NullPointerException("key == null"); } else { synchronized(this) { Bitmap previous = (Bitmap)this.map.remove(key); if(previous != null) { this.size -= this.sizeOf(key, previous); } return previous; } } } public Collection<String> keys() { synchronized(this) { return new HashSet(this.map.keySet()); } } public void clear() { this.trimToSize(-1); } private int sizeOf(String key, Bitmap value) { return value.getRowBytes() * value.getHeight(); } public final synchronized String toString() { return String.format("LruCache[maxSize=%d]", new Object[]{Integer.valueOf(this.maxSize)}); }}
图片显示器
定义图片显示效果,形状或动画,很nice
接口
public interface BitmapDisplayer { void display(Bitmap var1, ImageAware var2, LoadedFrom var3);}
其中LoadedFrom代表图片加载的位置
public enum LoadedFrom { NETWORK, DISC_CACHE, MEMORY_CACHE; private LoadedFrom() { }}
基本实现
无特效
public final class SimpleBitmapDisplayer implements BitmapDisplayer { public SimpleBitmapDisplayer() { } public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) { imageAware.setImageBitmap(bitmap); }}
圆形
好好学习,内部自定义了一个CircleDrawable
public class CircleBitmapDisplayer implements BitmapDisplayer { protected final Integer strokeColor; protected final float strokeWidth; public CircleBitmapDisplayer() { this((Integer)null); } public CircleBitmapDisplayer(Integer strokeColor) { this(strokeColor, 0.0F); } public CircleBitmapDisplayer(Integer strokeColor, float strokeWidth) { this.strokeColor = strokeColor; this.strokeWidth = strokeWidth; } public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) { if(!(imageAware instanceof ImageViewAware)) { throw new IllegalArgumentException("ImageAware should wrap ImageView. ImageViewAware is expected."); } else { imageAware.setImageDrawable(new CircleBitmapDisplayer.CircleDrawable(bitmap, this.strokeColor, this.strokeWidth)); } } public static class CircleDrawable extends Drawable { protected float radius; protected final RectF mRect = new RectF(); protected final RectF mBitmapRect; protected final BitmapShader bitmapShader; protected final Paint paint; protected final Paint strokePaint; protected final float strokeWidth; protected float strokeRadius; public CircleDrawable(Bitmap bitmap, Integer strokeColor, float strokeWidth) { this.radius = (float)(Math.min(bitmap.getWidth(), bitmap.getHeight()) / 2); this.bitmapShader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP); this.mBitmapRect = new RectF(0.0F, 0.0F, (float)bitmap.getWidth(), (float)bitmap.getHeight()); this.paint = new Paint(); this.paint.setAntiAlias(true); this.paint.setShader(this.bitmapShader); this.paint.setFilterBitmap(true); this.paint.setDither(true); if(strokeColor == null) { this.strokePaint = null; } else { this.strokePaint = new Paint(); this.strokePaint.setStyle(Style.STROKE); this.strokePaint.setColor(strokeColor.intValue()); this.strokePaint.setStrokeWidth(strokeWidth); this.strokePaint.setAntiAlias(true); } this.strokeWidth = strokeWidth; this.strokeRadius = this.radius - strokeWidth / 2.0F; } protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); this.mRect.set(0.0F, 0.0F, (float)bounds.width(), (float)bounds.height()); this.radius = (float)(Math.min(bounds.width(), bounds.height()) / 2); this.strokeRadius = this.radius - this.strokeWidth / 2.0F; Matrix shaderMatrix = new Matrix(); shaderMatrix.setRectToRect(this.mBitmapRect, this.mRect, ScaleToFit.FILL); this.bitmapShader.setLocalMatrix(shaderMatrix); } public void draw(Canvas canvas) { canvas.drawCircle(this.radius, this.radius, this.radius, this.paint); if(this.strokePaint != null) { canvas.drawCircle(this.radius, this.radius, this.strokeRadius, this.strokePaint); } } public int getOpacity() { return -3; } public void setAlpha(int alpha) { this.paint.setAlpha(alpha); } public void setColorFilter(ColorFilter cf) { this.paint.setColorFilter(cf); } }}
渐入动画
public class FadeInBitmapDisplayer implements BitmapDisplayer { private final int durationMillis; private final boolean animateFromNetwork; private final boolean animateFromDisk; private final boolean animateFromMemory; public FadeInBitmapDisplayer(int durationMillis) { this(durationMillis, true, true, true); } public FadeInBitmapDisplayer(int durationMillis, boolean animateFromNetwork, boolean animateFromDisk, boolean animateFromMemory) { this.durationMillis = durationMillis; this.animateFromNetwork = animateFromNetwork; this.animateFromDisk = animateFromDisk; this.animateFromMemory = animateFromMemory; } public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) { imageAware.setImageBitmap(bitmap); if(this.animateFromNetwork && loadedFrom == LoadedFrom.NETWORK || this.animateFromDisk && loadedFrom == LoadedFrom.DISC_CACHE || this.animateFromMemory && loadedFrom == LoadedFrom.MEMORY_CACHE) { animate(imageAware.getWrappedView(), this.durationMillis); } } public static void animate(View imageView, int durationMillis) { if(imageView != null) { AlphaAnimation fadeImage = new AlphaAnimation(0.0F, 1.0F); fadeImage.setDuration((long)durationMillis); fadeImage.setInterpolator(new DecelerateInterpolator()); imageView.startAnimation(fadeImage); } }}
处理逻辑
入口
//所有的同步加载、异步加载、有回调的、没回调的都会最终调用这个方法public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageSize targetSize, ImageLoadingListener listener, ImageLoadingProgressListener progressListene //在这检查是否init ImageLoderConfiguration this.checkConfiguration(); //封装了ImageView的属性和操作 if(imageAware == null) { throw new IllegalArgumentException("Wrong arguments were passed to displayImage() method (ImageView reference must not be null)"); } else { if(listener == null) { //默认为SimpleLoadingListener listener = this.defaultListener; } if(options == null) { //如果不设置图片显示属性,则使用默认值 options = this.configuration.defaultDisplayImageOptions; } //图片URL为空 if(TextUtils.isEmpty(uri)) { this.engine.cancelDisplayTaskFor(imageAware); listener.onLoadingStarted(uri, imageAware.getWrappedView()); //是否设置图片URL为空时要设置的图片 if(options.shouldShowImageForEmptyUri()) { imageAware.setImageDrawable(options.getImageForEmptyUri(this.configuration.resources)); } else { imageAware.setImageDrawable((Drawable)null); } listener.onLoadingComplete(uri, imageAware.getWrappedView(), (Bitmap)null); } else { //图片URL不为空的情况 //没有特别设置图片显示尺寸的话,显示最大尺寸 if(targetSize == null) { targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, this.configuration.getMaxImageSize()); } //通过尺寸和URL生成内存缓存的键值 String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize); this.engine.prepareDisplayTaskFor(imageAware, memoryCacheKey); listener.onLoadingStarted(uri, imageAware.getWrappedView()); //试图取出图片缓存 Bitmap bmp = this.configuration.memoryCache.get(memoryCacheKey); ImageLoadingInfo imageLoadingInfo; //有图片缓存&&bitmap没有被回收 if(bmp != null && !bmp.isRecycled()) { L.d("Load image from memory cache [%s]", new Object[]{memoryCacheKey}); //是否要对图片进行特殊处理,比如加水印什么的(主要实现接口为BitmapProcessor) if(options.shouldPostProcess()) { //把图片加载的配置信息封装起来 imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, progressListener, this.engine.getLockForUri(uri)); //Runnable类型的处理显示图片的任务,其实还是用调用LoadAndDisplayImageTask来执行的 ProcessAndDisplayImageTask displayTask1 = new ProcessAndDisplayImageTask(this.engine, bmp, imageLoadingInfo, defineHandler(options)); if(options.isSyncLoading()) { //同步加载 displayTask1.run(); } else { //异步加载,提交到线程池 this.engine.submit(displayTask1); } } else { //图片不需要特殊处理 //使用显示器显示,默认的实现(SimpleBitmapDisplayer)就是imageAware.setImageBirmap options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE); //回调图片加载完成 listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp); } } else { if(options.shouldShowImageOnLoading()) { //在加载阶段显示的图片 imageAware.setImageDrawable(options.getImageOnLoading(this.configuration.resources)); } else if(options.isResetViewBeforeLoading()) { //加载前重置的图片 imageAware.setImageDrawable((Drawable)null); } imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, progressListener, this.engine.getLockForUri(uri)); //新建加载和显示任务,也是Runnable类型的 LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(this.engine, imageLoadingInfo, defineHandler(options)); if(options.isSyncLoading()) { displayTask.run(); } else { this.engine.submit(displayTask); } } } }
任务类型
处理和显示
图片在内存已缓存,执行此任务
final class ProcessAndDisplayImageTask implements Runnable { private static final String LOG_POSTPROCESS_IMAGE = "PostProcess image before displaying [%s]"; private final ImageLoaderEngine engine; private final Bitmap bitmap; private final ImageLoadingInfo imageLoadingInfo; private final Handler handler; //... public void run() { L.d("PostProcess image before displaying [%s]", new Object[]{this.imageLoadingInfo.memoryCacheKey}); BitmapProcessor processor = this.imageLoadingInfo.options.getPostProcessor(); Bitmap processedBitmap = processor.process(this.bitmap); DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(processedBitmap, this.imageLoadingInfo, this.engine, LoadedFrom.MEMORY_CACHE); LoadAndDisplayImageTask.runTask(displayBitmapTask, this.imageLoadingInfo.options.isSyncLoading(), this.handler, this.engine); }}
加载和(处理和)显示
LoadAndDisplayImageTask,代码太多就不贴出来了,简单捋下逻辑:
1. 在run方法里面判断内存中是否有缓存,否则调用tryLoadBitmap
2. tryLoadBitmap首先判断磁盘是否有缓存,有则调用decodeImage返回Bitmap(经过以上分析,其实是调用了ImageDecoder,而在decoder里面又会调用ImageDowloader的getStream获得要解码的图片流)
3. 如果磁盘没有缓存,则调用网络获取图片流
执行引擎
从磁盘、网络加载图片等都是耗时操作,需要使用线程池执行操作。ImageLoaderEngine,顾名思义为UIL的整个动力源,没错它里面有三个线程池。
class ImageLoaderEngine { //分发任务,在run里面分发不同任务到下面两个线程池 private Executor taskExecutor; //执行ProcessAndDisplayImageTask任务,从缓存(磁盘、内存)里面读 private Executor taskExecutorForCachedImages; //执行LoadAndDisplayImageTask,从网络读 private Executor taskDistributor; //... void submit(final LoadAndDisplayImageTask task) { this.taskDistributor.execute(new Runnable() { public void run() { File image = ImageLoaderEngine.this.configuration.diskCache.get(task.getLoadingUri()); boolean isImageCachedOnDisk = image != null && image.exists(); ImageLoaderEngine.this.initExecutorsIfNeed(); if(isImageCachedOnDisk) { ImageLoaderEngine.this.taskExecutorForCachedImages.execute(task); } else { ImageLoaderEngine.this.taskExecutor.execute(task); } } }); } void submit(ProcessAndDisplayImageTask task) { this.initExecutorsIfNeed(); this.taskExecutorForCachedImages.execute(task); }}//...
相关阅读:UIL源码解析
0 0
- Android Universal-Image-Loader 解析
- Universal-Image-Loader解析
- Android-Universal-Image-Loader最新框架解析
- Android-Universal-Image-Loader最新框架解析
- Android-Universal-Image-Loader最新框架解析
- Android-Universal-Image-Loader最新框架解析
- Android-Universal-Image-Loader最新框架解析
- Android-Universal-Image-loader源码解析
- Android-Universal-Image-Loader最新框架解析
- Android universal-image-loader详细解析
- Android-Universal-Image-Loader最新框架解析
- Android-Universal-Image-Loader源码解析
- android Universal-Image-Loader 使用及解析
- android-universal-image-loader
- Android-Universal-Image-Loader
- Android-Universal-Image-Loader
- Android-Universal-Image-Loader
- Android-Universal-Image-Loader
- 栈的实现
- Java并发编程:阻塞队列
- 【实践】java.lang.Integer源码分析 -- valueOf
- 注册那些事儿—交互设计总结
- 微软将重新崛起
- Android Universal-Image-Loader 解析
- Android BitmapDrawable使用场景
- 短信验证接口0818
- 在ScrollView中ListView或者类似的组件只显示一列的问题
- dex2oat程序参数总结
- MP4文件sample读取流程
- Distributed Objects
- java中的容器讲解
- Min Stack