ImageLoader源码分析

ImageLoader源码分析_第1张图片
image

介绍

ImageLoader是android使用中出现比较早,使用最多的一个开源图片加载库了,随着glide,picasso等图片加载的库出现,ImageLoader使用变得越来越少。

特点

  • 支持网络,本地加载图片
  • 多线程下载图片
  • 支持图片下载进度的监听
  • 支持Listview滚动时暂停下载
  • 支持内存和磁盘的图片缓存
  • 支持自定义配置选项(线程执行器,下载器,编码器,缓存,显示图片等)

本文也是采用调用流程为线的方式来分析源码,下面我们先来看下ImageLoader整体流程图和核心类的名称和作用。

预习

流程图:


ImageLoader源码分析_第2张图片
img_01.png

重要的类:

  • ImageLoaderConfiguration

      配置信息类,使用builder设计模式
    
  • ImageLoaderEngine

    图片加载引擎,主要负责将LoadAndDisplayImageTaskProcessAndDisplayImageTask任务分发给线程池去执行
    
    Executor taskExecutor; // 用于从源获取图片的线程池
    Executor taskExecutorForCachedImages; // 从缓存池中获取图片的线程池
    Executor taskDistributor; // 分发任务的线程池,把任务分发到上面两个线程池中
    
  • ImageAware

    图片显示的对象,一般为ImageView的封装
    
  • ImageDownloader

    图片下载器,主要是从本地,网络等获取图片流
    
  • MemoryCache

    内存缓存,使用Lru算法实现对内存缓存的管理,当图片占用内存或者图片数量大于设置的阈值,回收最老的,最少使用的。
    
  • DiskCache

    本地缓存,可以自定义配置,实现对本地缓存的控制
    
  • ImageDecoder

    图片解码器,将输入流转化成Bitmap
    
  • BitmapProcessor

    图片处理器,图片的预处理和后期处理都使用这个类,图片的处理包括图片宽高等等
    
  • BitmapDisplayer

    bitmap显示器,负责将处理过后的bitmap显示
    
  • LoadAndDisplayImageTask

    加载并显示图片任务
    
  • ProcessAndDisplayImageTask

    处理并显示图片任务
    
  • DisplayBitmapTask

    显示图片任务
    


源码解析

本文不打算对ImageLoaderConfiguration配置类进行讲解,其实就是进行配置,还有使用了Builder设置模式,有兴趣的可以去看源码。

1.ImageLoader.displayImage
    public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
        ImageSize targetSize, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
        //检查配置
        checkConfiguration();
        //如果没有显示对象就抛出异常
        if (imageAware == null) {
            throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS);
        }
        //如果没有加载监听,设置默认的
        if (listener == null) {
            listener = defaultListener;
        }
        //设置显示图片的选项(缓存,加载中显示,图片处理,是否同步,编码)
        if (options == null) {
            options = configuration.defaultDisplayImageOptions;
        }
        //请求地址为空
        if (TextUtils.isEmpty(uri)) {
            //取消显示任务
            engine.cancelDisplayTaskFor(imageAware);
            //加载开始
            listener.onLoadingStarted(uri, imageAware.getWrappedView());
            //空地址情况下,是否有显示的图片
            if (options.shouldShowImageForEmptyUri()) {
                imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources));
            } else {
                imageAware.setImageDrawable(null);
            }
            //加载完成
            listener.onLoadingComplete(uri, imageAware.getWrappedView(), null);
            return;
        }
        //图片宽高为空,设置为imageview宽高
        if (targetSize == null) {
            targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());
        }
        //通过uri+targetsize生成缓存 key
        String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
        //将imageAware和缓存key关联
        engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);
        //开始加载
        listener.onLoadingStarted(uri, imageAware.getWrappedView());
        //获取缓存
        Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);
        if (bmp != null && !bmp.isRecycled()) {
            L.d(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey);

            //有缓存且可用
            //是否支持后期处理bitmap
            if (options.shouldPostProcess()) {
                //封装ImageLoadingInfo,封装处理显示任务
                //defineHandler()如果同步,返回null
                ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
                        options, listener, progressListener, engine.getLockForUri(uri));
                ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo,
                        defineHandler(options));
                //是否同步调用
                if (options.isSyncLoading()) {
                    //直接执行run(当前线程)
                    displayTask.run();
                } else {
                    //将任务交给taskExecutorForCachedImages处理缓存的线程池来执行
                    engine.submit(displayTask);
                }
            } else {
                //不支持后期处理bitmap
                //显示bitmap,加载完毕
                options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);
                listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);
            }
        } else {
            //没有缓存或者已经被回收

            //是否有显示加载中图片
            if (options.shouldShowImageOnLoading()) {
                imageAware.setImageDrawable(options.getImageOnLoading(configuration.resources));
            } else if (options.isResetViewBeforeLoading()) {
                //加载图片前重置
                imageAware.setImageDrawable(null);
            }
            //封装ImageLoadingInfo,封装加载显示任务
            //defineHandler()如果同步,返回null
            ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
                    options, listener, progressListener, engine.getLockForUri(uri));
            LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,
                    defineHandler(options));
            //是否同步
            if (options.isSyncLoading()) {
                //同步执行run
                displayTask.run();
            } else {
                //任务交给taskDistributor进行派发(根据是否有本地缓存)
                engine.submit(displayTask);
            }
        }
    }

