imageloader详解

来源:互联网 发布:幺正矩阵 编辑:程序博客网 时间:2024/06/06 01:48

最近在做相册管理的时候遇到了gridview中加在大量本地图片的情况,第一次只是很简单的用了gridview 没有添加任何的其他加速功能,因为加载的速度慢的真心可以。虽然功能是实现了,但是无法让人感到愉快。

百度了一下找到了imageloader这个开源框架,可以大大提升加载的速度因为本身其使用了异步加载的方法。

它基本可以做到一下几点:

  1. 多线程下载图片,图片可以来源于网络,文件系统,项目文件夹assets中以及drawable中等
  2. 支持随意的配置ImageLoader,例如线程池,图片下载器,内存缓存策略,硬盘缓存策略,图片显示选项以及其他的一些配置
  3. 支持图片的内存缓存,文件系统缓存或者SD卡缓存
  4. 支持图片下载过程的监听
  5. 根据控件(ImageView)的大小对Bitmap进行裁剪,减少Bitmap占用过多的内存
  6. 较好的控制图片的加载过程,例如暂停图片加载,重新开始加载图片,一般使用在ListView,GridView中,滑动过程中暂停加载图片,停止滑动的时候去加载图片
  7. 提供在较慢的网络下对图片进行加载
这个是在github上面的imageloader的项目https://github.com/nostra13/Android-Universal-Image-Loader。



接下来就是如何使用的问题:

1、如果是需要从网上下载图片的话,那么就需要设置权限了 首先要在androidmanifest中获得权限

<uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />这两句是分别是获得网络权限和SD卡的读写权限。然后初始化imageloader需要自己写一个application来进行初始化
package com.example.administrator.first;import android.app.Application;import com.nostra13.universalimageloader.core.ImageLoader;import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;/** * Created by Administrator on 2015/7/16. */public class MyApplication extends Application {    @Override    public void onCreate() {        super.onCreate();        ImageLoaderConfiguration configuration = ImageLoaderConfiguration                .createDefault(this);        ImageLoader.getInstance().init(configuration);    }}

这里我使用的是默认初始化的,如果自己需要设定的话可以自己选择。
File cacheDir = StorageUtils.getCacheDirectory(context);//获得缓存的默认地址ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)        .memoryCacheExtraOptions(480, 800) // 设置每个缓存文件的大小        .diskCacheExtraOptions(480, 800, null)//设置每个硬盘缓存文件的大小        .taskExecutor(...)        .taskExecutorForCachedImages(...)        .threadPoolSize(3) <span><span class="comment">// 线程池内加载的数量</span><span>  </span></span>        .threadPriority(Thread.NORM_PRIORITY - 2) <span><span class="comment">// 线程优先级</span></span>        .tasksProcessingOrder(QueueProcessingType.FIFO) // default        .denyCacheImageMultipleSizesInMemory()        .memoryCache(new LruMemoryCache(2 * 1024 * 1024))        .memoryCacheSize(2 * 1024 * 1024)        .memoryCacheSizePercentage(13) // default        .diskCache(new UnlimitedDiscCache(cacheDir)) // 硬盘缓存的方法        .diskCacheSize(50 * 1024 * 1024)//<span><span class="comment">硬盘缓存50MB</span><span></span></span>        .diskCacheFileCount(100)<span><span></span><span class="comment">//缓存的File数量</span></span>        .diskCacheFileNameGenerator(new HashCodeFileNameGenerator()) <span><span></span><span class="comment">//将保存的时候的URI名称用HASHCODE加密</span><span> </span></span>        .imageDownloader(new BaseImageDownloader(context)) // default        .imageDecoder(new BaseImageDecoder()) // default        .defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default        .writeDebugLogs()        .build();

因为默认的也差不多可以使用所以没怎么选,如果需要选择的话可以百度相应的选项的意思。
还需要在androidmanifest中在运行APP的时候先运行初始化的application。
<application android:name="MyApplication">假如这句话就会让这个先进行初始化。


