浅析Android 开源框架ImageLoader的用法

一、前言

在Android开发中,会经常涉及到显示图片的相关操作,在网上查阅资料,ImageLoader得到大家广泛的使用,本篇文章针对初使用者的一个向导,同时也是自己使用该框架的一个总结,主要包含:

   ## 源码浅析 ##
   ## 使用教程 ##
   ## 用法总结及demo下载 ##

二、源码浅析

从用法来看,我们在使用该框架的时候,会先做一个初始化操作(一般在Application中),

ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.init(ImageLoaderConfiguration imageLoaderConfiguration );

我们在源码进入该方法查看:

private volatile static ImageLoader instance;

    /** Returns singleton class instance */
    public static ImageLoader getInstance() {
        if (instance == null) {
            synchronized (ImageLoader.class) {
                if (instance == null) {
                    instance = new ImageLoader();
                }
            }
        }
        return instance;
    }

    protected ImageLoader() {
    }

    /**
     * Initializes ImageLoader instance with configuration.<br />
     * If configurations was set before ( {@link #isInited()} == true) then this method does nothing.<br />
     * To force initialization with new configuration you should {@linkplain #destroy() destroy ImageLoader} at first.
     *
     * @param configuration {@linkplain ImageLoaderConfiguration ImageLoader configuration}
     * @throws IllegalArgumentException if <b>configuration</b> parameter is null
     */
    public synchronized void init(ImageLoaderConfiguration configuration) {
        if (configuration == null) {
            throw new IllegalArgumentException(ERROR_INIT_CONFIG_WITH_NULL);
        }
        if (this.configuration == null) {
            L.d(LOG_INIT_CONFIG);
            engine = new ImageLoaderEngine(configuration);
            this.configuration = configuration;
        } else {
            L.w(WARNING_RE_INIT_CONFIG);
        }
    }

获取实例对象的方式用了单例模式,这里我们主要看一下init()这个方法:

根据注释的文档可知初始化时的一些注意事项,该方法主要对engine做初始化:

engine = new ImageLoaderEngine(configuration);

我们继续进入ImageLoaderEngine中看下这个类的构造方法:

ImageLoaderEngine(ImageLoaderConfiguration configuration) {
        this.configuration = configuration;

        taskExecutor = configuration.taskExecutor;
        taskExecutorForCachedImages = configuration.taskExecutorForCachedImages;

        taskDistributor = DefaultConfigurationFactory.createTaskDistributor();
    }

可知,该库是通过Executor对象将线程放入线程池中运行的,此构造方法里面初始化了taskExecutorForCachedImages、taskExecutor、taskDistributor这三个对象,它们都是Executor接口的实例。关于Executor接口,大家可以上网搜一下相关的知识,这里给个参考链接:

http://blog.csdn.net/minword/article/details/20565867

关于初始化的操作我们先看到这里,主要是明白了它是通过Executor来处理任务的。

接下来看下显示图片的操作,同样,我们看下源码:

/**
     * Adds display image task to execution pool. Image will be set to ImageAware when it‘s turn.<br />
     * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
     *
     * @param uri              Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
     * @param imageAware       {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view}
     *                         which should display image
     * @param options          {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
     *                         decoding and displaying. If <b>null</b> - default display image options
     *                         {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
     *                         from configuration} will be used.
     * @param listener         {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
     *                         events on UI thread if this method is called on UI thread.
     * @param progressListener {@linkplain com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener
     *                         Listener} for image loading progress. Listener fires events on UI thread if this method
     *                         is called on UI thread. Caching on disk should be enabled in
     *                         {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions options} to make
     *                         this listener work.
     * @throws IllegalStateException    if {@link #init(ImageLoaderConfiguration)} method wasn‘t called before
     * @throws IllegalArgumentException if passed <b>imageAware</b> is null
     */
    public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
            ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
        checkConfiguration();
        if (imageAware == null) {
            throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS);
        }
        if (listener == null) {
            listener = emptyListener;
        }
        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;
        }

        ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());
        String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
        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);

            if (options.shouldPostProcess()) {
                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()) {
                    displayTask.run();
                } else {
                    engine.submit(displayTask);
                }
            } else {
                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 imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
                    options, listener, progressListener, engine.getLockForUri(uri));
            LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,
                    defineHandler(options));
            if (options.isSyncLoading()) {
                displayTask.run();
            } else {
                engine.submit(displayTask);
            }
        }
    }

可知,我们将显示图片的任务加入到线程池中,之后ImageAware进行工作,

这里看下这段代码:

if (options == null) {
            options = configuration.defaultDisplayImageOptions;
}