displayImage方法中主要就是:

  • 生成 cacheKey,并和当前ImageAware保存到map中

    作用是,在线程中判断当前ImageAware是否被重用了
    
  • 有缓存,异步,生成ProcessAndDisplayImageTask,且交给缓存线程池taskExecutorForCachedImages

  • 没有缓存,异步,生成LoadAndDisplayImageTask,交给分发线程池taskDistributor来根据是否有本地缓存来分发

关于同步我们就不讲解了,我们下面从两个方面讲解:有内存缓存异步,没有内存缓存异步。

2.有内存缓存异步

2.1 ProcessAndDisplayImageTask.run()

public void run() {     
    //后期处理图片
    BitmapProcessor processor = imageLoadingInfo.options.getPostProcessor();
    Bitmap processedBitmap = processor.process(bitmap);
    //创建DisplayBitmapTask(显示任务)
    DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(processedBitmap, imageLoadingInfo, engine,
            LoadedFrom.MEMORY_CACHE);
    //执行显示任务
    LoadAndDisplayImageTask.runTask(displayBitmapTask, imageLoadingInfo.options.isSyncLoading(), handler, engine);
}

对来自内存缓存中的图片进行后期处理,创建DisplayBitmapTask,交给LoadAndDisplayImageTask的runTask方法去执行。

2.2 LoadAndDisplayImageTask.runTask

    static void runTask(Runnable r, boolean sync, Handler handler, ImageLoaderEngine engine) {
        if (sync) {
            //同步执行,当前线程显示
            r.run();
        } else if (handler == null) {
            //异步执行,且在执行线程中显示
            engine.fireCallback(r);
        } else {
            //handler执行(主线程显示)
            handler.post(r);
        }
    }

runTask方法就是判断显示任务在那个线程中执行。

2.3 DisplayBitmapTask.run

public void run() {
    //imageAware被回收
    if (imageAware.isCollected()) {
            listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
    } else if (isViewWasReused()) {
        //imageAware被重用
        listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
    } else {
        //显示图片
        displayer.display(bitmap, imageAware, loadedFrom);
        //移除cacheKeysForImageAwares中当前iamgeAware元素
        engine.cancelDisplayTaskFor(imageAware);
        //调用加载完成监听
        listener.onLoadingComplete(imageUri, imageAware.getWrappedView(), bitmap);
    }
}

如果ImageAware被回收或者被重用,那么直接回调onLoadingCancelled,负责显示图片,移除cacheKeysForImageAwares中当前ImageAware的数据,回调onLoadingComplete。

2.4 isViewWasReused()

    private boolean isViewWasReused() {
        //获取ImageAware最新的cacheKey
        String currentCacheKey = engine.getLoadingUriForView(imageAware);
        //如果当前任务的cacheKey和最新的cacheKey不一致,说明ImageAware被重用了。
        return !memoryCacheKey.equals(currentCacheKey);
    }

如果当前任务的cacheKey和最新的cacheKey不一致,说明ImageAware被重用了。

有内存缓存的情况已经分析结束。


3.没有内存缓存异步

