Android Universal-Image-Loader 解析

任務流圖

任務流圖中的每一步都有自己的接口(在圖片底部)來負責這部分的任務。大部分接口(除了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源碼解析

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章