其实就是我们设置的图片显示失败时显示的图片,这边是我们设置null时,使用了默认的图片。

回到刚才,我们发现该类读取了很多的配置参数信息,其实是我们初始化时,配置的参数,主要是配置缓存相关信息,见第二部分,使用教程。最后我们通过ImageLoaderEngine来执行显示图片的任务。

engine.submit(displayTask);

submit内部方法(ImageLoaderEngine类中):

/** Submits task to execution pool */
    void submit(ProcessAndDisplayImageTask task) {
        initExecutorsIfNeed();
        taskExecutorForCachedImages.execute(task);
    }

    private void initExecutorsIfNeed() {
        if (!configuration.customExecutor && ((ExecutorService) taskExecutor).isShutdown()) {
            taskExecutor = createTaskExecutor();
        }
        if (!configuration.customExecutorForCachedImages && ((ExecutorService) taskExecutorForCachedImages)
                .isShutdown()) {
            taskExecutorForCachedImages = createTaskExecutor();
        }
    }

以上是主要的一些源码浅析,想了解更多的可以自行翻阅源码查看。接下来我们介绍一下简单的使用教程。

三、使用教程

首先获取该库:我用的是gradle配置:

在gradle中加入:

compile ‘com.nostra13.universalimageloader:universal-image-loader:1.9.3‘

Maven 配置:

<dependency>
    <groupid>com.nostra13.universalimageloader</groupid>
    <artifactId>universal-image-loader</artifactid>
    <version>1.9.3</version>
</dependency>

在AndroidManifest.xml中加入(涉及到图片缓存读写路径,访问网络操作):

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />

之后我们就可以代码编写。

首先在activity_main中设置一个显示图片的控件:

  <ImageView
        android:id="@+id/user_image"
        android:layout_width="64dp"
        android:layout_height="64dp"/>

在MainActivity中使用ImageLoader加载一张网络图片:

imageView = (ImageView) findViewById(R.id.user_image);
ImageLoaderUtil.init(this);
String url_image = "https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWsK1HF6hhy/it/u=3266622398,4228444443&fm=116&gp=0.jpg";
ImageLoaderUtil.displayImage(url_image,imageView,ImageLoaderUtil.getAvatarDisplayOptions());

显示的效果图:

当网络路径不存在图片时(显示预设的图片):

ImageLoaderUtil是我自己封装的一个类:

主要我们得编写init()方法:

public static void init(Context context) {
        File cacheDir = getCacheDirectory(context);  //缓存文件夹路径

        ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
                .memoryCacheExtraOptions(480, 800) // default = device screen dimensions 内存缓存文件的最大长宽
                .diskCacheExtraOptions(480, 800, null)  // 本地缓存的详细信息(缓存的最大长宽),最好不要设置这个
//                .taskExecutor("")
//        .taskExecutorForCachedImages("")
        .threadPoolSize(3) // default  线程池内加载的数量
                .threadPriority(Thread.NORM_PRIORITY - 2) // default 设置当前线程的优先级
                .tasksProcessingOrder(QueueProcessingType.FIFO) // default
                .denyCacheImageMultipleSizesInMemory()
                .memoryCache(new LruMemoryCache(2 * 1024 * 1024)) //可以通过自己的内存缓存实现
                .memoryCacheSize(2 * 1024 * 1024)  // 内存缓存的最大值
                .memoryCacheSizePercentage(13) // default
                .diskCache(new UnlimitedDiscCache(cacheDir)) // default 可以自定义缓存路径
                .diskCacheSize(50 * 1024 * 1024) // 50 Mb sd卡(本地)缓存的最大值
                .diskCacheFileCount(100)  // 可以缓存的文件数量
                // default为使用HASHCODE对UIL进行加密命名, 还可以用MD5(new Md5FileNameGenerator())加密
                .diskCacheFileNameGenerator(new HashCodeFileNameGenerator())
                .imageDownloader(new BaseImageDownloader(context)) // default
                .imageDecoder(new BaseImageDecoder(true)) // l
                .defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default
                .writeDebugLogs() // 打印debug log
                .build(); //开始构建
        ImageLoader.getInstance().init(config);
    }

可参照注释对应了解参数信息。

完整的ImageLoaderUtil类:

package constraintlayout.test.test.viviant.imageloadertest.util;

import android.content.Context;
import android.util.Log;
import android.widget.ImageView;

import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiscCache;
import com.nostra13.universalimageloader.cache.disc.naming.HashCodeFileNameGenerator;
import com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
import com.nostra13.universalimageloader.core.assist.QueueProcessingType;
import com.nostra13.universalimageloader.core.decode.BaseImageDecoder;
import com.nostra13.universalimageloader.core.download.BaseImageDownloader;