3.1 ImageLoaderEngine.submit()

    void submit(final LoadAndDisplayImageTask task) {
        taskDistributor.execute(new Runnable() {
            @Override
            public void run() {
                //获取本地缓存
                File image = configuration.diskCache.get(task.getLoadingUri());
                boolean isImageCachedOnDisk = image != null && image.exists();
                initExecutorsIfNeed();
                //是否有本地缓存
                if (isImageCachedOnDisk) {
                    //如果有本地缓存,交给处理缓存线程池
                    taskExecutorForCachedImages.execute(task);
                } else {
                    //如果没有本地缓存,交给taskExecutor
                    taskExecutor.execute(task);
                }
            }
        });
    }

分发线程池taskDistributor根据是否有本地缓存来进行处理:

  • 有本地缓存,将任务交给缓存线程池
  • 没有本地缓存,将任务交给taskExecutor

3.2 LoadAndDisplayImageTask.run()

    public void run() {
        //如果线程被打断,进入休眠
        //如果线程没有被打断或者被唤醒,且imageAware被GC回收,或者imageAware被重用了,那么返回true
        if (waitIfPaused()) return;
        //延时加载且imageAware被GC回收,或者imageAware被重用了,那么返回true
        if (delayIfNeed()) return;

        //根据当前uri,获取锁
        ReentrantLock loadFromUriLock = imageLoadingInfo.loadFromUriLock;
        
        //加锁
        loadFromUriLock.lock();
        Bitmap bmp;
        try {
            //如果ImageAware被回收了或者被重用了,直接抛出任务取消异常TaskCancelledException
            checkTaskNotActual();
            //获取内存缓存(有可能图片被其他线程加载过)
            bmp = configuration.memoryCache.get(memoryCacheKey);
            if (bmp == null || bmp.isRecycled()) {
                //没有内存缓存

                //尝试从本地缓存或者本地或者网络等等源加载图片
                bmp = tryLoadBitmap();
                //失败的onLoadingFailed监听已经被回调,这边直接返回
                if (bmp == null) return; // listener callback already was fired

                //处理图片前,检查ImageAware被回收了,或者被重用了,或者线程被打断了
                checkTaskNotActual();
                checkTaskInterrupted();

                //内存缓存前的预处理
                if (options.shouldPreProcess()) {
                    bmp = options.getPreProcessor().process(bmp);
                }

                //添加到内存缓存中
                if (bmp != null && options.isCacheInMemory()) {
                    configuration.memoryCache.put(memoryCacheKey, bmp);
                }
            } else {
                //有内存缓存
                loadedFrom = LoadedFrom.MEMORY_CACHE;
            }

            //内存缓存后的处理
            if (bmp != null && options.shouldPostProcess()) {
                bmp = options.getPostProcessor().process(bmp);
            }
            ////处理图片后,检查ImageAware被回收了,或者被重用了,或者线程被打断了
            checkTaskNotActual();
            checkTaskInterrupted();
        } catch (TaskCancelledException e) {
            //异步且线程不是被打断的,执行onLoadingCancelled
            fireCancelEvent();
            return;
        } finally {
            //释放锁
            loadFromUriLock.unlock();
        }
        //创建DisplayBitmapTask任务
        DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);
        //执行
        runTask(displayBitmapTask, syncLoading, handler, engine);
    }

代码注释很清晰的,来个简单的流程总结:

  • 1.imageAware被GC回收,或者imageAware被重用了,直接返回true
  • 2.加锁
  • 3.获取内存缓存

有缓存:

  • 4.后期的处理
  • 5.释放锁
  • 6.创建DisplayBitmapTask任务,并执行

没有缓存

  • 4.从本地缓存中获取,如果没有从网络,sd卡等中获取图片
  • 5.预处理
  • 6.添加到内存缓存中
  • 7.后期的处理
  • 8.释放锁
  • 9.创建DisplayBitmapTask任务,并执行

下面分析核心的方法:

3.2.1 waitIfPaused() && delayIfNeed()

3.2.1.1 waitIfPaused()

    /**
     * 如果线程被打断(比如:滚动listview),线程进入wait,并释放锁
     * 如果线程没有被打断或者被唤醒,你们返回isTaskNotActual()
     * isTaskNotActual --- >如果imageAware被GC回收,或者imageAware被重用了,那么返回true
     *
     * @return
     */
    private boolean waitIfPaused() {
        //获取是否暂停状态
        AtomicBoolean pause = engine.getPause();
        if (pause.get()) {
            synchronized (engine.getPauseLock()) {
                if (pause.get()) {
                    L.d(LOG_WAITING_FOR_RESUME, memoryCacheKey);
                    try {
                        //休眠,等待被唤醒
                        engine.getPauseLock().wait();
                    } catch (InterruptedException e) {
                        L.e(LOG_TASK_INTERRUPTED, memoryCacheKey);
                        return true;
                    }
                    L.d(LOG_RESUME_AFTER_PAUSE, memoryCacheKey);
                }
            }
        }
        return isTaskNotActual();
    }
  • 如果当前的状态为暂停(pasue = true),那么进入休眠,等待被唤醒
  • 如果没有暂停或者被唤醒,那么返回isTaskNotActual()结果