然后是在你需要使用的使用初始化DisplayImageOptions对象
DisplayImageOptions options = new DisplayImageOptions.Builder()          // 设置图片在下载期间显示的图片                  .showImageOnLoading(R.drawable.ic_stub)                  // 设置图片Uri为空或是错误的时候显示的图片                  .showImageForEmptyUri(R.drawable.ic_stub)                  // 设置图片加载/解码过程中错误时候显示的图片                  .showImageOnFail(R.drawable.ic_error)                  // 设置下载的图片是否缓存在内存中                  .cacheInMemory(false)                  // 设置下载的图片是否缓存在SD卡中                  .cacheOnDisc(true)                  // 保留Exif信息                  .considerExifParams(true)                  // 设置图片以如何的编码方式显示                  .imageScaleType(ImageScaleType.EXACTLY_STRETCHED)                  // 设置图片的解码类型                  .bitmapConfig(Bitmap.Config.RGB_565)                  // .decodingOptions(android.graphics.BitmapFactory.Options                  // decodingOptions)//设置图片的解码配置                  .considerExifParams(true)                  // 设置图片下载前的延迟                  .delayBeforeLoading(100)// int                  // delayInMillis为你设置的延迟时间                  // 设置图片加入缓存前,对bitmap进行设置                  // .preProcessor(BitmapProcessor preProcessor)                  .resetViewBeforeLoading(true)// 设置图片在下载前是否重置,复位                  // .displayer(new RoundedBitmapDisplayer(20))//是否设置为圆角,弧度为多少                  .displayer(new FadeInBitmapDisplayer(100))// 淡入                  .build();  



这是XML的布局:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="fill_parent"    android:layout_height="fill_parent"    android:orientation="vertical" >    <Gallery        android:id="@+id/myGallery"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:gravity="center_vertical"        android:spacing="3px" /></LinearLayout>
.java代码
package com.example.administrator.text;import android.app.Activity;import android.os.Bundle;import android.os.Environment;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.widget.AdapterView;import android.widget.Gallery;import android.widget.Toast;import com.nostra13.universalimageloader.core.download.ImageDownloader;import java.io.File;import java.util.ArrayList;public class MainActivity extends Activity {    private Gallery myGallery;    private ArrayList<String>array = null;    private String []url;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))//查看当前的SD是不是存在        {            String path = Environment.getExternalStorageDirectory() + "/DCIM/Camera/";//获得照片的文件夹            array = readlist(path);//获得照片的URL            url = new String[array.size()];            for(int i = 0;i < array.size();i++)            {                url[i] = ImageDownloader.Scheme.FILE.wrap(array.get(i));            }            myGallery = (Gallery) findViewById(R.id.myGallery);            myGallery.setAdapter(new ImageGalleryAdapter(this,url));            myGallery.setOnItemClickListener(new OnItemClickListenerImpl());        }        else        {            Toast.makeText(this,"无SD卡请插入",Toast.LENGTH_LONG);        }    }    private class OnItemClickListenerImpl implements AdapterView.OnItemClickListener {//设置选择了其中的响应        public void onItemClick(AdapterView<?> parent, View view, int position,                                long id) {            Toast.makeText(MainActivity.this, String.valueOf(position),                    Toast.LENGTH_SHORT).show();        }    }        @Override    public boolean onCreateOptionsMenu(Menu menu) {        // Inflate the menu; this adds items to the action bar if it is present.        getMenuInflater().inflate(R.menu.menu_main, menu);        return true;    }    @Override    public boolean onOptionsItemSelected(MenuItem item) {        // Handle action bar item clicks here. The action bar will        // automatically handle clicks on the Home/Up button, so long        // as you specify a parent activity in AndroidManifest.xml.        int id = item.getItemId();        //noinspection SimplifiableIfStatement        if (id == R.id.action_settings) {            return true;        }        return super.onOptionsItemSelected(item);    }    private ArrayList<String> readlist(String path)//获得文件夹中的照片的URL    {        ArrayList<String> list = new ArrayList<String>();        File file = new File(path);        File []files = file.listFiles();        if(files != null)        {            for(File f : files)            {                String filename = f.getName();                if(filename.lastIndexOf(".") > 0 && filename.substring(filename.lastIndexOf(".")+1,filename.length()).equals("jpg"))                {                    list.add(f.getPath());                }            }        }        return list;    }}

