Android Universal-Image-Loader 解析

来源:互联网 发布:mac管理员名称是什么 编辑:程序博客网 时间:2024/04/30 06:44

任务流图

任务流图中的每一步都有自己的接口(在图片底部)来负责这部分的任务。大部分接口(除了BitmapProcessor)都拥有默认的实现从左到右依次是BaseImageDowloader、UnlimitedDiscCache、BaseImageDecoder、LruMemoryCache、SimpleBitmapDisplayer

UIL_Flow

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