上面代码有两个点需要注意:1.何时暂停,何时被唤醒。 2.isTaskNotActual()执行

1.何时暂停,何时被唤醒

我们都知道当ListView滑动的时候,ImageLoader会暂停图片加载,停止滑动时,继续加载图片,这就是暂停和唤醒的时机:

PauseOnScrollListener.onScrollStateChanged()

    public void onScrollStateChanged(AbsListView view, int scrollState) {
    switch (scrollState) {
        case OnScrollListener.SCROLL_STATE_IDLE:
            //恢复加载(唤醒)
            imageLoader.resume();
            break;
        case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
            if (pauseOnScroll) {
                //滑动,暂停加载(休眠)
                imageLoader.pause();
            }
            break;
        case OnScrollListener.SCROLL_STATE_FLING:
            if (pauseOnFling) {
                //滚动,暂停加载(休眠)
                imageLoader.pause();
            }
            break;
    }
    ...
}

ImageLoaderEngine.java

private final AtomicBoolean paused = new AtomicBoolean(false);
void pause() {
    paused.set(true);
}

void resume() {
    paused.set(false);
    synchronized (pauseLock) {
        //唤醒所有任务
        pauseLock.notifyAll();
    }
}

2.isTaskNotActual()

    /**
     * 如果imageAware被GC回收,或者imageAware被重用了,那么返回true
     * @return
     */
    private boolean isTaskNotActual() {
        return isViewCollected() || isViewReused();
    }

     /**
     * imageAware是否被GC回收
     *
     * @return 回收 --true
     */
    private boolean isViewCollected() {
        if (imageAware.isCollected()) {
            return true;
        }
        return false;
    }
    
    /**
     * ImageAware是否被重用,来显示其他图片
     * 以开始执行时,存放在cacheKeysForImageAwares中的cacheKey为最新的key
     * 如果当前的cachekey不相同,则停止舍弃当前的加载任务
     *
     * @return true--重用
     */
    private boolean isViewReused() {
        //获取最新的cachekey
        String currentCacheKey = engine.getLoadingUriForView(imageAware);
        //如果和当前cachekey不一致,说明imageAware被重用了
        boolean imageAwareWasReused = !memoryCacheKey.equals(currentCacheKey);
        if (imageAwareWasReused) {
            return true;
        }
        return false;
    }

waitIfPaused()总结:

  • 如果暂停加载,进入休眠等待被唤醒
  • 如果没有暂停,或者被唤醒,则ImageAware被重用或者imageAware被GC回收,返回true,直接结束

3.2.1.2 delayIfNeed()

private boolean delayIfNeed() {
    //延时加载
    if (options.shouldDelayBeforeLoading()) {
         try {
            //睡眠
            Thread.sleep(options.getDelayBeforeLoading());
        } catch (InterruptedException e) {
            return true;
        }
        return isTaskNotActual();
    }
    return false;
}

delayIfNeed总结:

  • 如果没有延时加载,返回false
  • 如果延时加载,进入睡眠
  • 线程被打断结束睡眠 ,返回true
  • 正常结束睡眠,则ImageAware被重用或者imageAware被GC回收,返回true,直接结束


3.2.2 checkTaskNotActual() && checkTaskInterrupted()

//如果ImageAware被GC回收,抛出TaskCancelledException
private void checkViewCollected() throws TaskCancelledException {
    if (isViewCollected()) {
        throw new TaskCancelledException();
    }
}
//如果ImageAware被重用,抛出TaskCancelledException
private void checkViewReused() throws TaskCancelledException {
    if (isViewReused()) {
        throw new TaskCancelledException();
    }
}