import java.io.File;

import constraintlayout.test.test.viviant.imageloadertest.R;

/**
 * 作者:viviant on 2016/6/30 09:22
 * 描述:
 */
public class ImageLoaderUtil {

    private static final String PICTURE_CACHE_DIR = "picture";
    private static String TAG = "ImageLoaderUtil";

    public static void init(Context context) {
        File cacheDir = getCacheDirectory(context);  //缓存文件夹路径

        ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
                .memoryCacheExtraOptions(480, 800) // default = device screen dimensions 内存缓存文件的最大长宽
                .diskCacheExtraOptions(480, 800, null)  // 本地缓存的详细信息(缓存的最大长宽),最好不要设置这个
//                .taskExecutor("")
//        .taskExecutorForCachedImages("")
        .threadPoolSize(3) // default  线程池内加载的数量
                .threadPriority(Thread.NORM_PRIORITY - 2) // default 设置当前线程的优先级
                .tasksProcessingOrder(QueueProcessingType.FIFO) // default
                .denyCacheImageMultipleSizesInMemory()
                .memoryCache(new LruMemoryCache(2 * 1024 * 1024)) //可以通过自己的内存缓存实现
                .memoryCacheSize(2 * 1024 * 1024)  // 内存缓存的最大值
                .memoryCacheSizePercentage(13) // default
                .diskCache(new UnlimitedDiscCache(cacheDir)) // default 可以自定义缓存路径
                .diskCacheSize(50 * 1024 * 1024) // 50 Mb sd卡(本地)缓存的最大值
                .diskCacheFileCount(100)  // 可以缓存的文件数量
                // default为使用HASHCODE对UIL进行加密命名, 还可以用MD5(new Md5FileNameGenerator())加密
                .diskCacheFileNameGenerator(new HashCodeFileNameGenerator())
                .imageDownloader(new BaseImageDownloader(context)) // default
                .imageDecoder(new BaseImageDecoder(true)) // l
                .defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default
                .writeDebugLogs() // 打印debug log
                .build(); //开始构建
        ImageLoader.getInstance().init(config);
    }

    /**
     * 获取缓存文件
     *
     * @param context
     * @return
     */
    public final static File getCacheDirectory(Context context) {
        String cacheDir = SystemUtility.getAppCachePath();
        return createDir(cacheDir + PICTURE_CACHE_DIR);
    }

    private final static File createDir(String dir) {
        File appCacheDir = new File(dir);
        if (!appCacheDir.exists()) {
            if (!appCacheDir.mkdirs()) {
                Log.i(TAG, "createDir# Unable to create external cache directory");
                return null;
            }
        }
        return appCacheDir;
    }

    /**
     *显示出错,替换的图片
     * @return
     */
    public static DisplayImageOptions getAvatarDisplayOptions() {
        DisplayImageOptions avatarOptions = new DisplayImageOptions.Builder()
                .showImageOnLoading(R.drawable.error)
                .showImageForEmptyUri(R.drawable.error)
                .showImageOnFail(R.drawable.error)
                .cacheInMemory(true).cacheOnDisk(true).build();
        return avatarOptions;
    }

    /**
     * 显示图片
     *
     * @param url
     * @param imageView
     * @param options
     */
    public static void displayImage(String url, ImageView imageView,
                                    DisplayImageOptions options) {
        ImageLoader.getInstance().displayImage(url, imageView, options);
    }

}

四、用法总结及源码下载:

ImageLoader的用法总的来说还是很便捷的,我们可以设置相应的参数来初始化配置,并且它的优点是应用进行大量的访问网络图片。以上是我对该框架的一些使用方法进行总结,不足之处,欢迎批评。

源码下载(AndroidStudio直接导入):

https://github.com/viviant1224/ImageLoaderTest

时间: 2024-10-10 18:26:53

浅析Android 开源框架ImageLoader的用法的相关文章

Android开源框架Image-Loader详解

如果说评价一下哪个图片开源库最被广泛使用的话,我想应该可以说是Universal-Image-Loader,在主流的应用中如 果你随便去反编译几个,基本都能看到他的身影,它就像个图片加载守护者,默默的守护着图片加载.相信很多人对 这个异步加载图片框架还不是很熟,再加上最近它更改优化了好几个地方,而网上的大部分资料还是以前的,于是花 了几天时间专门的研究了下开源框架Universal-Image-Loader(实际上是近期项目刚好用到,且仔细的考虑过各种情 况),希望对新手能有所帮助,也希望大神能

