Android异步加载图片详解之方式一(2)

简介: FileCache.java如下: package cn.loadImages; import java.io.File; import android.

FileCache.java如下:

package cn.loadImages;
import java.io.File;
import android.content.Context;
import android.net.Uri;
import android.os.Environment;
public class FileCache {
    private File fileCacheDir;
    public FileCache(Context context){
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
        	fileCacheDir=new File(Environment.getExternalStorageDirectory(),"idealLV001");
        }
        else{
        	 //context.getCacheDir();
        	 //获取缓存文件所在的目录
        	 fileCacheDir=context.getCacheDir();
        }        
        if(!fileCacheDir.exists()){
        	 fileCacheDir.mkdirs();
        }
           
    }
    
    public File getImageFile(String url){
        //String filename=String.valueOf(url.hashCode());
        //String filename = URLEncoder.encode(url);
        Uri uri=Uri.parse(url);
        String fileName=uri.getLastPathSegment();
        File file= new File(fileCacheDir, fileName);
        return file;
    }
    
	public void clear() {
		File[] files = fileCacheDir.listFiles();
		if (files == null) {
			return;
		}
		for (File file : files) {
			file.delete();
		}
	}
}


MemoryCache.java如下:

package cn.loadImages;
import java.lang.ref.SoftReference;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import android.graphics.Bitmap;
import android.util.Log;
public class MemoryCache {
    private static final String TAG = "xx";
    public static HashMap<String, Integer> bitmapsSizeHashMap;
    //1 建立一级缓存
    //  注意:利用了Collections.synchronizedMap使其变为一个同步的map
    private Map<String, Bitmap> hardBitmapCacheHashMap=
    Collections.synchronizedMap(new LinkedHashMap<String, Bitmap>(10,1.5f,true));
    //2 建立二级缓存
    private final static ConcurrentHashMap<String, SoftReference<Bitmap>> 
	softBitmapCacheHashMap = new ConcurrentHashMap<String, SoftReference<Bitmap>>(20);
    //一级缓存分配的总大小
    private long allocatedMemoryMaxSize=0;
    //一级缓存已使用的大小
    private long nowTotalUsedMemorySize=0;
    

    public MemoryCache(){
        //use 10% of available heap size
        setAllocatedMemoryMaxSize(Runtime.getRuntime().maxMemory()/10);//85
        bitmapsSizeHashMap=new HashMap<String, Integer>();
    }
    
    public void setAllocatedMemoryMaxSize(long allocatedMemoryMaxSize){
        this.allocatedMemoryMaxSize=allocatedMemoryMaxSize;
        Log.i(TAG, "allocatedMemoryMaxSize="+allocatedMemoryMaxSize/1024/1024+"MB");
    }

	public Bitmap getBitmapFromMemory(String url) {
		try {
			//1 从一级缓存中查找图片
			Bitmap bitmap = hardBitmapCacheHashMap.get(url);
			if (bitmap != null) {
				// 既然现在要得到此图片,则该图片为最近被使用
				// 即在所有的对象中为最新的对象.
				// 所以先将该对象从hardBitmapCacheHashMap中移除
				// 再将其插入到hardBitmapCacheHashMap的最前面
				hardBitmapCacheHashMap.remove(url);
				hardBitmapCacheHashMap.put(url, bitmap);
				return bitmap;
			}
            //2 从二级缓存中查找图片
			// 因为:若在sHardBitmapCache中没有,那么可能是因为该对象太陈旧
			// 且sHardBitmapCache容量已达上限,所以将其存入softBitmapCacheHashMap
			// 所以尝试从softBitmapCacheHashMap中获取对象
			 System.out.println("88 get方法中从SoftReference获取");
			 System.out.println("88 get方法中从SoftReference获取的url="+url);
			SoftReference<Bitmap> bitmapReference = softBitmapCacheHashMap.get(url);
			if (bitmapReference != null) {
				Bitmap bp = bitmapReference.get();
				if (bp != null) {
					return bp;
				} else {
					// SoftReference已被GC回收
					softBitmapCacheHashMap.remove(url);
				}
			}
			return null;
		} catch (NullPointerException ex) {
			ex.printStackTrace();
			return null;
		}
	}

