• 【Android】开源项目UniversalImageLoader及开源框架ImageLoader


    UniversalImageLoader

      简单来说就是用于加载图片的一个开源项目,在其项目介绍中是这么写的

    1. 支持多线程图片加载
    2. 提供丰富的细节配置,比如线程池大小,HTPP请求项,内存和磁盘缓存,图片显示时的参数配置等等;
    3. 提供双缓存
    4. 支持加载过程的监听;
    5. 提供图片的个性化显示配置接口;

      其他类似的项目也有很多,但这个作为github上著名的开源项目被广泛使用。第三方的包虽然好用省力,可以有效避免重复造轮子,但是却隐藏了一些开发上的细节,如果不关注其内部实现,那么将不利于掌握核心技术,当然也谈不上更好的使用它,计划分析项目的集成使用和低层实现。

      源码地址:https://github.com/nostra13/Android-Universal-Image-Loader

    简单分析

    1. 加载图片之前,先要做初始化配置,这个类似很多游戏引擎使用前要做一下初始化;
    2. 其实只做了一件事,实例化一个全局的ImageLoader对象,同时传入图片加载缓存的配置
    3. ImageLoaderConfiguration封装了基本的配置信息,比如加载图片事用的线程池大小,线程的优先级,内存缓存大小,是否支持同一图片的多尺寸缓存(默认是支持的,可以手动关闭),还有缓存的命名规则等等.

      这基本也就是几行代码,下面这张图里有实例化和初始化的过程。

      UniversalImageLoader

         关于这个实例化,是线程安全,忽略第二层判断,如果A,B线程同时执行if(instance==null),A,B都满足条件进入,此时,其中一个换锁,另一个等待,还需要再次判断instance==null(这是必要的,否则可能使得,再次实例化)这样一个单例就正常初始化了。

      配置完后,就可以开始使用了,通过ImageLoaderdisplayImage()绑定一个图片和ImageView,该方法有四个重载版本,传的参数比较多,这也印证了该项目提供每个图片单独的显示配置这一说法。

      其中参数最全的是:

    displayImage(String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener);

      另外三个其实就是减少其中几个参数用默认的值而已。

    1. String uri, 图片链接没什么疑问
    2. ImageView imageView, 图片载体控件,也没什么好说

      比较重要的是后面两个,DisplayImageOptions options,图片的参数配置对象

    options = new DisplayImageOptions.Builder() 
    .showStubImage(R.drawable.stub_image) 
    .showImageForEmptyUri(R.drawable.image_for_empty_url) 
    .cacheInMemory() 
    .cacheOnDisc() 
    .build();

      来看看都有什么信息可以配置的,

    1. 第一个是图片加载过程中显示的图片
    2. 第二个是图片加载失败时用的的图片
    3. 第三个允许内存缓存
    4. 第四个允许磁盘缓存

      除此之外还有两个

    1. imageScaleType(ImageScaleType imageScaleType)图片缩放类型
    2. displayer(BitmapDisplayer displayer)bitmap显示控制层,可以在显示图片前对Bitmap简单处理一下,这两个不是一定要设定,应为他们都有默认值。

      最后一个参数ImageLoadingListener listener当然是监听过程的回调接口


    Imageloader

      图片加载的Imageloader实现

      

      通过ImageLoader实例对象,调用public void displayImage(String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener)发放将开始加载图片

      具体过程可以分为几个阶段

    合法性检查

      主要是初始化检查和参数检查,可能会

    1. 抛出异常或是下载不受干扰可以继续;有传入的图片地址为空
    2. imageview为空
    3. 图片配置实例为空
    4. 过程监听接口为空四种情况。
    if (configuration == null) 
    { 
        throw new RuntimeException(ERROR_NOT_INIT); 
    } 
    if (imageView == null) 
    { 
        Log.w(TAG, ERROR_WRONG_ARGUMENTS); 
        return; 
    } 
    if (listener == null) 
    { 
        listener = emptyListener; 
    } 
    if (options == null) 
    { 
        options = configuration.defaultDisplayImageOptions; 
    }
    
    if (uri == null || uri.length() == 0) 
    { 
        cacheKeyForImageView.remove(imageView); 
        listener.onLoadingStarted(); 
        if (options.isShowImageForEmptyUri()) 
        { 
            imageView.setImageResource(options.getImageForEmptyUri()); 
        } else 
        { 
            imageView.setImageBitmap(null); 
        } 
        listener.onLoadingComplete(null); 
        return; 
    } 
    View Code
    1. 如果没有初始化ImageLoader是比较严重的,将直接抛出运行时异常
    2. 控件ImageView为空时不影响,可以直接退出,不再下载
    3. 图片配置和监听接口为空则将启用默认值
    4. 至于图片url的话,由于初始化时已经实例化了默认值的情形,所以将显示本地设置的默认图片,同时将控件移出HashMap。

    加载准备

    1. 这里的准备操作一个是获取图片的尺寸参数
    2. 然后根据这个参数和url生成标记ImageView的key
    3. 最后以key-value的形式存入HashMap中备用
    targetSize = getImageSizeScaleTo(imageView); 
    String memoryCacheKey = MemoryCacheKeyUtil.generateKey(uri, targetSize); 
    cacheKeyForImageView.put(imageView, memoryCacheKey); 
    View Code

    加载操作

      加载分为调用内存缓存本地缓存/网络下载,根据上一步加载准备中得到的key获取bitmap,这个过程比较发杂

      先看看从内存缓存获取图片

    if (bmp != null && !bmp.isRecycled()) 
    { 
        if (configuration.loggingEnabled) 
            Log.i(TAG, String.format(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey)); 
        listener.onLoadingStarted(); 
        Bitmap displayedBitmap = options.getDisplayer().display(bmp, imageView); 
        imageView.setImageBitmap(displayedBitmap); 
        listener.onLoadingComplete(bmp); 
    }
    View Code
    1. 根据得到的bitmap,如果不为空且未被标记为回收状态,那么就可以使用这个缓存的bitmap
    2. 调用监听接口的onLoadingStarted()处理一些加载前的操作,然后对bitmap做一些显示前的操作
    3. 这个就用到传入进来的图片显示的option配置,如果没传这个值,那启用默认值,应该是不对bitmap操作
    4. 这个默认的option使用SimpleBitmapDisplayer 实例
    5. 接下来调用监听接口的onLoadingComlete()
    6. 到此显示内存缓存图片的操作就结束了

      查看其源代码,果然是直接将bitmap设置给ImageView然后对外返回原来的Bitmap,对于这种情况后面的在此设置bitmap给imageview其实有些累赘,重复操作了。

    public final class SimpleBitmapDisplayer implements BitmapDisplayer 
    { 
        @Override 
        public Bitmap display(Bitmap bitmap, ImageView imageView) 
        { 
            imageView.setImageBitmap(bitmap); 
            return bitmap; 
        } 
    }
    View Code

      下面介绍第二种情况,就是从磁盘缓存/网新下载图片

    1. 先调用监听接口的onLoadingStarted()
    2. 接着显示一个下载过程中的图片或则干脆在下载时不显示任何图片
    3. 接着检查一下线程池是否初始化并正常工作中checkExecutors()

      查看源代码可以发现,项目用与下载的的task其实是通过ExecutorService来管理

    private void checkExecutors() 
    { 
        if (imageLoadingExecutor == null || imageLoadingExecutor.isShutdown()) 
        { 
            imageLoadingExecutor = Executors.newFixedThreadPool(configuration.threadPoolSize, configuration.displayImageThreadFactory); 
        } 
        if (cachedImageLoadingExecutor == null || cachedImageLoadingExecutor.isShutdown()) 
        { 
            cachedImageLoadingExecutor = Executors.newFixedThreadPool(configuration.threadPoolSize, configuration.displayImageThreadFactory); 
        } 
    } 
    View Code
    1. 为了下载图片这里把必要的图片信息做了一个封装传给工作线程
    2. ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageView, targetSize, options, listener, getLockForUri(uri));
    3. 现在一起看一下他的工作线程是怎么写的:LoadAndDisplayImageTask displayImageTask = new LoadAndDisplayImageTask(configuration, imageLoadingInfo, new Handler());
    4. 这里的最后一个参数是我们熟悉的Handler 实例,可以预测这个task类应该是Runnale的是子类,在run() 中根据情况向handler发送处理操作请求

    源码详细注释

      http://blog.csdn.net/wwj_748/article/details/10079311

    参考文章

      https://github.com/nostra13/Android-Universal-Image-Loader

      http://blog.csdn.net/wwj_748/article/details/10079311

      http://www.cnblogs.com/avenwu/

  • 相关阅读:
    .NET基础篇——利用泛型与反射更新实体(ADO.NET Entity Framework)(转)
    反射奥秘
    ubuntu 安装chmsee
    ubuntu安装sublime,新立得
    Ubuntu 升级VisualBox后无法启动 Kernel driver not installed
    ubuntu安装stardict并导入词典
    PHP运行模式
    Ubuntu ibus输入法图标消失的解决办法
    MySQL 获得当前日期时间(以及时间的转换)
    mysql索引之or条件
  • 原文地址:https://www.cnblogs.com/lcw/p/3597574.html
Copyright © 2020-2023  润新知