网络图片的异步加载器,多线程队列,带文件和内存缓存

本文是实现一个图片异步加载器。只需直接调用以下代码网络异步加载图片。

ImageLoader imageLoader = new ImageLoader(context);  //生成图片加载器
imageLoader.DisplayImage(imgUrl,imageView);   //异步加载图片,imgUrl为图片的URL,imageView为图片控件

 具体实现:

1)文件缓存类:主要配置缓存文件存放地址。

/**
 * 文件缓存类
 *   
 * @version 1.0 
 * @created 2013-5-8
 */
public class FileCache {
    
    private File cacheDir;//文件缓存目录
    
    public FileCache(Context context){
        if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
        	//如果有SD卡则设定 卡内“/cache/”为缓存目录
        	cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"/cache/");
        else{
        	//如果无SD卡则设定手机内为缓存目录
            cacheDir=context.getCacheDir();
            String path=context.getCacheDir().getAbsolutePath();
        }
        if(!cacheDir.exists())
            cacheDir.mkdirs();
    }
    //通过网络图片的URL,映射相应的文件。
    public File getFile(String url){
        String filename=String.valueOf(url.hashCode()); //以url的哈希码为文件名
        //String filename = URLEncoder.encode(url);     //另一种方法可以选择
        File f = new File(cacheDir, filename);
        return f;
    }
    //清除文件缓存
    public void clear(){
        File[] files=cacheDir.listFiles();
        if(files==null)
            return;
        for(File f:files)
            f.delete();
    }

}

2)内存缓存类:用哈希表存储图片,采用LRU方法管理缓存,限定为系统空闲内存的1/4

/**
 * 内存缓存类
 *   
 * @version 1.0 
 * @created 2013-5-8
 */
public class MemoryCache {

    private static final String TAG = "MemoryCache";  //log标志
    private Map<String, Bitmap> cache=Collections.synchronizedMap(
            new LinkedHashMap<String, Bitmap>(10,1.5f,true));
    //内存缓存
    //LinkedHashMap为生成遍历时有序的哈希表,便于实现LRU,既最近最少使用算法
    //Collections.synchronizedMap用于此哈希表对多线程调用,既让此哈希表多线程操作是安全的
    private long size=0;       //分配的内存大小
    private long limit=1000000;//最大分配内存限定,单位为字节bytes

    public MemoryCache(){
        //设定当前系统的1/4的内存为最大分配内存
        setLimit(Runtime.getRuntime().maxMemory()/4);
    }
    
    public void setLimit(long new_limit){
        limit=new_limit;
        Log.i(TAG, "MemoryCache will use up to "+limit/1024./1024.+"MB");
    }
    //查找图片
    public Bitmap get(String url){
        try{
            if(!cache.containsKey(url))
                return null;
            return cache.get(url);
        }catch(NullPointerException ex){
            ex.printStackTrace();
            return null;
        }
    }
   //删除图片
    public void reMove(String url){
        try{
            if(!cache.containsKey(url))
                return;
            cache.remove(url);
        }catch(NullPointerException ex){
            ex.printStackTrace();
        }
    }
    //增加图片到内存缓存
    public void put(String url, Bitmap bitmap){
        try{
            if(cache.containsKey(url))
                size-=getSizeInBytes(cache.get(url));
            cache.put(url, bitmap);
            size+=getSizeInBytes(bitmap);
            checkSize();//检查内存是否到限定的最大值
        }catch(Throwable th){
            th.printStackTrace();
        }
    }
   //检查内存是否到限定的最大值,如果到了,删除最早的图片
    private void checkSize() {
        Log.i(TAG, "cache size="+size+" length="+cache.size());
        if(size>limit){
            Iterator<Entry<String, Bitmap>> iter=cache.entrySet().iterator(); 
           //遍历缓存,删除最早的图片,直到大小小于最大值
            while(iter.hasNext()){
                Entry<String, Bitmap> entry=iter.next();
                size-=getSizeInBytes(entry.getValue());
                iter.remove();
                if(size<=limit)
                    break;
            }
            Log.i(TAG, "Clean cache. New size "+cache.size());
        }
    }
    //清除缓存
    public void clear() {
        try{
            cache.clear();
            size=0;
        }catch(NullPointerException ex){
            ex.printStackTrace();
        }
    }
    //计算图片的大小
    long getSizeInBytes(Bitmap bitmap) {
        if(bitmap==null)
            return 0;
        return bitmap.getRowBytes() * bitmap.getHeight();
    }
}

3)图片加载器:加载图片,预先显示的默认的图片,网络下载完成将自动刷新图片显示,加载图片将限定大小(节省空间,防止OOM)。默认同时加载5张图片。

public class ImageLoader {
    
    MemoryCache memoryCache=new MemoryCache(); //内存缓存
    FileCache fileCache; //文件缓存
    private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
    //管理需加载的ImageView控件
    //WeakHashMap以弱键实现的基于哈希表的 Map,当ImageView控件不正常使用时,其键值对将丢弃
    //Collections.synchronizedMap方法进行线程级安全控制
    ExecutorService executorService;//线程池控制网络下载图片
    Handler handler=new Handler();//handler用于显示图片(子线程通过Handler才能控制UI操作)
    
    Context mContext;
    final int stub_id = R.drawable.network_error; //ImageView控件默认显示的图片
    final int REQUIRED_SIZE = 145;  //设定图片长或宽的最大值,其单位为像素,
    							    //其为图片下载后显示的长,宽都要小于此值
    