    public void putBitmapToMemory(String url, Bitmap bitmap){
    	 try{
             if(!hardBitmapCacheHashMap.containsKey(url)){
            	 nowTotalUsedMemorySize+=getBitmapSizeInBytes(bitmap,url);
                 System.out.println("88 put方法中 nowTotalUsedMemorySize="+nowTotalUsedMemorySize);
                 checkMemorySizeStatus();
                 hardBitmapCacheHashMap.put(url, bitmap);
             }             
         }catch(Throwable th){
             th.printStackTrace();
         }
    }
    

    
    //检查一级缓存的使用情况
    //若一级缓存已达上限,则将该缓存中组后一个元素放入二级缓存softBitmapCacheHashMap中
    //再将其充一级缓存hardBitmapCacheHashMap中删除
    private void checkMemorySizeStatus() {
    	int hardBitmapCacheHashMapSize=hardBitmapCacheHashMap.size();
    	int count=0;
    	 System.out.println("88 checkSizeStatus方法中 nowTotalUsedMemorySize="+nowTotalUsedMemorySize);
    	 System.out.println("88 checkSizeStatus方法中 memoryMaxSize="+allocatedMemoryMaxSize);
    	
        if(nowTotalUsedMemorySize>=allocatedMemoryMaxSize){
        	System.out.println("88 checkSizeStatus方法中  满足nowTotalUsedMemorySize>=memoryMaxSize");
        	System.out.println("88 checkSizeStatus方法中  hardBitmapCacheHashMap.size()="+hardBitmapCacheHashMap.size());
            Iterator<Entry<String, Bitmap>> iter=hardBitmapCacheHashMap.entrySet().iterator();
            //least recently accessed item will be the first one iterated
            while(iter.hasNext()){
            	count++;
            	Entry<String, Bitmap> entry=iter.next();
            	if (count==hardBitmapCacheHashMapSize) {
            		 System.out.println("88 checkSizeStatus方法中 count="+count);
            		 System.out.println("88 checkSizeStatus方法中 memoryMaxSize="+allocatedMemoryMaxSize);
            		 System.out.println("88 checkSizeStatus方法中 删除前 nowTotalUsedMemorySize="+nowTotalUsedMemorySize);
            		 nowTotalUsedMemorySize-=getBitmapSizeInBytes(entry.getValue(),entry.getKey());
            		 //1将最后一个元素放到softBitmapCacheHashMap中
            		 softBitmapCacheHashMap.put(entry.getKey(),new SoftReference<Bitmap>(entry.getValue()));
            		  System.out.println("88 checkSizeStatus方法中放到SoftReference的url="+entry.getKey());
            		  System.out.println("88 checkSizeStatus方法中放入SoftReference后softBitmapCacheHashMap.size()="+softBitmapCacheHashMap.size());
                     //2 删除最后的这一个元素
                     iter.remove();
                     //3 从bitmapsSizeHashMap中删除该元素
                     bitmapsSizeHashMap.remove(entry.getKey());
                     System.out.println("88 checkSizeStatus方法中 删除后 hardBitmapCacheHashMap.size()="+hardBitmapCacheHashMap.size());
                     System.out.println("88 checkSizeStatus方法中 删除后 softBitmapCacheHashMap.size()="+softBitmapCacheHashMap.size());
                     
                     System.out.println("88 checkSizeStatus方法中 删除后 nowTotalUsedMemorySize="+nowTotalUsedMemorySize);
                     System.out.println("88 checkSizeStatus方法中 删除后 memoryMaxSize="+allocatedMemoryMaxSize);
            	}    
            }
        }
        if(nowTotalUsedMemorySize>=allocatedMemoryMaxSize){
            checkMemorySizeStatus();
        }
       
    }


    public void clear() {
        try{
            hardBitmapCacheHashMap.clear();
            softBitmapCacheHashMap.clear();
            bitmapsSizeHashMap.clear();
            nowTotalUsedMemorySize=0;
        }catch(NullPointerException ex){
            ex.printStackTrace();
        }
    }

