Android开发中我们会经常遇到图片过多或操作不当造成OOM异常,有时虽然是解决了这个问题但却会影响程序的运行效率,例如:当用户在快速滑动滚动条的过程中,我们程序在仍在艰难的加载服务器端的图片,这样给用户造成了极不好的体验。其实网络上关于图片的异步加载和缓存的讲解很多,但是其实,写一个这方面的程序还是比较麻烦的,要考虑多线程,缓存,内存溢出等很多方面,针对这一光大开发者都会遇到的问题,一些牛人们已经帮我们解决了这一问题,今天我为大家介绍一款很流行的开源类库,可以很很好的解决大家的烦恼!

   

一.介绍:

   Android-Universal-Image-Loader是一个开源的UI组件程序,该项目的目的是提供一个可重复使用的仪器为异步图像加载,缓存和显示。

   在GitHub上面的一个开源类库(官方下载:https://github.com/nostra13/Android-Universal-Image-Loader


  特点:

       1.多线程的图像加载;

       2.图片异步加载缓存机制,包括内存缓存(软引用)及本地缓存;

       3.动态对ImageLoader的配置(线程池的大小,HTTP选项,内存和光盘高速缓存方式,显示图像,以及其他选项);

       4.对加载过程实现监听和事件处理;

       5.能够配置加载图片的显示选项,包括图片圆角处理和加载完成显示动画等;

(官方截图)

233715740.png


二.使用

  1.将下载下来的zip包,解压开得到如下图所示文件夹

233039238.jpg

  2.将universal-image-loader-1.8.6-with-sources.jar导入到新建的项目中,参考sample中的例子进行使用即可。为了让新手们快速掌握这里我简单讲解一下它的使用过程(使用该类库中ImageLoader加载图片,ListView、GridView、ViewPager)


Demo项目图解:

235758706.jpg


   

    2.1.在程序启动时,用户可以根据自己的情况初始化ImageLoaderConfiguration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public  class  MyApplication  extends  Application{
     @Override
     public  void  onCreate() {
         super .onCreate();
         initImageLoader(getApplicationContext());
     }
     /**初始化图片加载类配置信息**/
     public  static  void  initImageLoader(Context context) {
         // This configuration tuning is custom. You can tune every option, you may tune some of them,
         // or you can create default configuration by
         //  ImageLoaderConfiguration.createDefault(this);
         // method.
         ImageLoaderConfiguration config =  new  ImageLoaderConfiguration.Builder(context)
             .threadPriority(Thread.NORM_PRIORITY -  2 ) //加载图片的线程数
             .denyCacheImageMultipleSizesInMemory()  //解码图像的大尺寸将在内存中缓存先前解码图像的小尺寸。
             .discCacheFileNameGenerator( new  Md5FileNameGenerator()) //设置磁盘缓存文件名称
             .tasksProcessingOrder(QueueProcessingType.LIFO) //设置加载显示图片队列进程
             .writeDebugLogs()  // Remove for release app
             .build();
         // Initialize ImageLoader with configuration.
         ImageLoader.getInstance().init(config);
     }
}

别忘了在AndroidManifest.xml中

1
android:name= ".MyApplication"


  2.2.在MainActivity中我们做的仅仅是跳转到对应的界面,下面看一下ListView中的具体使用,剩下两个自己看一下Demo,原理一样。

ImageListActivity:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/**
  * listView中使用ImageLoader
  * @author ZHF
  *
  */
public  class  ImageListActivity  extends  AbsListViewBaseActivity {
     DisplayImageOptions options;  //配置图片加载及显示选项
     String[] imageUrls;
     @Override
     public  void  onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.ac_image_list);
         //获取url数组
         Bundle bundle = getIntent().getExtras();
         imageUrls = bundle.getStringArray(Extra.IMAGES);
         //配置图片加载及显示选项(还有一些其他的配置,查阅doc文档吧)
         options =  new  DisplayImageOptions.Builder()
             .showStubImage(R.drawable.ic_stub)     //在ImageView加载过程中显示图片
             .showImageForEmptyUri(R.drawable.ic_empty)   //image连接地址为空时
             .showImageOnFail(R.drawable.ic_error)   //image加载失败
             .cacheInMemory( true )   //加载图片时会在内存中加载缓存
             .cacheOnDisc( true )    //加载图片时会在磁盘中加载缓存
             .displayer( new  RoundedBitmapDisplayer( 20 ))   //设置用户加载图片task(这里是圆角图片显示)
             .build();
         listView = (ListView) findViewById(android.R.id.list);
         //绑定适配器
         listView.setAdapter( new  ItemAdapter());
         listView.setOnItemClickListener( new  OnItemClickListener() {
             @Override
             public  void  onItemClick(AdapterView<?> parent, View view,  int  position,  long  id) {
                 startImagePagerActivity(position);
             }
         });
     }
     @Override
     public  void  onBackPressed() {
         AnimateFirstDisplayListener.displayedImages.clear();
         super .onBackPressed();
     }
     private  void  startImagePagerActivity( int  position) {
         Intent intent =  new  Intent( this , ImagePagerActivity. class );
         intent.putExtra(Extra.IMAGES, imageUrls);
         intent.putExtra(Extra.IMAGE_POSITION, position);
         startActivity(intent);
     }
     /**自定义图片适配器**/
     class  ItemAdapter  extends  BaseAdapter {
         private  ImageLoadingListener animateFirstListener =  new  AnimateFirstDisplayListener();
         private  class  ViewHolder {
             public  TextView text;
             public  ImageView image;
         }
         @Override
         public  int  getCount() {
             return  imageUrls.length;
         }
         @Override
         public  Object getItem( int  position) {
             return  position;
         }
         @Override
         public  long  getItemId( int  position) {
             return  position;
         }
         @Override
         public  View getView( final  int  position, View convertView, ViewGroup parent) {
             View view = convertView;
             final  ViewHolder holder;
             if  (convertView ==  null ) {
                 view = getLayoutInflater().inflate(R.layout.item_list_image, parent,  false );
                 holder =  new  ViewHolder();
                 holder.text = (TextView) view.findViewById(R.id.text);
                 holder.image = (ImageView) view.findViewById(R.id.image);
                 view.setTag(holder);
             else  {
                 holder = (ViewHolder) view.getTag();
             }
             holder.text.setText( "Item "  + (position +  1 ));
             //Adds display image task to execution pool. Image will be set to ImageView when it's turn.
             imageLoader.displayImage(imageUrls[position], holder.image, options, animateFirstListener);
             return  view;
         }
     }
     /**图片加载监听事件**/
     private  static  class  AnimateFirstDisplayListener  extends  SimpleImageLoadingListener {
         static  final  List<String> displayedImages = Collections.synchronizedList( new  LinkedList<String>());
         @Override
         public  void  onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
             if  (loadedImage !=  null ) {
                 ImageView imageView = (ImageView) view;
                 boolean  firstDisplay = !displayedImages.contains(imageUri);
                 if  (firstDisplay) {
                     FadeInBitmapDisplayer.animate(imageView,  500 );  //设置image隐藏动画500ms
                     displayedImages.add(imageUri);  //将图片uri添加到集合中
                 }
             }
         }
     }
}

说明:

   1.使用ImageLoader加载图片,只要在Adapter的getView方法中调用displayImage方法完成了异步列表图片加载,其中options是之前定义的图片加载和显示选项(我们这里使用的是RoundedBitmapDisplayer圆角图片显示),animateFirstListener是当图片第一次加载的监听事件,目的在于显示一个淡入的显示效果动画,可以添加其他事件

   2.进本人测试,官网例子中的Constant类中图片的Uri在手机中链接很慢,完全达不到效果,之后我将其更改为其他一系列图片的Uri,便于观察效果!


效果图:

001446807.png   001446272.png   001447354.png


  最后,大家在使用的时候记得关注一下在/sdcard/Android/data/[package_name]/cache目录下的缓存的文件。记得定期清理缓存,否则时间一长,SD卡就会被占满了,同时也可以在ImageLoaderConfiguration中配置SD的缓存策略,限制缓存文件数量(memoryCacheSizePercentage,限制缓存文件最大尺寸(memoryCacheSize等选项。


Demo源码已上传!