    public ImageLoader(Context context){
    	mContext = context;
        fileCache = new FileCache(context);
        executorService = Executors.newFixedThreadPool(5); //最多5个线程的线程池
    }
    //异步加载图片的调用
    public void DisplayImage(String url, ImageView imageView)
    {
        imageViews.put(imageView, url);
        Bitmap bitmap=memoryCache.get(url);//查找内存缓存
        if(bitmap!=null)
            imageView.setImageBitmap(bitmap);//如果内存缓存中有此图片,直接显示图
        else
        {
            queuePhoto(url, imageView); //内存缓存中无此图片,网络加载图片
            imageView.setImageResource(stub_id); //先显示默认图片
        }
    }
    //加载图片
    private void queuePhoto(String url, ImageView imageView)
    {
        PhotoToLoad p=new PhotoToLoad(url, imageView); //生成图片加载信息类
        executorService.submit(new PhotosLoader(p));//生成子线程图片加载任务类,并调用子线程
    }
    
    //图片加载信息类
    private class PhotoToLoad
    {
        public String url;
        public ImageView imageView;
        public boolean isDown; //是否下载
        public PhotoToLoad(String u, ImageView i){
            url=u; 
            imageView=i;
            isDown = false;
        }
        
        public PhotoToLoad(String u, ImageView i,boolean down){
            url=u; 
            imageView=i;
            isDown = down;
        }
    }
    //子线程图片加载任务类
    class PhotosLoader implements Runnable {
        PhotoToLoad photoToLoad; //
        PhotosLoader(PhotoToLoad photoToLoad){
            this.photoToLoad=photoToLoad;
        }
        
        @Override
        public void run() {
            try{
                if(imageViewReused(photoToLoad))
                    return; //如果imageViewsm无此条目,无需加载(当ImageView控件不正常使用时,其键值对将丢弃)
                Bitmap bmp=getBitmap(photoToLoad.url); //网络下载图片
                memoryCache.put(photoToLoad.url, bmp);
                if(imageViewReused(photoToLoad))
                    return;//如果imageViewsm无此条目,无需加载(当ImageView控件不正常使用时,其键值对将丢弃)
                BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad);
                handler.post(bd); //控制UI加载图片
            }catch(Throwable th){
                th.printStackTrace();
            }
        }
    }
    
    private Bitmap getBitmap(String url) 
    {
        File f=fileCache.getFile(url); //文件缓存图片
        
        Bitmap b = decodeFile(f);  //从文件中加载图片
        if(b!=null){
            return b; //如果文件缓存中有此图片,直接用此图
        }
        
        //网络下载图片
        try {
            Bitmap bitmap=null;
            URL imageUrl = new URL(url);
            HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
            conn.setConnectTimeout(30000); //连接超时30秒
            conn.setReadTimeout(30000);  //读取超时30秒
            conn.setInstanceFollowRedirects(true);
            InputStream is=conn.getInputStream(); //网络流输入
            OutputStream os = new FileOutputStream(f); //文件流输出
            CopyStream(is, os); //拷贝图片的网络数据到文件
            os.close();
            conn.disconnect();
            bitmap = decodeFile(f); //读取已下载的图片
			return bitmap;
        } catch (Throwable ex){
           ex.printStackTrace();
           if(ex instanceof OutOfMemoryError)
               memoryCache.clear();
           return null;
        }
    }
   //判断imageViews是否有此条目
    boolean imageViewReused(PhotoToLoad photoToLoad){
        String tag=imageViews.get(photoToLoad.imageView);
        if(tag==null || !tag.equals(photoToLoad.url))
            return true;
        return false;
    }
    
   //控制图片控件显示图片
    class BitmapDisplayer implements Runnable
    {
        Bitmap bitmap;
        PhotoToLoad photoToLoad;
        public BitmapDisplayer(Bitmap b, PhotoToLoad p){bitmap=b;photoToLoad=p;}
        public void run()
        {
            if(imageViewReused(photoToLoad))
                return;
            if(bitmap!=null)
                photoToLoad.imageView.setImageBitmap(bitmap);
            else
                photoToLoad.imageView.setImageResource(stub_id);
        }
    }
    //清除缓存
    public void clearCache() {
        memoryCache.clear();
        fileCache.clear();
    }
    //文件读取图片,读取图片将按比例缩小,来达到REQUIRED_SIZE
    public Bitmap decodeFile(File f){
        try {
            //decode image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;  //设定此值将不编码图片,而获得宽,高
            FileInputStream stream1=new FileInputStream(f);
            BitmapFactory.decodeStream(stream1,null,o);
            stream1.close();
            
            //Find the correct scale value. It should be the power of 2.
            int width_tmp=o.outWidth, height_tmp=o.outHeight; //获得宽,高
            int scale=1;
            while(true){
                if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
                    break;
                width_tmp/=2;
                height_tmp/=2;
                scale*=2; //计算缩放值,使宽,高小于REQUIRED_SIZE
            }
            //根据缩放值编码图片
            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inSampleSize=scale;
            FileInputStream stream2=new FileInputStream(f);
            Bitmap bitmap=BitmapFactory.decodeStream(stream2, null, o2);
            stream2.close();
            return bitmap;
        } catch (FileNotFoundException e) {
        } 
        catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    
    public void CopyStream(InputStream is, OutputStream os){
        final int buffer_size=1024;
        try
        {
            byte[] bytes=new byte[buffer_size];
            for(;;)
            {
              int count=is.read(bytes, 0, buffer_size);
              if(count==-1)
                  break;
              os.write(bytes, 0, count);
            }
        }
        catch(Exception ex){}
    }
}







  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值