    //得到Bitmap的大小
    long getBitmapSizeInBytes(Bitmap bitmap,String url) {
        if(bitmap==null){
        	 return 0;
        } 
         int bitmapSize=bitmapsSizeHashMap.get(url);
         return bitmapSize;
        //return bitmap.getRowBytes() * bitmap.getHeight();
    }
    
   
}


ImageLoader.java如下:

package cn.loadImages;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.widget.ImageView;
import cn.ideallistview.R;
public class ImageLoader {
    MemoryCache memoryCache=new MemoryCache();
    FileCache fileCache;
    private boolean isgetBitmapThumbnail=false;
    private final int REQUIRED_BITMAP_WIDTH=50;
    private final int REQUIRED_BITMAP_HEIGHT=50;
    private final int REQUIRED_BITMAP_MAXNUMOFPIXELS=200*200;
    //参见文档:
    //WeakHashMap像大多数集合类一样,是不同步的.
    //可使用 Collections.synchronizedMap方法来构造同步的WeakHashMap
    private Map<ImageView, String> imageViewsWeakHashMap=
    Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
    ExecutorService executorService; 
    //默认的图片
    final int defaultImageId=R.drawable.stub;
    public ImageLoader(Context context){
        fileCache=new FileCache(context);
        //创建一个可重用固定线程集合的线程池,以共享的无界队列方式来运行这些线程
        executorService=Executors.newFixedThreadPool(5);
    }
   
    //显示网络图片
    public void displayImage(String url, ImageView imageView){
    	//1 将imageView和其对应的url放入imageViewsWeakHashMap中
        imageViewsWeakHashMap.put(imageView, url);
        //2 试图从内存中得到图片
        Bitmap bitmap=memoryCache.getBitmapFromMemory(url);
        if(bitmap!=null){
        	 imageView.setImageBitmap(bitmap);
        }
       //3 不在内存中,则从SD卡和网络上获取
        else{
            taskQueueForImages(url, imageView);
            imageView.setImageResource(defaultImageId);
        }
    }
        
    private void taskQueueForImages(String url, ImageView imageView){
        WillLoadedImageBean willLoadedImageBean=new WillLoadedImageBean(url, imageView);
        //提交一个 Runnable任务用于执行,并返回一个表示该任务的 Future
        executorService.submit(new LoaderImagesRunnable(willLoadedImageBean));
    }
    
    //该线程在线程池中运行
    class LoaderImagesRunnable implements Runnable {
        WillLoadedImageBean willLoadedImageBean;
        LoaderImagesRunnable(WillLoadedImageBean willLoadedImageBean){
            this.willLoadedImageBean=willLoadedImageBean;
        }
        
        @Override
        public void run() {
            try{
                if(isImageViewReused(willLoadedImageBean)){
                	 return;
                }
                //依据图片Url获得其对应的Bitmap
                //1 从SDCard中寻找
                //2 若不在SDCard中,则从网络下载,且将图片存至SDCard中
                //3 将SDCard中图片返回至此
                Bitmap bitmap=getBitmapByUrl(willLoadedImageBean.url);
                //4 将图片存至memoryCache中
                memoryCache.putBitmapToMemory(willLoadedImageBean.url, bitmap);
                if(isImageViewReused(willLoadedImageBean)){
                	 return;
                }
                //5 将Bitmap在UI中显示
                BitmapDisplayerRunnableInUIThread bitmapDisplayerRunnable
                =new BitmapDisplayerRunnableInUIThread(bitmap, willLoadedImageBean);
                Activity activity=(Activity)willLoadedImageBean.imageView.getContext();
                activity.runOnUiThread(bitmapDisplayerRunnable);
            }catch(Throwable th){
                th.printStackTrace();
            }
        }
    }
    
