美文网首页
ImageLoader(UIL)

ImageLoader(UIL)

作者: 刘尔泽 | 来源:发表于2017-10-12 17:29 被阅读3次

    前言

    universalimageloader 这个图片加载框架,虽然现在 加载图片不再用了,但是设计理念还是很厉害。值得学习
    对 ImageView 做了一个封装。

    配置

    option

    加载

    displayImage(ImageView imageView){
    new ImageViewAware( imageView)
    }
    ImageViewAware,对 ImageVIew 做一个封装,然后把imageview的强引用改为弱引用。
    所以在内存不足的时候可以更好的回收这些对象

    还有对 高度 宽度 进行一个裁剪

    最终的调用

    public void displayImage(DealUrl uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener) {
     checkConfiguration(); // 判空
    
    }
    

    第一个大的操作

      if (uri == null) {
                engine.cancelDisplayTaskFor(imageView);
                listener.onLoadingStarted(uri.url, imageView);
                if (options.shouldShowImageForEmptyUri()) {
                    imageView.setImageResource(options.getImageForEmptyUri());
                } else {
                    imageView.setImageDrawable(null);
                }
                listener.onLoadingComplete(uri.url, imageView, null);
                return;
            }
    

    也就是去判断url 是空的话,移除 imageview 的一些属性,因为 ImageLoaderEngine 这个里面存辙imageview 的很多配置属性在hashmap中,所以现在给他移除,然后通过listener 的回掉告诉 图片已经加载完成了。

    第二操作
    把 图片的宽高,封装成ImageSize这个对象,
    如果宽高 <= 0 ,就拿 手机屏幕的宽高。

    然后 :

     ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageView, configuration.maxImageWidthForMemoryCache, configuration.maxImageHeightForMemoryCache);
            String memoryCacheKey = MemoryCacheUtil.generateKey(uri.url, targetSize);
            engine.prepareDisplayTaskFor(imageView, memoryCacheKey);
    
            listener.onLoadingStarted(uri.url, imageView);
            Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);
    

    我们可以看到这个get 是从lrucache 中拿的,

    最终 :

    LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo, options.getHandler());
                engine.submit(displayTask);
    

    其实所有的图片加载都是一样的,都是内部需要一个线程池,然后不断的发送消息给我们的线程池,来进行图片的加载请求。

    接下里看
    LoadAndDisplayImageTask.java

        @Override
        public void run() {
            if (waitIfPaused())
                return;
            if (delayIfNeed())
                return;
    
            ReentrantLock loadFromUriLock = imageLoadingInfo.loadFromUriLock;
            log(LOG_START_DISPLAY_IMAGE_TASK);
            if (loadFromUriLock.isLocked()) {
                log(LOG_WAITING_FOR_IMAGE_LOADED);
            }
    
            loadFromUriLock.lock();
            Bitmap bmp;
            try {
                if (checkTaskIsNotActual())
                    return;
    
                bmp = configuration.memoryCache.get(memoryCacheKey);
                if (bmp == null) {
                    bmp = tryLoadBitmap();
                    if (bmp == null)
                        return; // listener callback already was fired
                    if (checkTaskIsNotActual() || checkTaskIsInterrupted())
                        return;
                    if (options.shouldPreProcess()) {
                        log(LOG_PREPROCESS_IMAGE);
                        bmp = options.getPreProcessor().process(bmp);
                        if (bmp == null) {
                            L.e(ERROR_PRE_PROCESSOR_NULL);
                        }
                    }
    
                    if (bmp != null && options.isCacheInMemory()) {
                        log(LOG_CACHE_IMAGE_IN_MEMORY);
                        configuration.memoryCache.put(memoryCacheKey, bmp);
                    }
                } else {
                    loadedFrom = LoadedFrom.MEMORY_CACHE;
                    log(LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING);
                }
    
                if (bmp != null && options.shouldPostProcess()) {
                    log(LOG_POSTPROCESS_IMAGE);
                    bmp = options.getPostProcessor().process(bmp);
                    if (bmp == null) {
                        L.e(ERROR_POST_PROCESSOR_NULL, memoryCacheKey);
                    }
                }
            } finally {
                loadFromUriLock.unlock();
            }
    
            if (checkTaskIsNotActual() || checkTaskIsInterrupted())
                return;
    
            DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);
            displayBitmapTask.setLoggingEnabled(loggingEnabled);
            handler.post(displayBitmapTask);
        }
    

    if ( waitIfPaused()) return;

        private boolean waitIfPaused() {
            AtomicBoolean pause = engine.getPause();
            if (pause.get()) {
                synchronized (pause) {
                    log(LOG_WAITING_FOR_RESUME);
                    try {
                        pause.wait();
                    } catch (InterruptedException e) {
                        L.e(LOG_TASK_INTERRUPTED, memoryCacheKey);
                        return true;
                    }
                    log(LOG_RESUME_AFTER_PAUSE);
                }
            }
            return checkTaskIsNotActual();
        }
    

    ListView 可以设置listener 使图片不去加载,
    就是如果获得了 暂停的锁的话就可以 不去加载
    主要是用在 快速滑动时候什么的

    最后是return isTaskActual()
    view 是否已经被复用了,或者回收了。
    这样的话直接返回,不加载,因为listview 总会复用,如果快速滑动等等

    在所有的判断之后,他会获得一个
    重入锁 ReentrantLock,
    是在 ImageLoaderEngine的 getLock

     ReentrantLock getLockForUri(String uri) {
            ReentrantLock lock = uriLocks.get(uri);
            if (lock == null) {
                lock = new ReentrantLock();
                uriLocks.put(uri, lock);
            }
            return lock;
        }
    

    比如那种listview 不断的滚出屏幕再滚进来,我们不必再加载,就这种场景。
    接着

    loadFromUriLock.lock();
    拿到锁之后 在这里等待,等到 图片被加载完了之后,这个锁就会被释放。
    然后相同url 的请求就会直接往后走

    接着看 bitmap 如何创建的。
    bmp = configuration.memoryCache.get(memoryCacheKey);

    首先 会从内存缓存中去取,等等,其实就是三级缓存的概念:

     bmp = configuration.memoryCache.get(memoryCacheKey);
                if (bmp == null) {
                    bmp = tryLoadBitmap();
                    if (bmp == null)
                        return; // listener callback already was fired
                    if (checkTaskIsNotActual() || checkTaskIsInterrupted())
                        return;
                    if (options.shouldPreProcess()) {
                        log(LOG_PREPROCESS_IMAGE);
                        bmp = options.getPreProcessor().process(bmp);
                        if (bmp == null) {
                            L.e(ERROR_PRE_PROCESSOR_NULL);
                        }
                    }
    
                    if (bmp != null && options.isCacheInMemory()) {
                        log(LOG_CACHE_IMAGE_IN_MEMORY);
                        configuration.memoryCache.put(memoryCacheKey, bmp);
                    }
                } else {
                    loadedFrom = LoadedFrom.MEMORY_CACHE;
                    log(LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING);
                }
    
                if (bmp != null && options.shouldPostProcess()) {
                    log(LOG_POSTPROCESS_IMAGE);
                    bmp = options.getPostProcessor().process(bmp);
                    if (bmp == null) {
                        L.e(ERROR_POST_PROCESSOR_NULL, memoryCacheKey);
                    }
                }
    

    先从缓存中去拿,没拿到,就去tryLoadBitmap()

     private Bitmap tryLoadBitmap() {
            File imageFile = getImageFileInDiscCache();
            Bitmap bitmap = null;
            try {
                if (imageFile.exists()) {
                    log(LOG_LOAD_IMAGE_FROM_DISC_CACHE);
                    loadedFrom = LoadedFrom.DISC_CACHE;
                    bitmap = decodeImage(new DealUrl(Scheme.FILE.wrap(imageFile.getAbsolutePath())));
                }
                if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
                    log(LOG_LOAD_IMAGE_FROM_NETWORK);
                    loadedFrom = LoadedFrom.NETWORK;
                    boolean is = options.isCacheOnDisc();
    //                 is=true;
    //                String imageUriForDecoding = is ? tryCacheImageOnDisc(imageFile) : uri;
                    DealUrl imageUriForDecoding = is ? new DealUrl(tryCacheImageOnDisc(imageFile)) : uri;
                    if (!checkTaskIsNotActual()) {
                        bitmap = decodeImage(imageUriForDecoding);
                        if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
                            fireImageLoadingFailedEvent(FailType.DECODING_ERROR, null);
                        }
                    }
                }
            } catch (IllegalStateException e) {
                L.e(e);
                fireImageLoadingFailedEvent(FailType.NETWORK_DENIED, null);
            } catch (IOException e) {
                L.e(e);
                fireImageLoadingFailedEvent(FailType.IO_ERROR, e);
                if (imageFile.exists()) {
                    imageFile.delete();
                }
            } catch (OutOfMemoryError e) {
                L.e(e);
                fireImageLoadingFailedEvent(FailType.OUT_OF_MEMORY, e);
            } catch (Exception e) {
                L.e(e);
                fireImageLoadingFailedEvent(FailType.UNKNOWN, e);
            }
            return bitmap;
        }
    

    同样是先从磁盘diskCache中 去找,看看幽默没有该文件,如果有的话,直接调用,我们会调用他的 decodeImage 把image 给解码。

    如果再为空的话就从网络中去请求图片了。
    请求图片的时候,再把图片往磁盘缓存和内存缓存中各存一份。

    所有的准备完了之后
    就是 最重要的:

    DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);
            displayBitmapTask.setLoggingEnabled(loggingEnabled);
            handler.post(displayBitmapTask);
    

    他也是一个 Runnable
    我们直接看 run()
    还是判断 是不是被回收了,被重用了。
    如果有,用listener.loadCancle()
    如果没有,用BitmapDisplayer 这个 display() 方法把bitmap 显示出来。然后用listener.onLoadingComplete().

    相关文章

      网友评论

          本文标题:ImageLoader(UIL)

          本文链接:https://www.haomeiwen.com/subject/xwqoyxtx.html