zoukankan      html  css  js  c++  java
  • Universal-Image-Loader完全解析(上)

    Universal-Image-Loader完全解析(上)

    基本介绍及使用

    大家平时做项目的时候,或多或少都会接触到异步加载图片,或者大量加载图片的问题,而加载图片时候经常会遇到各种问题,如oom,图片加载混乱等。对于刚入门的新手来说,这些问题目前解决起来还比较因难,因此放多开源图片加载的框架就应运而生,其中Universal-Image-Loader就是里面的佼佼者。今天我们主要是针对这个开源框架进行解析,此框架的源码存在在Github上面,具体地址:https://github.com/nostra13/Android-Universal-Image-Loader,我们可以先看看这个框架有哪些特点:

    1. 多线程图片下载,图片可来自网络、项目文件夹assets中以及drawable中等
    2. 支持随意配置ImageLoader,例如线程池,内存缓存策略,硬盘缓存策略等
    3. 支持图片的内存缓存、文件缓存以及SD卡缓存等
    4. 支持加载图片过程中的各种事件的监听
    5. 能根据ImageView的大小对Bitmap进行裁剪,减少Bitmap占用过多的内存
    6. 支持控制图片的加载过程,例如暂停图片加载、重新加载图片,一般在ListView或者 GridView等滑动时暂停加载图片,停止滑动时再去加载图片
    7. 提供在较慢的网络下对图片进行加载

    接下来我们进行这种开源框架的简单使用吧!

    添加Jar包

    新建一个Android项目,并在上面的地址中下载框架项目的jar包,然后导入到libs工程目录下
    可以新建立一个MyApplication继承Application,并在OnCreate中创建ImageLoader的配置参数,并进行初始化:

      public class MyApplication extends Application {
          @Override
          public void onCreate() {
              super.onCreate();
              //创建默认的ImageLoade配置参数
              ImageLoaderConfiguration configuration = ImageLoaderConfiguration.createDefault(this);
              //初始化ImageLoade参数
              ImageLoader.getInstance().init(configuration);
          }
      }
    

    ImageLoaderConfiguration是ImageLoader的配置参数,我们可以由代码看出其使用了单例模式和建造者模式,这里直接用createDefault()方法直接创建一个默认的ImageLoaderConfiguration,当然我们也可以直接设置自己的ImageLoaderConfiguration,设置如下

    File cacheDir = StorageUtils.getCacheDirectory(context);
    ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
        .memoryCacheExtraOptions(480, 800) // default = device screen dimensions
        .diskCacheExtraOptions(480, 800, CompressFormat.JPEG, 75, null)
        .taskExecutor(...)
        .taskExecutorForCachedImages(...)
        .threadPoolSize(3) // default
        .threadPriority(Thread.NORM_PRIORITY - 1) // 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)
        .diskCacheFileCount(100)
        .diskCacheFileNameGenerator(new HashCodeFileNameGenerator()) // default
        .imageDownloader(new BaseImageDownloader(context)) // default
        .imageDecoder(new BaseImageDecoder()) // default
        .defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default
        .writeDebugLogs()
        .build();	
    

    这里是所有的选项参数,我们在项目中根据自己需求去设置即可,一般来说,使用默认的createDefault()方法创建configuration创建即可。之后调用ImageLoader的init()方法将ImageLoaderConfiguration参数传递进去。

    进行Android Manifest配置

    <manifest>
    	<uses-permission android:name="android.permission.INTERNET" />
    	<!-- Include next permission if you want to allow UIL to cache imageson SD card -->
    	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    	...
    	<application android:name="MyApplication">
        ...
    	</application>
    </manifest>
    

    接下来我们可以进行加载图片了,首先我们可以定义好Activity的布局文件

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    	android:layout_width="match_parent"
    	android:layout_height="match_parent">
    
    	<ImageView
        	android:id="@+id/image"
        	android:layout_width="wrap_content"
        	android:layout_height="wrap_content"
        	android:layout_gravity="center"
        	android:src="@drawable/ic_picture" />
    </FrameLayout>
    

    里面只包含了一个ImageView控件,接下来我们去加载图片,我们可以观察ImageLoader项目源码可以看出,其提供了几个图片加载的方法,主要有dispalyImage()、loadImage()、loadImageSync(),其中,我们可以由方法名可以看出loadImageSync()方法是同步的,因为我们知道Android4.0版本以上有个特点,网络操作不能在主线程操作,因此,一般来说,loadImageSync()方法我们不去使用。

    LoadImage()加载图片

    我们可以使用ImageLoader的loadImage()方法来加载网络上的图片资源

    private void initData() {
    
        String imgUrl = "http://img4.imgtn.bdimg.com/it/u=1326316882,880909110&fm=21&gp=0.jpg";
    
        ImageLoader.getInstance().loadImage(imgUrl, new ImageLoadingListener() {
            @Override
            public void onLoadingStarted(String s, View view) {
                //开始加载图片
            }
    
            @Override
            public void onLoadingFailed(String s, View view, FailReason failReason) {
                //图片加载失败
            }
    
            @Override
            public void onLoadingComplete(String s, View view, Bitmap bitmap) {
                //图片加载完成
            }
    
            @Override
            public void onLoadingCancelled(String s, View view) {
                //图片加载取消
            }
        });
    }
    

    传入图片的Url地址和监听图片加载情况的ImageLoaderListener监听器,在回调方法的OnLoadingComplete()中将LoadImage()设置到所在的ImageView控件上就行了,如果你觉得传入的监听器中要实现的方法太多,那么也可以选择SimpleImageLoadingListener类,此类提供了ImageLoadingListener类中方法的实现,我们可根据需要选择实现的方法即可。

    ImageLoader.getInstance().loadImage(imgUrl,new SimpleImageLoadingListener(){
            @Override
            public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
                super.onLoadingComplete(imageUri, view, loadedImage);
            }
    });
    

    如果我们要指定图片的大小,则就应该在开始加载图片前指定一个ImageSize对象,并指定其宽高,之后再传入给loadImage()即可。

    ImageSize mImageSize = new ImageSize(100,100);
    ImageLoader.getInstance().loadImage(imgUrl,mImageSize,new SimpleImageLoadingListener(){
         @Override
         public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
              super.onLoadingComplete(imageUri, view, loadedImage);
            }
    });
    

    上面只是简单的加载网络图片,但在实际的开发中,因为涉及到是否需要使用内存缓存、是否使用文件缓存等等。这时我们就会用到DisplayImageOptions()来配置这些相关图片的选项。具体如下

    DisplayImageOptions options = new DisplayImageOptions.Builder()
        .showImageOnLoading(R.drawable.ic_loading) // resource or drawable
        .showImageForEmptyUri(R.drawable.ic_empty) // resource or drawable
        .showImageOnFail(R.drawable.ic_fail) // resource or drawable
        .resetViewBeforeLoading(false)  // default
        .delayBeforeLoading(1000)
        .cacheInMemory(false) // default
        .cacheOnDisk(false) // default
        .preProcessor(...)
        .postProcessor(...)
        .extraForDownloader(...)
        .considerExifParams(false) // default
        .imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // default
        .bitmapConfig(Bitmap.Config.ARGB_8888) // default
        .decodingOptions(...)
        .displayer(new SimpleBitmapDisplayer()) // default
        .handler(new Handler()) // default
        .build();
    

    因此我们可以进行进一步对图片进行配置

    private void initData() {
    
        String imgUrl = "http://img4.imgtn.bdimg.com/it/u=1326316882,880909110&fm=21&gp=0.jpg";
    
        final ImageSize mImageSize = new ImageSize(100, 100);
    
        DisplayImageOptions options = new DisplayImageOptions.Builder()
                .cacheInMemory(true)
                .cacheOnDisk(true)
                .bitmapConfig(Bitmap.Config.RGB_565)
                .build();
        ImageLoader.getInstance().loadImage(imgUrl, mImageSize,options, new SimpleImageLoadingListener(){
            @Override
            public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
                super.onLoadingComplete(imageUri, view, loadedImage);
                mImageView.setImageBitmap(loadedImage);
            }
        });
    }
    

    我们使用了DisplayImageOptions()来配置图片的一些选项,上面的代码将图片进行内存缓存、硬盘缓存,这样我们就不用每次加载图片就从网络上加载。但是一些选项对于loadImage()方法是无效的,如showImageOnLoading()、showImageForEmptyUri()等。

    DisplayImage()加载图片

    我们也可以选用DisplayImage()方法来加载网络图片

    DisplayImageOptions options = new DisplayImageOptions.Builder()
                .showImageOnLoading(R.drawable.ic_loading)
                .showImageOnFail(R.drawable.ic_fail)
                .cacheInMemory(true)
                .cacheOnDisk(true)
                .bitmapConfig(Bitmap.Config.RGB_565)
                .build();
        
    ImageLoader.getInstance().displayImage(imgUrl, mImageView, options);
    

    从上面可以看出,我们利用DisplayImageOptions()方法来加载图片更加方便快捷,省去了ImageLoadingListener接口的监听,也无需手动设置显示Bitmap对象,我们直接将ImageView对象作为参数传入到displayImage()即可,在配置的参数options中,我们设置了正在加载时显示的图片,以及图片加载错误时显示的图片。
    我们在加载图片的过程中,我们有需要显示图片下载进度的需求,Universal-Image-Loader当然也有这样子的功能,我们只用在displayImage()方法中传入ImageLoadingProcessListener接口,如下所示:

    ImageLoader.getInstance().displayImage(imgUrl, mImageView, options, new SimpleImageLoadingListener(), new ImageLoadingProgressListener() {
           @Override
           public void onProgressUpdate(String imageUrl, View view, int current, int total) {
               //得到图片的加载进度并进行更新
           }
    });
    

    加载其他来源的图片

    使用此项目框架不仅我们可以加载网络图片,还可以加载sd卡中的图片,ContentProvider等。使用时只需要将图片的地址Url修改一下就可以了。比如下面是加载文件系统上的图片代码

    DisplayImageOptions options = new DisplayImageOptions.Builder()
                .showImageOnLoading(R.drawable.ic_loading)
                .showImageOnFail(R.drawable.ic_fail)
                .cacheInMemory(true)
                .cacheOnDisk(true)
                .bitmapConfig(Bitmap.Config.RGB_565)
                .build();
    String imagePath = "/mnt/sdcard/image.png";
    String imagUrl = ImageDownloader.Scheme.FILE.wrap(imagePath);
    ImageLoader.getInstance().displayImage(imagUrl, mImageView, options);
    

    对于不同的图片来源,我们只要在每个图片来源的地方加上Scheme包裹起来(Content provider)除外,然后当作图片的Url传给imageLoader中,之后项目框架就会根据不同的Scheme来获取相关的输入流

    //图片来源于Content Provider
    String contentProviderUrl = "content://media/external/audio/albumart/14";
    
    //图片来源于assets资源文件夹
    String assetsUrl = Scheme.ASSENTS.wrap("image.png");
    
    //图片来源于drawable文件夹
    String drawableUrl = Scheme.DRAWABLE.wrap("R.drawable.image");
    

    ListView、GridView加载图片

    我们一般情况下要展示大量的图片时都是采用GridView、ListView组件,而我们在这些组件上滚动时,我们可以停止图片的加载,而当停止滑动时,我们就可以加载当前界面上的图片。这个框架也提供这样的功能,使用起来也很简单,这提供了PauseOnScrollListener这个类来控制ListView或者GridView滑动过程中停止加载图片。

    listView.setOnScrollListener(new PauseOnScrollListener(imageLoader, pauseOnScroll, pauseOnFling));
    gridView.setOnScrollListener(new PauseOnScrollListener(imageLoader, pauseOnScroll, pauseOnFling));
    

    其中第一个参数就是ImageLoader对象,第二个参数是控制是否在滑动过程中暂停加载图片,如果需要暂停则传入true,第三个参数是控制快速滑动界面时是否加载图片。

    OutOfMemoryError

    这个框架有很好的缓存机制,能够有效的避免OOM现象的产生,在这个框架中,对于OutOfMemoryError只是做了简单的捕获catch,从而保证我们的程序能够避免遇到OOM现象而不被crash掉,如果使用该框架经常发生OOM,则我们可以根据下面的配置进行改善。

    • 尽量减少线程池中的个数,可以在初始化的ImageLoaderConfiguration中的threadPoolSize中配置,一般是1-5线程
    • 减少图片内存消耗,在DisplayImageOptions选项中配置bitmapConfig为Bitmap.Config.RGB_565,默认是ARGB_8888,这样内存至少减少一半
    • 可使用软引用,在ImageLoaderConfiguration中配置图片的内存缓存为memoryCache(new WeakMemoryCache())或者不使用内存缓存
    • 设置图片的大小,可在DisplayImageOptions选项中设置.imageScaleType(ImageScaleType.IN_SAMPLE_INT)或者imageScaleType(imageScaleType.EXACTLY)

    总结

    通过上面的对Universal-Image-Loader框架的学习,相信对于一些简单的运用也已经来说比较了解,在使用框架的时候尽量用displayImage()方法去加载图片,loadImage()是将图片对象进行回调到ImageLoadingListener接口的onLoadingComplete()方法,从而将图片资源设置到ImageView控件上。如果我们需要裁剪图片时,可以向LoadImage()方法传递一个ImageSize对象,而displayImage()则会根据ImageView控件设置的测量值来裁剪图片,其次,displayImage()方法中对ImageView对象使用的是Weak references,方便垃圾回收器回收。

    今天的分享就到这里,接下来我会针对此框架讲解图片的缓存策略,希望各位继续关注。

  • 相关阅读:
    SQL语句之DQL数据查询语言(select、desc)
    SQL语句之DML数据操作语言(insert、delete、update)
    SQL语句之DDL跟DCL
    MySQL的命令
    数据库配置文件
    Linux-用户/用户组身份提权
    MYSQL的连接管理与启动关闭流程
    MYSQL权限的管理
    MySQL5.6与MySQL5.7的区别
    MYSQL结构
  • 原文地址:https://www.cnblogs.com/Mz-Chris/p/4603409.html
Copyright © 2011-2022 走看看