checkTaskNotActual总结:

  • 如果ImageAware被GC回收或者ImageAware被重用,抛出TaskCancelledException异常
  • 抛出异常的处理将在后面进行讲解


    //如果线程被打断,抛出TaskCancelledException
    private void checkTaskInterrupted() throws TaskCancelledException {
        if (isTaskInterrupted()) {
            throw new TaskCancelledException();
        }
   }
    //如果线程被打断,返回true
    private boolean isTaskInterrupted() {
        if (Thread.interrupted()) {
            return true;
        }
        return false;
    }

checkTaskInterrupted总结:

  • 如果线程被打断,抛出TaskCancelledException异常
  • 抛出异常的处理将在后面进行讲解


3.2.3 TaskCancelledException 异常处理

private void fireCancelEvent() {
    //同步或者线程被打断,直接返回
    if (syncLoading || isTaskInterrupted()) return;
    Runnable r = new Runnable() {
        @Override
        public void run() {
            listener.onLoadingCancelled(uri, imageAware.getWrappedView());
        }
    };
    //执行onLoadingCancelled
    runTask(r, false, handler, engine);
}

如果是同步或者线程被打断,直接返回,否则在指定线程中回调onLoadingCancelled


3.2.4 tryLoadBitmap()

获取图片的核心方法:

private Bitmap tryLoadBitmap() throws TaskCancelledException {
    Bitmap bitmap = null;
    try {
        //获取本地缓存
        File imageFile = configuration.diskCache.get(uri);
        if (imageFile != null && imageFile.exists() && imageFile.length() > 0) {
            //有本地缓存
            loadedFrom = LoadedFrom.DISC_CACHE;
            //检查
            checkTaskNotActual();
            //解码(file-->inputstream--->bitmap)
            bitmap = decodeImage(Scheme.FILE.wrap(imageFile.getAbsolutePath()));
        }
        //没有本地缓存
        if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
            loadedFrom = LoadedFrom.NETWORK;

            String imageUriForDecoding = uri;
            //如果开启本地缓存则调用tryCacheImageOnDisk从network,asset,content,file,drawable中获取图片,且缓存到本地缓存中
            if (options.isCacheOnDisk() && tryCacheImageOnDisk()) {
                //获取到下载的文件
                imageFile = configuration.diskCache.get(uri);
                if (imageFile != null) {
                    imageUriForDecoding = Scheme.FILE.wrap(imageFile.getAbsolutePath());
                }
            }

            //检查
            checkTaskNotActual();
            //如果上面已经获取了图片,则这里的uri为本地uri
            //如果没有,下面将从network,asset,content,file,drawable中获取图片
            //内存保存的是根据ImageView大小、scaletype、方向处理过得图片
            bitmap = decodeImage(imageUriForDecoding);

            if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
                //如果是异步且线程没有被打断,imageaware没有被回收和重用
                // 那么获取图片失败,显示失败后的图片,并且调用onLoadingFailed
                fireFailEvent(FailType.DECODING_ERROR, null);
            }
        }
    } catch (IllegalStateException e) {
        //如果是异步且线程没有被打断,imageaware没有被回收和重用
        // 那么获取图片失败,显示失败后的图片,并且调用onLoadingFailed
        fireFailEvent(FailType.NETWORK_DENIED, null);
    } catch (TaskCancelledException e) {
        throw e;
    } catch (IOException e) {
        L.e(e);
        fireFailEvent(FailType.IO_ERROR, e);
    } catch (OutOfMemoryError e) {
        L.e(e);
        fireFailEvent(FailType.OUT_OF_MEMORY, e);
    } catch (Throwable e) {
        L.e(e);
        fireFailEvent(FailType.UNKNOWN, e);
    }
    return bitmap;
}

tryLoadBitmap流程:

  • 1.获取本地缓存
  • 2.如果有本地缓存,直接解码取出
  • 3.如果开启了本地缓存,则下载图片,并将图片保存到本地缓存中
  • 4.解码图片,返回

分析几个核心方法:

1.tryCacheImageOnDisk

    /**
     * 从network,asset,content,file,drawable中获取图片,且缓存到本地缓存中
     * @return  获取图片,保存本地成功返回true
     * @throws TaskCancelledException
     */
    private boolean tryCacheImageOnDisk() throws TaskCancelledException {
        boolean loaded;
        try {
            //获取图片,并保存本地缓存
            loaded = downloadImage();
            if (loaded) {
                //将原图转化为设定的最大本地缓存图片大小(默认为0,所以不要改边)
                int width = configuration.maxImageWidthForDiskCache;
                int height = configuration.maxImageHeightForDiskCache;
                if (width > 0 || height > 0) {
                    L.d(LOG_RESIZE_CACHED_IMAGE_FILE, memoryCacheKey);
                    //从新处理图片并保存
                    resizeAndSaveImage(width, height); // TODO : process boolean result
                }
            }
        } catch (IOException e) {
            loaded = false;
        }
        return loaded;
    }   
    