Android开源框架ImageLoader:加载图片的三级缓存机制

前言:可从  https://github.com/nostra13/Android-Universal-Image-Loader 下载三级缓存机制的开源框架.下文简单介绍该框架中主要的常用方法,掌握这些方法,基本就可应对多数图片下载的需求. 注意:以下代码为示意代码片断,仔细读一下应能知道怎么用.蓝色表示为开源框架中的类. 1.初始化ImageLoader类对象: ImageLoader imageLoader = ImageLoader.getInstance(); imageLoader.

Android开源框架ImageLoader的完美例子

要使用ImageLoader就要到这里下载jar包: https://github.com/nostra13/Android-Universal-Image-Loader 然后导入项目中去就行了 项目文档结构图: 从界面说起,界面本身是没什么好说的,就是如何在xml当中进行定义罢了 有以下这么多个布局文件 一个一个来看呗 首先是这样的效果 这个在Android4.2.2比较好看,在Android2.3.3就显得比较挫. /2013.8.19_Universal_Image_Loader_Demo

Android开源框架 Android-Universal-Image-Loader

Android开源框架Universal-Image-Loader就像图片加载守护者,为我们提供了丰富的功能特性: (1)多线程加载图像(异步或同步): (2)高度可定制化imageloader配置(线程池.图片下载器.解码器.内存和磁盘缓存.显示图像选项等): (3)每一个显示图像有许多自定义选项(存根图片,缓存开关,解码选项,位图处理和显示等): (4)支持内存和磁盘上的图像缓存(设备的文件系统和SD卡): (5)监听加载过程(包括下载进度): 下来我们详解如何配置使用Universal-I

Android开源框架Afinal第一篇——揭开圣女的面纱

Android开源框架Afinal第一篇——揭开圣女的面纱 分类: Android开源框架哪点事2013-09-02 14:25 260人阅读 评论(0) 收藏 举报 Afinal 这是Afinal在github的地址:https://github.com/yangfuhai/afinal Afinal这个框架主要分4块: 1.FinalDB模块:android中的orm框架,一行代码就可以进行增删改查.支持一对多,多对一等查询. 2.FinalActivity模块:android中的ioc框架

Android 开源框架Universal-Image-Loader完全解析(三)---源代码解读

本篇文章主要是带大家从源码的角度上面去解读这个强大的图片加载框架,自己很久没有写文章了,感觉生疏了许多,距离上一篇文章三个月多了,确实是自己平常忙,换了工作很多东西都要去看去理解,然后加上自己也懒了,没有以前那么有激情了,我感觉这节奏不对,我要继续保持以前的激情,正所谓好记性不如烂笔头,有时候自己也会去翻看下之前写的东西,我觉得知识写下来比在脑海中留存的更久,今天就给大家来读一读这个框架的源码,我感觉这个图片加载框架确实写的很不错,读完代码自己也学到了很多.我希望大家可以先去看下Android

Android开源框架Universal-Image-Loader解析(一)

来自xiaanming的一篇博客:Android开源框架Universal-Image-Loader解析之基本介绍及使用. 相信大家平时做Android应用的时候,多少会接触到异步加载图片,或者加载大量图片的问题,而加载图片我们常常会遇到许多的问题,比如说图片的错乱,OOM等问题,对于新手来说,这些问题解决起来会比较吃力,所以就有很多的开源图片加载框架应运而生,比较著名的就是Universal-Image-Loader,相信很多朋友都听过或者使用过这个强大的图片加载框架,今天这篇文章就是对这个框

Android 开源框架Universal-Image-Loader完全解析(二)--- 图片缓存策略详解

本篇文章继续为大家介绍Universal-Image-Loader这个开源的图片加载框架,介绍的是图片缓存策略方面的,如果大家对这个开源框架的使用还不了解,大家可以看看我之前写的一篇文章Android 开源框架Universal-Image-Loader完全解析(一)--- 基本介绍及使用,我们一般去加载大量的图片的时候,都会做缓存策略,缓存又分为内存缓存和硬盘缓存,我之前也写了几篇异步加载大量图片的文章,使用的内存缓存是LruCache这个类,LRU是Least Recently Used 近

greenDao android开源框架数据库更新表的问题

最近使用greenDao当android应用升级数据库新增表或者修改表,发现数据被清空的问题 查找资料也没有找到解决方案,最后查看代码发现需要自己修改SQLiteOpenHelper 1.找到greenDao生成的DaoMaster.java文件,里面有SQLiteOpenHelper实现 2.修改DevOpenHelper类里的   public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) 方法 通过old