    //通过Url得到其对应的Bitmap
    private Bitmap getBitmapByUrl(String url) 
    {  
    	//1 从SD卡中获取
        File file=fileCache.getImageFile(url);
        Bitmap bitmap = getBitmapFromSDCardFile(file);
        if(bitmap!=null){
        	return bitmap;
        }else{
        	//2 若不存在SD卡中,则从网络下载并存至SD卡的File文件中
        	 bitmap=getBitmapFromNetWork(url,file);
        	 if (bitmap!=null) {
				return bitmap;
			}
        }
		return null;
    }
    
  
	private Bitmap getBitmapFromSDCardFile(File file) {
		if (!isgetBitmapThumbnail) {
			try {
				FileInputStream inputStream = new FileInputStream(file);
				Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
				inputStream.close();
				return bitmap;
			} catch (Exception e) {
				e.printStackTrace();
			}

		} else {
			try {
			String filePath=file.getAbsolutePath();
			int minSideLength=Math.min(REQUIRED_BITMAP_HEIGHT, REQUIRED_BITMAP_WIDTH);
			Bitmap bp=Utils.getBitmapThumbnail(filePath, minSideLength, REQUIRED_BITMAP_MAXNUMOFPIXELS);
		    return bp;
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		return null;
	}
//    private Bitmap getBitmapFromSDCardFile(File file){
//        try {
//            //decode image size
//            BitmapFactory.Options options1 = new BitmapFactory.Options();
//            options1.inJustDecodeBounds = true;
//            FileInputStream stream1=new FileInputStream(file);
//            BitmapFactory.decodeStream(stream1,null,options1);
//            stream1.close();
//            
//            //Find the correct scale value. It should be the power of 2.
//            final int REQUIRED_SIZE=70;
//            int width_tmp=options1.outWidth, height_tmp=options1.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;
//            }
//            
//            //decode with inSampleSize
//            BitmapFactory.Options options2 = new BitmapFactory.Options();
//            options2.inSampleSize=scale;
//            FileInputStream stream2=new FileInputStream(file);
//            Bitmap bitmap=BitmapFactory.decodeStream(stream2, null, options2);
//            stream2.close();
//            
//            System.out.println("xxxxxxxxxxxxxxxxx f.getPath="+file.getPath());
//            System.out.println("xxxxxxxxxxxxxxxxx options1.outWidth="+options1.outWidth);
//            System.out.println("xxxxxxxxxxxxxxxxx options1.outHeight="+options1.outHeight);
//            System.out.println("xxxxxxxxxxxxxxxxx scale="+scale);
//            return bitmap;
//        } catch (FileNotFoundException e) {
//        } 
//        catch (IOException e) {
//            e.printStackTrace();
//        }
//        return null;
//    }
  //网络下载图片且保存到SDCard
    private Bitmap getBitmapFromNetWork(String url,File file){
        try {
            Bitmap bitmap=null;
            URL imageUrl = new URL(url);
            HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
            conn.setConnectTimeout(30000);
            conn.setReadTimeout(30000);
            conn.setInstanceFollowRedirects(true);
            InputStream is=conn.getInputStream();
            
            //保存其大小
            MemoryCache.bitmapsSizeHashMap.put(url,conn.getContentLength());
            OutputStream os = new FileOutputStream(file);
            Utils.copyStream(is, os);
            os.close();
            bitmap = getBitmapFromSDCardFile(file);
            return bitmap;
        } catch (Throwable ex){
           ex.printStackTrace();
           if(ex instanceof OutOfMemoryError){
        	   memoryCache.clear();
           }
           return null;
        }
    }
    
    //在UI线程中显示Bitmap
    class BitmapDisplayerRunnableInUIThread implements Runnable{
        Bitmap bitmap;
        WillLoadedImageBean willLoadedImageBean;
        public BitmapDisplayerRunnableInUIThread(Bitmap bitmap, WillLoadedImageBean willLoadedImageBean){
        	this.bitmap=bitmap;
        	this.willLoadedImageBean=willLoadedImageBean;
        	}
        public void run(){
            if(isImageViewReused(willLoadedImageBean)){
            	 return;
            }
            if(bitmap!=null){
            	 willLoadedImageBean.imageView.setImageBitmap(bitmap);
            }          
            else{
            	 willLoadedImageBean.imageView.setImageResource(defaultImageId);
            }
               
        }
    }

    //Task for the queue
    private class WillLoadedImageBean
    {
        public String url;
        public ImageView imageView;
        public WillLoadedImageBean(String url, ImageView imageView){
            this.url=url; 
            this.imageView=imageView;
        }
    }
    
    boolean isImageViewReused(WillLoadedImageBean willLoadedImageBean){
        String imageUrl=imageViewsWeakHashMap.get(willLoadedImageBean.imageView);
        if(imageUrl==null || !imageUrl.equals(willLoadedImageBean.url)){
        	  return true;
        }        
        return false;
    }
    
    public void clearCache() {
        memoryCache.clear();
        fileCache.clear();
    }
}


 

相关文章
|
6月前
|
XML Java Android开发
Android Studio App开发之对图片进行简单加工(包括放缩,旋转等等 附源码)
Android Studio App开发之对图片进行简单加工(包括放缩,旋转等等 附源码)
102 0
|
6月前
|
XML Java Android开发
Android Studio App开发之使用相机拍摄照片和从相册中选取图片(附源码 超详细必看)
Android Studio App开发之使用相机拍摄照片和从相册中选取图片(附源码 超详细必看)
793 0
|
2月前
|
存储 缓存 编解码
Android经典面试题之图片Bitmap怎么做优化
本文介绍了图片相关的内存优化方法,包括分辨率适配、图片压缩与缓存。文中详细讲解了如何根据不同分辨率放置图片资源,避免图片拉伸变形;并通过示例代码展示了使用`BitmapFactory.Options`进行图片压缩的具体步骤。此外,还介绍了Glide等第三方库如何利用LRU算法实现高效图片缓存。
61 20
Android经典面试题之图片Bitmap怎么做优化
|
6月前
|
Android开发
Android通过手势(多点)缩放和拖拽图片
Android通过手势(多点)缩放和拖拽图片
52 4
|
6月前
|
Java Android开发
android 下载图片的问题
android 下载图片的问题
44 3
|
3月前
|
数据处理 开发工具 数据安全/隐私保护
Android平台RTMP推送|轻量级RTSP服务|GB28181接入之文字、png图片水印的精进之路
本文探讨了Android平台上推流模块中添加文字与PNG水印的技术演进。自2015年起,为了满足应急指挥及安防领域的需求,逐步发展出三代水印技术:第一代为静态文字与图像水印;第二代实现了动态更新水印内容的能力,例如实时位置与时间信息;至第三代,则优化了数据传输效率,直接使用Bitmap对象传递水印数据至JNI层,减少了内存拷贝次数。这些迭代不仅提升了用户体验和技术效率,也体现了开发者追求极致与不断创新的精神。
|
3月前
|
自然语言处理 定位技术 API
Android经典实战之如何获取图片的经纬度以及如何根据经纬度获取对应的地点名称
本文介绍如何在Android中从图片提取地理位置信息并转换为地址。首先利用`ExifInterface`获取图片内的经纬度,然后通过`Geocoder`将经纬度转为地址。注意操作需在子线程进行且考虑多语言支持。
218 4
|
3月前
|
XML 前端开发 Android开发
Android经典实战之Kotlin中实现圆角图片和圆形图片
本文介绍两种实现圆角图像视图的方法。第一种是通过自定义Kotlin `AppCompatImageView`,重写`onDraw`方法使用`Canvas`和`Path`进行圆角剪裁。第二种利用Android Material库中的`ShapeableImageView`,简单配置即可实现圆角效果。两种方法均易于实现且提供动态调整圆角半径的功能。
65 0
|
5月前
|
JSON 编解码 Apache
Android中使用HttpURLConnection实现GET POST JSON数据与下载图片
Android中使用HttpURLConnection实现GET POST JSON数据与下载图片
57 1
|
5月前
|
Java Android开发
18. 【Android教程】图片控件 ImageView
18. 【Android教程】图片控件 ImageView
79 4
下一篇
无影云桌面