private boolean downloadImage() throws IOException {
    //下载图片
    InputStream is = getDownloader().getStream(uri, options.getExtraForDownloader());
    if (is == null) {
        return false;
    } else {
        try {
            //保存到磁盘中
            return configuration.diskCache.save(uri, is, this);
        } finally {
            IoUtils.closeSilently(is);
        }
    }
}

BaseImageDownloader.getStream()

    public InputStream getStream(String imageUri, Object extra) throws IOException {
        switch (Scheme.ofUri(imageUri)) {
            case HTTP:
            case HTTPS:
                //网络图片通过HttpURLConnection实现的
                return getStreamFromNetwork(imageUri, extra);
            case FILE:
                return getStreamFromFile(imageUri, extra);
            case CONTENT:
                return getStreamFromContent(imageUri, extra);
            case ASSETS:
                return getStreamFromAssets(imageUri, extra);
            case DRAWABLE:
                return getStreamFromDrawable(imageUri, extra);
            case UNKNOWN:
            default:
                return getStreamFromOtherSource(imageUri, extra);
        }
    }

getStream总结:

  • 根据uri协议的不同,从不同的源加载图片

tryCacheImageOnDisk总结:

  • 获取图片,保存到本地缓存



2.decodeImage

private Bitmap decodeImage(String imageUri) throws IOException {
    ViewScaleType viewScaleType = imageAware.getScaleType();
    //targetSize -- imageview大小或者内存缓存的最大size
    ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey, imageUri, uri, targetSize, viewScaleType,
            getDownloader(), options);
    return decoder.decode(decodingInfo);
}

BaseImageDecoder.decode()

public Bitmap decode(ImageDecodingInfo decodingInfo) throws IOException {
    Bitmap decodedBitmap;
    ImageFileInfo imageInfo;
    //获取图片流
    InputStream imageStream = getImageStream(decodingInfo);
    if (imageStream == null) {
        return null;
    }
    try {
        //确定图片尺寸 和 旋转角度 ,生成ImageFileInfo
        imageInfo = defineImageSizeAndRotation(imageStream, decodingInfo);
        //重置流游标
        imageStream = resetStream(imageStream, decodingInfo);
        //BitmapFactory.options
        //准备decode的opions
        //decodingOptions.inSampleSize = scale; 对bitmap进行压缩
        Options decodingOptions = prepareDecodingOptions(imageInfo.imageSize, decodingInfo);
        decodedBitmap = BitmapFactory.decodeStream(imageStream, null, decodingOptions);
    } finally {
        IoUtils.closeSilently(imageStream);
    }

    if (decodedBitmap == null) {
    } else {
        //对Bitmap进行缩放,翻转和旋转等操作
        decodedBitmap = considerExactScaleAndOrientatiton(decodedBitmap, decodingInfo, imageInfo.exif.rotation,
                imageInfo.exif.flipHorizontal);
    }
    return decodedBitmap;
}

getImageStream():

    //调用BaseImageDownloader.getStream() 
    protected InputStream getImageStream(ImageDecodingInfo decodingInfo) throws IOException {
        return decodingInfo.getDownloader().getStream(decodingInfo.getImageUri(), decodingInfo.getExtraForDownloader());
    }

defineImageSizeAndRotation()

    //获取图片的size 和 旋转角度(EXIF信息)
    protected ImageFileInfo defineImageSizeAndRotation(InputStream imageStream, ImageDecodingInfo decodingInfo) {
        //BitmapFactory.options
        Options options = new Options();
        //不加载bitmap数据,只返回bitmap信息
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(imageStream, null, options);

        //EXIF信息,是可交换图像文件的缩写,是专门为数码相机的照片设定的,可以记录数码照片的属性信息和拍摄数据。
        ExifInfo exif;
        String imageUri = decodingInfo.getImageUri();
        //考虑 exif  且 文件能够定义 exif(JPEG文件)
        //附加:EXIF可以附加于JPEG、TIFF、RIFF等文件之中
        if (decodingInfo.shouldConsiderExifParams() && canDefineExifParams(imageUri, options.outMimeType)) {
            //根据文件exif信息,获取旋转角度,保存到ExifInfo中
            exif = defineExifOrientation(imageUri);
        } else {
            //创建对象(角度为0,即不旋转)
            exif = new ExifInfo();
        }
        //将   图片的宽高,旋转角度 ,exif  构建ImageFileInfo对象
        return new ImageFileInfo(new ImageSize(options.outWidth, options.outHeight, exif.rotation), exif);
    }