适配器代码:
package com.example.administrator.text;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import com.nostra13.universalimageloader.core.DisplayImageOptions;import com.nostra13.universalimageloader.core.ImageLoader;/** * Created by Administrator on 2015/7/17. */public class ImageGalleryAdapter extends BaseAdapter {    private Context context;    private String []url;    private DisplayImageOptions options;    public ImageGalleryAdapter(Context context,String []s) {        this.context = context;        this.url = s;        options = new DisplayImageOptions.Builder()//使用imageloader的方法进行初始化                .showStubImage(R.drawable.ic_stub)                .showImageForEmptyUri(null)                .showImageOnFail(R.drawable.ic_error)                .cacheInMemory()                .cacheOnDisc()                .build();    }    public int getCount() { // 取得要显示内容的数量        return url.length;    }    public Object getItem(int position) { // 每个资源的位置        return position;    }    public long getItemId(int position) { // 取得每个项的ID        return position;    }    // 将资源设置到一个组件之中,很明显这个组件是ImageView    public View getView(int position, View convertView, ViewGroup parent) {        View view;        view = convertView;        ImageView imageView;        if (convertView == null) {            view = LayoutInflater.from(context).inflate(R.layout.item_list_image, null);            imageView = (ImageView) view.findViewById(R.id.image);            view.setTag(imageView);        }        else {            imageView = (ImageView) view.getTag();        }        ImageLoader.getInstance().displayImage(url[position], imageView, options);        ImageLoader.getInstance().displayImage(url[position],imageView);        return view;    }}

item_list_imame.xml:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="fill_parent"    android:layout_height="wrap_content" >        <ImageView        android:layout_width="80dip"        android:layout_height="70dip"        android:id="@+id/image"        android:scaleType="centerCrop"        android:adjustViewBounds="true"        android:layout_marginLeft = "5dp"/></LinearLayout>

经过以上的测试,发现和正常的加载速度快了很多,如果不使用异步和这个框架的话,那么打开的开始就是白屏等好久才会出现,有了这个的话会慢慢的跳出来,如果存在了缓存
那么就是更加快。

最重要的是这样可以保证内存不会炸。因为如果同时加在所有的图片的话同时图片有几千张,内存直接炸。

如何还存在内存不够,我们则需要在初始化的时候进行一些调整 .threadPoolSize(// default  使用的线程数目减少

或者是在使用
DisplayImageOptions的时候不添加缓存就可以

  1.   .cacheInMemory(false// default  
  2.         .cacheOnDisk(false// default 

或者是在DisplayImageOptions选项中配置bitmapConfig为Bitmap.Config.RGB_565,因为默认是ARGB_8888, 使用RGB_565会比使用ARGB_8888少消耗2倍的内存
这样就是初步的减少内存爆炸的可能性。

在内存缓存方面:

1. 只使用的是强引用缓存 

  • LruMemoryCache(这个类就是这个开源框架默认的内存缓存类,缓存的是bitmap的强引用,下面我会从源码上面分析这个类)

2.使用强引用和弱引用相结合的缓存有

  • UsingFreqLimitedMemoryCache(如果缓存的图片总量超过限定值,先删除使用频率最小的bitmap)
  • LRULimitedMemoryCache(这个也是使用的lru算法,和LruMemoryCache不同的是,他缓存的是bitmap的弱引用)
  • FIFOLimitedMemoryCache(先进先出的缓存策略,当超过设定值,先删除最先加入缓存的bitmap)
  • LargestLimitedMemoryCache(当超过缓存限定值,先删除最大的bitmap对象)
  • LimitedAgeMemoryCache(当 bitmap加入缓存中的时间超过我们设定的值,将其删除)

3.只使用弱引用缓存

  • WeakMemoryCache(这个类缓存bitmap的总大小没有限制,唯一不足的地方就是不稳定,缓存的图片容易被回收掉)
我们可以在初始化的时候选择自己想用的
    ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder(this)              .memoryCache(new WeakMemoryCache())              .build();  <strong>在硬盘缓存方面:</strong><ul><li>FileCountLimitedDiscCache(可以设定缓存图片的个数,当超过设定值,删除掉最先加入到硬盘的文件)</li><li>LimitedAgeDiscCache(设定文件存活的最长时间,当超过这个值,就删除该文件)</li><li>TotalSizeLimitedDiscCache(设定缓存bitmap的最大值,当超过这个值,删除最先加入到硬盘的文件)</li><li>UnlimitedDiscCache(这个缓存类没有任何的限制)</li></ul><strong></strong>
 ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder(this)              .diskCache(new UnlimitedDiscCache(cacheDir))            .build();  


2 0