resetStream()

    //重置游标
    protected InputStream resetStream(InputStream imageStream, ImageDecodingInfo decodingInfo) throws IOException {
        if (imageStream.markSupported()) {
            try {
                //重置游标
                imageStream.reset();
                return imageStream;
            } catch (IOException ignored) {
            }
        }
        IoUtils.closeSilently(imageStream);
        return getImageStream(decodingInfo);
    }

prepareDecodingOptions():

    准备decode的opions,根据ImageScaleType,imagesize计算decodingOptions.inSampleSize 的值。

considerExactScaleAndOrientatiton()

    //对Bitmap进行缩放,翻转和旋转等操作
    protected Bitmap considerExactScaleAndOrientatiton(Bitmap subsampledBitmap, ImageDecodingInfo decodingInfo,
        int rotation, boolean flipHorizontal) {
        Matrix m = new Matrix();
        // Scale to exact size if need
        //缩放到指定size
        ImageScaleType scaleType = decodingInfo.getImageScaleType();
        if (scaleType == ImageScaleType.EXACTLY || scaleType == ImageScaleType.EXACTLY_STRETCHED) {
            ImageSize srcSize = new ImageSize(subsampledBitmap.getWidth(), subsampledBitmap.getHeight(), rotation);
            float scale = ImageSizeUtils.computeImageScale(srcSize, decodingInfo.getTargetSize(), decodingInfo
                    .getViewScaleType(), scaleType == ImageScaleType.EXACTLY_STRETCHED);
            if (Float.compare(scale, 1f) != 0) {
                m.setScale(scale, scale);

                if (loggingEnabled) {
                    L.d(LOG_SCALE_IMAGE, srcSize, srcSize.scale(scale), scale, decodingInfo.getImageKey());
                }
            }
        }
        // Flip bitmap if need
        //翻转bitmap
        if (flipHorizontal) {
            m.postScale(-1, 1);

            if (loggingEnabled) L.d(LOG_FLIP_IMAGE, decodingInfo.getImageKey());
        }
        // Rotate bitmap if need
        //旋转bitmap
        if (rotation != 0) {
            m.postRotate(rotation);

            if (loggingEnabled) L.d(LOG_ROTATE_IMAGE, rotation, decodingInfo.getImageKey());
        }

        Bitmap finalBitmap = Bitmap.createBitmap(subsampledBitmap, 0, 0, subsampledBitmap.getWidth(), subsampledBitmap
                .getHeight(), m, true);
        if (finalBitmap != subsampledBitmap) {
            subsampledBitmap.recycle();
        }
        return finalBitmap;
    }

decodeImage总结:

  • 获取bitmap,根据ImageView大小、scaletype、方向,旋转角度处理图片,最后返回



3.fireFailEvent

    /**
     * 如果是异步且线程没有被打断,imageaware没有被回收和重用
       那么获取图片失败,显示失败后的图片,并且调用onLoadingFailed
     * @param failType
     * @param failCause
     */
    private void fireFailEvent(final FailType failType, final Throwable failCause) {
        if (syncLoading || isTaskInterrupted() || isTaskNotActual()) return;
        Runnable r = new Runnable() {
            @Override
            public void run() {
                //设置失败后的图片
                if (options.shouldShowImageOnFail()) {
                    imageAware.setImageDrawable(options.getImageOnFail(configuration.resources));
                }
                listener.onLoadingFailed(uri, imageAware.getWrappedView(), new FailReason(failType, failCause));
            }
        };
        runTask(r, false, handler, engine);
    }


3.2.5 执行DisplayBitmapTask

同上,2.2-2.3

上一个我自己画的流程图(请忽略试用版水印几个大字):

ImageLoader源码分析_第3张图片
img_02.png


imageloader框架的源码解析就讲到这里,有兴趣的朋友可以去研究下内存缓存,本地缓存的实现等等。

你可能感兴趣的:(ImageLoader源码分析)