Android之ListView异步加载网络图片(优化缓存机制)和对图片资源进行优化,并且实现内存双缓存 + 磁盘缓存

1.Android之ListView异步加载网络图片(优化缓存机制)


网上关于这个方面的文章也不少,基本的思路是线程+缓存来解决。下面提出一些优化:

1、采用线程池

2、内存缓存+文件缓存

3、内存缓存中网上很多是采用SoftReference来防止堆溢出,这儿严格限制只能使用最大JVM内存的1/4

4、对下载的图片进行按比例缩放,以减少内存的消耗

具体的代码里面说明。先放上内存缓存类的代码MemoryCache.java:

[java]  view plain copy
  1. public class MemoryCache {  
  2.   
  3.     private static final String TAG = "MemoryCache";  
  4.     // 放入缓存时是个同步操作  
  5.     // LinkedHashMap构造方法的最后一个参数true代表这个map里的元素将按照最近使用次数由少到多排列,即LRU  
  6.     // 这样的好处是如果要将缓存中的元素替换,则先遍历出最近最少使用的元素来替换以提高效率  
  7.     private Map<String, Bitmap> cache = Collections  
  8.             .synchronizedMap(new LinkedHashMap<String, Bitmap>(101.5f, true));  
  9.     // 缓存中图片所占用的字节,初始0,将通过此变量严格控制缓存所占用的堆内存  
  10.     private long size = 0;// current allocated size  
  11.     // 缓存只能占用的最大堆内存  
  12.     private long limit = 1000000;// max memory in bytes  
  13.   
  14.     public MemoryCache() {  
  15.         // use 25% of available heap size  
  16.         setLimit(Runtime.getRuntime().maxMemory() / 4);  
  17.     }  
  18.   
  19.     public void setLimit(long new_limit) {   
  20.         limit = new_limit;  
  21.         Log.i(TAG, "MemoryCache will use up to " + limit / 1024. / 1024. + "MB");  
  22.     }  
  23.   
  24.     public Bitmap get(String id) {  
  25.         try {  
  26.             if (!cache.containsKey(id))  
  27.                 return null;  
  28.             return cache.get(id);  
  29.         } catch (NullPointerException ex) {  
  30.             return null;  
  31.         }  
  32.     }  
  33.   
  34.     public void put(String id, Bitmap bitmap) {  
  35.         try {  
  36.             if (cache.containsKey(id))  
  37.                 size -= getSizeInBytes(cache.get(id));  
  38.             cache.put(id, bitmap);  
  39.             size += getSizeInBytes(bitmap);  
  40.             checkSize();  
  41.         } catch (Throwable th) {  
  42.             th.printStackTrace();  
  43.         }  
  44.     }  
  45.   
  46.     /** 
  47.      * 严格控制堆内存,如果超过将首先替换最近最少使用的那个图片缓存 
  48.      *  
  49.      */  
  50.     private void checkSize() {  
  51.         Log.i(TAG, "cache size=" + size + " length=" + cache.size());  
  52.         if (size > limit) {  
  53.             // 先遍历最近最少使用的元素  
  54.             Iterator<Entry<String, Bitmap>> iter = cache.entrySet().iterator();  
  55.             while (iter.hasNext()) {  
  56.                 Entry<String, Bitmap> entry = iter.next();  
  57.                 size -= getSizeInBytes(entry.getValue());  
  58.                 iter.remove();  
  59.                 if (size <= limit)  
  60.                     break;  
  61.             }  
  62.             Log.i(TAG, "Clean cache. New size " + cache.size());  
  63.         }  
  64.     }  
  65.   
  66.     public void clear() {  
  67.         cache.clear();  
  68.     }  
  69.   
  70.     /** 
  71.      * 图片占用的内存 
  72.      *  
  73.      * @param bitmap 
  74.      * @return 
  75.      */  
  76.     long getSizeInBytes(Bitmap bitmap) {  
  77.         if (bitmap == null)  
  78.             return 0;  
  79.         return bitmap.getRowBytes() * bitmap.getHeight();  
  80.     }  
  81. }  

也可以使用SoftReference,代码会简单很多,但是我推荐上面的方法。

[java]  view plain copy
  1. public class MemoryCache {  
  2.       
  3.     private Map<String, SoftReference<Bitmap>> cache = Collections  
  4.             .synchronizedMap(new HashMap<String, SoftReference<Bitmap>>());  
  5.   
  6.     public Bitmap get(String id) {  
  7.         if (!cache.containsKey(id))  
  8.             return null;  
  9.         SoftReference<Bitmap> ref = cache.get(id);  
  10.         return ref.get();  
  11.     }  
  12.   
  13.     public void put(String id, Bitmap bitmap) {  
  14.         cache.put(id, new SoftReference<Bitmap>(bitmap));  
  15.     }  
  16.   
  17.     public void clear() {  
  18.         cache.clear();  
  19.     }  
  20.   
  21. }  
下面是文件缓存类的代码FileCache.java:

[java]  view plain copy
  1. public class FileCache {  
  2.   
  3.     private File cacheDir;  
  4.   
  5.     public FileCache(Context context) {  
  6.         // 如果有SD卡则在SD卡中建一个LazyList的目录存放缓存的图片  
  7.         // 没有SD卡就放在系统的缓存目录中  
  8.         if (android.os.Environment.getExternalStorageState().equals(  
  9.                 android.os.Environment.MEDIA_MOUNTED))  
  10.             cacheDir = new File(  
  11.                     android.os.Environment.getExternalStorageDirectory(),  
  12.                     "LazyList");  
  13.         else  
  14.             cacheDir = context.getCacheDir();  
  15.         if (!cacheDir.exists())  
  16.             cacheDir.mkdirs();  
  17.     }  
  18.   
  19.     public File getFile(String url) {  
  20.         // 将url的hashCode作为缓存的文件名  
  21.         String filename = String.valueOf(url.hashCode());  
  22.         // Another possible solution  
  23.         // String filename = URLEncoder.encode(url);  
  24.         File f = new File(cacheDir, filename);  
  25.         return f;  
  26.   
  27.     }  
  28.   
  29.     public void clear() {  
  30.         File[] files = cacheDir.listFiles();  
  31.         if (files == null)  
  32.             return;  
  33.         for (File f : files)  
  34.             f.delete();  
  35.     }  
  36.   
  37. }  
最后最重要的加载图片的类,ImageLoader.java:

[java]  view plain copy
  1. public class ImageLoader {  
  2.   
  3.     MemoryCache memoryCache = new MemoryCache();  
  4.     FileCache fileCache;  
  5.     private Map<ImageView, String> imageViews = Collections  
  6.             .synchronizedMap(new WeakHashMap<ImageView, String>());  
  7.     // 线程池  
  8.     ExecutorService executorService;  
  9.   
  10.     public ImageLoader(Context context) {  
  11.         fileCache = new FileCache(context);  
  12.         executorService = Executors.newFixedThreadPool(5);  
  13.     }  
  14.   
  15.     // 当进入listview时默认的图片,可换成你自己的默认图片  
  16.     final int stub_id = R.drawable.stub;  
  17.   
  18.     // 最主要的方法  
  19.     public void DisplayImage(String url, ImageView imageView) {  
  20.         imageViews.put(imageView, url);  
  21.         // 先从内存缓存中查找  
  22.   
  23.         Bitmap bitmap = memoryCache.get(url);  
  24.         if (bitmap != null)  
  25.             imageView.setImageBitmap(bitmap);  
  26.         else {  
  27.             // 若没有的话则开启新线程加载图片  
  28.             queuePhoto(url, imageView);  
  29.             imageView.setImageResource(stub_id);  
  30.         }  
  31.     }  
  32.   
  33.     private void queuePhoto(String url, ImageView imageView) {  
  34.         PhotoToLoad p = new PhotoToLoad(url, imageView);  
  35.         executorService.submit(new PhotosLoader(p));  
  36.     }  
  37.   
  38.     private Bitmap getBitmap(String url) {  
  39.         File f = fileCache.getFile(url);  
  40.   
  41.         // 先从文件缓存中查找是否有  
  42.         Bitmap b = decodeFile(f);  
  43.         if (b != null)  
  44.             return b;  
  45.   
  46.         // 最后从指定的url中下载图片  
  47.         try {  
  48.             Bitmap bitmap = null;  
  49.             URL imageUrl = new URL(url);  
  50.             HttpURLConnection conn = (HttpURLConnection) imageUrl  
  51.                     .openConnection();  
  52.             conn.setConnectTimeout(30000);  
  53.             conn.setReadTimeout(30000);  
  54.             conn.setInstanceFollowRedirects(true);  
  55.             InputStream is = conn.getInputStream();  
  56.             OutputStream os = new FileOutputStream(f);  
  57.             CopyStream(is, os);  
  58.             os.close();  
  59.             bitmap = decodeFile(f);  
  60.             return bitmap;  
  61.         } catch (Exception ex) {  
  62.             ex.printStackTrace();  
  63.             return null;  
  64.         }  
  65.     }  
  66.   
  67.     // decode这个图片并且按比例缩放以减少内存消耗,虚拟机对每张图片的缓存大小也是有限制的  
  68.     private Bitmap decodeFile(File f) {  
  69.         try {  
  70.             // decode image size  
  71.             BitmapFactory.Options o = new BitmapFactory.Options();  
  72.             o.inJustDecodeBounds = true;  
  73.             BitmapFactory.decodeStream(new FileInputStream(f), null, o);  
  74.   
  75.             // Find the correct scale value. It should be the power of 2.  
  76.             final int REQUIRED_SIZE = 70;  
  77.             int width_tmp = o.outWidth, height_tmp = o.outHeight;  
  78.             int scale = 1;  
  79.             while (true) {  
  80.                 if (width_tmp / 2 < REQUIRED_SIZE  
  81.                         || height_tmp / 2 < REQUIRED_SIZE)  
  82.                     break;  
  83.                 width_tmp /= 2;  
  84.                 height_tmp /= 2;  
  85.                 scale *= 2;  
  86.             }  
  87.   
  88.             // decode with inSampleSize  
  89.             BitmapFactory.Options o2 = new BitmapFactory.Options();  
  90.             o2.inSampleSize = scale;  
  91.             return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);  
  92.         } catch (FileNotFoundException e) {  
  93.         }  
  94.         return null;  
  95.     }  
  96.   
  97.     // Task for the queue  
  98.     private class PhotoToLoad {  
  99.         public String url;  
  100.         public ImageView imageView;  
  101.   
  102.         public PhotoToLoad(String u, ImageView i) {  
  103.             url = u;  
  104.             imageView = i;  
  105.         }  
  106.     }  
  107.   
  108.     class PhotosLoader implements Runnable {  
  109.         PhotoToLoad photoToLoad;  
  110.   
  111.         PhotosLoader(PhotoToLoad photoToLoad) {  
  112.             this.photoToLoad = photoToLoad;  
  113.         }  
  114.   
  115.         @Override  
  116.         public void run() {  
  117.             if (imageViewReused(photoToLoad))  
  118.                 return;  
  119.             Bitmap bmp = getBitmap(photoToLoad.url);  
  120.             memoryCache.put(photoToLoad.url, bmp);  
  121.             if (imageViewReused(photoToLoad))  
  122.                 return;  
  123.             BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad);  
  124.             // 更新的操作放在UI线程中  
  125.             Activity a = (Activity) photoToLoad.imageView.getContext();  
  126.             a.runOnUiThread(bd);  
  127.         }  
  128.     }  
  129.   
  130.     /** 
  131.      * 防止图片错位 
  132.      *  
  133.      * @param photoToLoad 
  134.      * @return 
  135.      */  
  136.     boolean imageViewReused(PhotoToLoad photoToLoad) {  
  137.         String tag = imageViews.get(photoToLoad.imageView);  
  138.         if (tag == null || !tag.equals(photoToLoad.url))  
  139.             return true;  
  140.         return false;  
  141.     }  
  142.   
  143.     // 用于在UI线程中更新界面  
  144.     class BitmapDisplayer implements Runnable {  
  145.         Bitmap bitmap;  
  146.         PhotoToLoad photoToLoad;  
  147.   
  148.         public BitmapDisplayer(Bitmap b, PhotoToLoad p) {  
  149.             bitmap = b;  
  150.             photoToLoad = p;  
  151.         }  
  152.   
  153.         public void run() {  
  154.             if (imageViewReused(photoToLoad))  
  155.                 return;  
  156.             if (bitmap != null)  
  157.                 photoToLoad.imageView.setImageBitmap(bitmap);  
  158.             else  
  159.                 photoToLoad.imageView.setImageResource(stub_id);  
  160.         }  
  161.     }  
  162.   
  163.     public void clearCache() {  
  164.         memoryCache.clear();  
  165.         fileCache.clear();  
  166.     }  
  167.   
  168.     public static void CopyStream(InputStream is, OutputStream os) {  
  169.         final int buffer_size = 1024;  
  170.         try {  
  171.             byte[] bytes = new byte[buffer_size];  
  172.             for (;;) {  
  173.                 int count = is.read(bytes, 0, buffer_size);  
  174.                 if (count == -1)  
  175.                     break;  
  176.                 os.write(bytes, 0, count);  
  177.             }  
  178.         } catch (Exception ex) {  
  179.         }  
  180.     }  
  181. }  
主要流程是先从内存缓存中查找,若没有再开线程,从文件缓存中查找都没有则从指定的url中查找,并对bitmap进行处理,最后通过下面方法对UI进行更新操作。
[java]  view plain copy
  1. a.runOnUiThread(...);  

在你的程序中的基本用法:

[java]  view plain copy
  1. ImageLoader imageLoader=new ImageLoader(context);  
  2. ...  
  3. imageLoader.DisplayImage(url, imageView);  
比如你的放在你的ListView的adapter的getView()方法中,当然也适用于GridView。

OK,先到这。



2.android 网络加载图片,对图片资源进行优化,并且实现内存双缓存 + 磁盘缓存


经常会用到 网络文件 比如查看大图片数据 资源优化的问题,当然用开源的项目  Android-Universal-Image-Loader  或者 ignition 都是个很好的选择。

在这里把原来 写过的优化的代码直接拿出来,经过测试千张图片效果还是不错的。




工程目录:



至于 Activity 就是加载了 1个网格布局

[java]  view plain copy
  1. /** 
  2.  *   实现 异步加载 和   2级缓存 
  3.  */  
  4. public class ImagedownActivity extends Activity {  
  5.       
  6.     public static String filepath;  
  7.     @Override  
  8.     public void onCreate(Bundle savedInstanceState) {  
  9.         super.onCreate(savedInstanceState);  
  10.         setContentView(R.layout.main);  
  11.         filepath =   this.getCacheDir().getAbsolutePath();  
  12.         GridView gv=(GridView)findViewById(R.id.gridview01);  
  13.         //设置列数  
  14.         gv.setNumColumns(3);  
  15.         //配置适配器  
  16.         gv.setAdapter(new Myadapter(this));  
  17.     }  
  18.       
  19.     @Override  
  20.     protected void onDestroy() {  
  21.         // TODO Auto-generated method stub  
  22.         //activity 销毁时,清除缓存  
  23.         MyImageLoader.removeCache(filepath);  
  24.         super.onDestroy();  
  25.     }  
  26.       
  27. }  

接下来 Myadapter.java(给网格每个item塞入图片 )在生成每个 item 异步请求网络获取image

[java]  view plain copy
  1. public class Myadapter extends BaseAdapter {  
  2.     private Context context;  
  3.     private String  root ="http://192.168.0.100:8080/Android_list/";  
  4.     private String[] URLS;  
  5.     private final MyImageLoader  myImageLoader = new MyImageLoader(context);;  
  6.       
  7.     /** 
  8.      * adapter 初始化的时候早一堆数据 
  9.      * 这里我请求的是自己搭的服务器 
  10.      * @param context 
  11.      */  
  12.     public  Myadapter(Context context){  
  13.         this.context =context;  
  14.         URLS = new String[999];  
  15.         for (int i = 0; i < 999; i++) {  
  16.             URLS[i] = root + (i+1)+".jpg";  
  17.         }     
  18.     }  
  19.       
  20.       
  21.     @Override  
  22.     public int getCount() {  
  23.         return URLS.length;  
  24.     }  
  25.   
  26.     @Override  
  27.     public Object getItem(int position) {  
  28.         return URLS[position];  
  29.     }  
  30.   
  31.     @Override  
  32.     public long getItemId(int position) {  
  33.         return URLS[position].hashCode();  
  34.     }  
  35.   
  36.   
  37.       
  38.     @Override  
  39.     public View getView(int position, View view, ViewGroup parent) {  
  40.         ImageView imageView;  
  41.         if(view==null){  
  42.             imageView=new ImageView(context);  
  43.             imageView.setLayoutParams(new GridView.LayoutParams(200,190));  
  44.             imageView.setAdjustViewBounds(false);  
  45.             imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);  
  46.             imageView.setPadding(5555);  
  47.         }else{  
  48.             imageView=(ImageView)view;  
  49.         }  
  50.              myImageLoader.downLoad(URLS[position], (ImageView)imageView , context);  
  51.              return imageView;  
  52.     }  
  53. }  

MyImageLoader.java

[java]  view plain copy
  1. public class MyImageLoader {  
  2.   
  3.     //最大内存  
  4.     final static int memClass = (int) Runtime.getRuntime().maxMemory();    
  5.     private Context context;  
  6.       
  7.     // 是否缓存到硬盘  
  8.     private boolean  diskcache = true;  
  9.   
  10.     // 定义一级 缓存的图片数  
  11.     private static final int catch_num = 10;  
  12.       
  13.     // 定义二级缓存 容器  软引用  
  14.     private static ConcurrentHashMap<String, SoftReference<Bitmap>> current_hashmap = new ConcurrentHashMap<String, SoftReference<Bitmap>>();  
  15.       
  16.     // 定义一级缓存容器  强引用       (catch_num ,0.75f,true) 默认参数                                                                                                                        2.加载因子默认        3.排序模式 true  
  17.     private static LinkedHashMap<String, Bitmap> link_hashmap = new LinkedHashMap<String, Bitmap>(catch_num ,0.75f,true) {  
  18.   
  19.     // 必须实现的方法  
  20.         protected boolean removeEldestEntry(java.util.Map.Entry<String, Bitmap> eldest) {  
  21.             /** 当一级缓存中 图片数量大于 定义的数量 放入二级缓存中 
  22.              */  
  23.             if (this.size() > catch_num) {  
  24.                 // 软连接的方法 存进二级缓存中  
  25.                 current_hashmap.put(eldest.getKey(), new SoftReference<Bitmap>(  
  26.                         eldest.getValue()));  
  27.                 //缓存到本地  
  28.                 cancheToDisk(eldest.getKey(),eldest.getValue() );  
  29.                   
  30.                 return true;  
  31.             }  
  32.                return false;  
  33.         };  
  34.     };  
  35.   
  36.     public MyImageLoader(Context context) {  
  37.   
  38.     }  
  39.       
  40.       
  41.     /**    
  42.      *  外部调用此方法   进行下载图片   
  43.      */  
  44.     public void downLoad(String key , ImageView imageView,Context context){  
  45.       // 先从缓存中找   。    
  46.         context = this.context;  
  47.           
  48.         Bitmap bitmap = getBitmapFromCache(key);  
  49.         ifnull!= bitmap){   
  50.             imageView.setImageBitmap(bitmap);  
  51.             cancleDownload(key, imageView);         //取消下载  
  52.             return ;  
  53.         }      
  54.           
  55.         // 缓存中 没有  把当前的 imageView 给他 得到 task   
  56.         if(cancleDownload(key, imageView)){     //没有任务进行。,。。开始下载  
  57.             ImageDownloadTask task = new ImageDownloadTask(imageView);  
  58.             Zhanwei_Image  zhanwei_image = new Zhanwei_Image(task);  
  59.             //先把占位的图片放进去  
  60.             imageView.setImageDrawable(zhanwei_image);  
  61.             // task执行任务  
  62.             task.execute(key);   
  63.         }  
  64.     }  
  65.       
  66.       
  67.     /** 此方法 用于优化  : 用户直接 翻到 哪个 就先加载 哪个、 
  68.      * @param key                - URL 
  69.      * @param imageView          - imageView 
  70.      *  core: 给当前的 imageView 得到给他下载的 task 
  71.      */  
  72.       
  73.     private boolean cancleDownload(String key,ImageView imageView){  
  74.         // 给当前的 imageView 得到给他下载的 task  
  75.         ImageDownloadTask task = getImageDownloadTask(imageView);  
  76.         if(null != task){  
  77.             String down_key = task.key;  
  78.               ifnull == down_key || !down_key.equals(key)){  
  79.                   task.cancel(true);        // imageview 和 url 的key不一样       取消下载     
  80.               }else{  
  81.                   return false;      //正在下载:   
  82.               }    
  83.           }  
  84.              return true;            //没有正在下载  
  85.     }  
  86.       
  87.       
  88.       
  89. //  public void getThisProcessMemeryInfo() {  
  90. //        int pid = android.os.Process.myPid();  
  91. //        android.os.Debug.MemoryInfo[] memoryInfoArray = activityManager.getProcessMemoryInfo(new int[] {pid});  
  92. //        System.out.println("本应用当前使用了" + (float)memoryInfoArray[0].getTotalPrivateDirty() / 1024 + "mb的内存");  
  93. //    }  
  94.   
  95.       
  96.       
  97.     /** 
  98.      * 从缓存中得到 图片的方法 1.先从一级 缓存找 linkhashmap 不是线程安全的 必须要加同步 
  99.      */  
  100.     public Bitmap getBitmapFromCache(String key) {  
  101.          //1.先在一级缓存中找  
  102.          synchronized (link_hashmap) {  
  103.             Bitmap bitmap = link_hashmap.get(key);  
  104.             if (null != bitmap) {  
  105.                 link_hashmap.remove(key);  
  106.                 // 按照 LRU是Least Recently Used 近期最少使用算法 内存算法 就近 就 原则 放到首位  
  107.                 link_hashmap.put(key, bitmap);  
  108.                 System.out.println(" 在缓存1中找图片了 =" +key);  
  109.                 return bitmap;  
  110.             }  
  111.         }  
  112.           
  113.          // 2. 到二级 缓存找  
  114.         SoftReference<Bitmap> soft = current_hashmap.get(key);  
  115.         if (soft != null) {  
  116.             //得到 软连接 中的图片  
  117.             Bitmap soft_bitmap = soft.get();        
  118.             if (null != soft_bitmap) {  
  119.                 System.out.println(" 在缓存2中找图片了 =" +key);  
  120.                 return soft_bitmap;  
  121.             }  
  122.         } else {  
  123.             // 没有图片的话 把这个key删除  
  124.             current_hashmap.remove(key);        
  125.         }  
  126.           
  127.           
  128.         //3.都没有的话去从外部缓存文件读取  
  129.         if(diskcache){  
  130.             Bitmap bitmap = getBitmapFromFile(key);  
  131.             if(bitmap!= null){  
  132.                 link_hashmap.put(key, bitmap);   //将图片放到一级缓存首位  
  133.                 return bitmap;  
  134.             }  
  135.         }  
  136.           
  137.         return null;  
  138.     }  
  139.   
  140.       
  141.     /** 
  142.      * 缓存到本地文件 
  143.      * @param key 
  144.      * @param bitmap 
  145.      */  
  146.     public static void cancheToDisk(String key ,Bitmap bitmap ){  
  147.         //2.缓存bitmap至/data/data/packageName/cache/文件夹中  
  148.         try {  
  149.             String fileName = getMD5Str(key);  
  150.             String filePath = ImagedownActivity.filepath + "/" + fileName;  
  151.             System.out.println("缓存到本地===" + filePath);  
  152.             FileOutputStream fos = new FileOutputStream(filePath);  
  153.             bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);  
  154.               
  155.         } catch (Exception e) {  
  156.               
  157.         }  
  158.     }  
  159.       
  160.       
  161.     /** 
  162.      * 从外部文件缓存中获取bitmap 
  163.      * @param url 
  164.      * @return 
  165.      */  
  166.     private Bitmap getBitmapFromFile(String url){  
  167.         Bitmap bitmap = null;  
  168.         String fileName = getMD5Str(url);  
  169.         if(fileName == null){  
  170.             return null;  
  171.         }     
  172.         String filePath = ImagedownActivity.filepath + "/" + fileName;        
  173.         try {  
  174.             FileInputStream fis = new FileInputStream(filePath);  
  175.             bitmap = BitmapFactory.decodeStream(fis);  
  176.             System.out.println("在本地缓存中找到图片==="+ filePath);  
  177.         } catch (FileNotFoundException e) {  
  178.             System.out.println("getBitmapFromFile==="+ e.toString());  
  179.             e.printStackTrace();  
  180.             bitmap = null;  
  181.         }  
  182.         return bitmap;  
  183.     }  
  184.       
  185.       
  186.       
  187.     /** 
  188.      * 清理文件缓存 
  189.      * @param dirPath 
  190.      * @return 
  191.      */  
  192.     public static boolean removeCache(String dirPath) {  
  193.         File dir = new File(dirPath);  
  194.         File[] files = dir.listFiles();  
  195.         if(files == null || files.length == 0) {  
  196.             return true;  
  197.         }  
  198.             int dirSize = 0;  
  199.             //这里删除所有的缓存  
  200.             int all_ = (int) ( 1 * files.length + 1);  
  201.             //对files 进行排序  
  202.             Arrays.sort(files, new FileLastModifiedSort());  
  203.             for (int i = 0; i < all_ ; i++) {  
  204.                     files[i].delete();  
  205.             }  
  206.         return true;  
  207.     }  
  208.       
  209.   
  210.     /** 
  211.      * 根据文件最后修改时间进行排序 
  212.      */  
  213.     private static class FileLastModifiedSort implements Comparator<File> {  
  214.         @Override  
  215.         public int compare(File lhs, File rhs) {  
  216.             if(lhs.lastModified() > rhs.lastModified()) {  
  217.                 return 1;  
  218.             } else if(lhs.lastModified() == rhs.lastModified()) {  
  219.                 return 0;  
  220.             } else {  
  221.                 return -1;  
  222.             }  
  223.         }  
  224.     }  
  225.       
  226.   
  227.     /**   
  228.      * MD5 加密   
  229.      */     
  230.     private static String getMD5Str(String str) {     
  231.         MessageDigest messageDigest = null;     
  232.         try {     
  233.             messageDigest = MessageDigest.getInstance("MD5");     
  234.             messageDigest.reset();     
  235.             messageDigest.update(str.getBytes("UTF-8"));     
  236.         } catch (NoSuchAlgorithmException e) {     
  237.             System.out.println("NoSuchAlgorithmException caught!");     
  238.             return null;  
  239.         } catch (UnsupportedEncodingException e) {     
  240.             e.printStackTrace();  
  241.             return null;  
  242.         }     
  243.      
  244.         byte[] byteArray = messageDigest.digest();     
  245.         StringBuffer md5StrBuff = new StringBuffer();     
  246.         for (int i = 0; i < byteArray.length; i++) {                 
  247.             if (Integer.toHexString(0xFF & byteArray[i]).length() == 1)     
  248.                 md5StrBuff.append("0").append(Integer.toHexString(0xFF & byteArray[i]));     
  249.             else     
  250.                 md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i]));     
  251.         }     
  252.         return md5StrBuff.toString();     
  253.     }    
  254.       
  255.       
  256.     // ------------------------ 异步加载----------------------------  
  257.     /** 
  258.      *  占位的 图片 或者 颜色      用来绑定 相应的图片 
  259.      */    
  260.    class Zhanwei_Image extends ColorDrawable{  
  261.         //里面存放 相应 的异步 处理时加载好的图片 ----- 相应的 task  
  262.         private final WeakReference<ImageDownloadTask>  taskReference;  
  263.         public Zhanwei_Image(ImageDownloadTask task){     
  264.             super(Color.BLUE);  
  265.             taskReference = new WeakReference<MyImageLoader.ImageDownloadTask>(task);   
  266.         }    
  267.          // 返回去这个 task 用于比较  
  268.         public ImageDownloadTask getImageDownloadTask(){  
  269.           return taskReference.get();  
  270.         }  
  271.     }  
  272.       
  273.      
  274.     // 根据 给 的 iamgeView、 得到里面的 task  用于和当前的 task比较是不是同1个  
  275.     private ImageDownloadTask getImageDownloadTask(ImageView imageView){  
  276.         ifnull != imageView){  
  277.                 Drawable drawable = imageView.getDrawable();      
  278.             if( drawable instanceof Zhanwei_Image)  
  279.                 return ((Zhanwei_Image)drawable).getImageDownloadTask();  
  280.               
  281.         }  
  282.         return null;  
  283.     }  
  284.       
  285.       
  286.       
  287.     /** 
  288.      * 把图片 添加到缓存中 
  289.      */  
  290.     public void addBitmap(String key, Bitmap bitmap) {  
  291.         if (null != bitmap) {  
  292.             synchronized (link_hashmap) {         // 添加到一级 缓存中  
  293.                 link_hashmap.put(key, bitmap);  
  294.             }  
  295.         }  
  296.     }  
  297.       
  298.       
  299.     /** 在后台 加载每个图片 
  300.      *  第一个参数 第2个要进度条不 第三个返回结果 bitmap 
  301.      */  
  302.     class ImageDownloadTask extends AsyncTask<String, Void, Bitmap> {  
  303.   
  304.         private String key;  
  305.         private WeakReference<ImageView> imgViReference;  
  306.   
  307.         public ImageDownloadTask(ImageView imageView) {  
  308.             //imageView 传进来 。。要给哪个iamgeView加载图片  
  309.             imgViReference = new WeakReference<ImageView>(  
  310.                     imageView);  
  311.         }  
  312.   
  313.         @Override  
  314.         protected Bitmap doInBackground(String... params){  
  315.             key = params[0];  
  316.             //调用下载函数 根据 url 下载        
  317.             return downloadBitmap(key);  
  318.         }  
  319.   
  320.         @Override  
  321.         protected void onPostExecute(Bitmap result) {  
  322.             if(isCancelled()){  
  323.                 result = null;  
  324.             }  
  325.               
  326.             System.out.println("result=="+ result.getByteCount()+"---memClassmemery="+memClass);  
  327.               
  328.             if(null!= result){  
  329.                 //保存到缓存中  
  330.                    addBitmap(key, result);  
  331.                    ImageView  imageView = imgViReference.get();  
  332.                    ifnull != imageView){    
  333.                   //向 imageView 里面放入 bitmap           
  334.                         ImageDownloadTask task = getImageDownloadTask(imageView);  
  335.                           
  336.                    /** 
  337.                     *  判断 是不是 同一个 task( ) 
  338.                     *  如果当前这个 task  ==  imageView 里面的那个 task 就是同1个 
  339.                     */  
  340.                     ifthis == task ){  
  341.                          imageView.setImageBitmap(result);  
  342.                            
  343.                      }  
  344.                 }  
  345.             }  
  346.         }  
  347.     }  
  348.   
  349.       
  350.     /** 
  351.      * 连接网络 客户端 下载图片 
  352.      */  
  353.     private Bitmap downloadBitmap(String url) {  
  354.        
  355.         final HttpClient client = AndroidHttpClient.newInstance("Android");  
  356.         final HttpGet getRequest = new HttpGet(url);   
  357.         try {  
  358.             HttpResponse response = client.execute(getRequest);  
  359.             final int statusCode = response.getStatusLine().getStatusCode();  
  360.               
  361.             if (statusCode != HttpStatus.SC_OK) {  
  362.                   
  363.                 Log.w("ImageDownloader""Error " + statusCode + " while retrieving bitmap from " + url);  
  364.                 return null;  
  365.             }  
  366.   
  367.             final HttpEntity entity = response.getEntity();  
  368.             if (entity != null) {  
  369.                 InputStream inputStream = null;  
  370.                 try {  
  371.                       
  372.                     inputStream = entity.getContent();                   
  373.                     /** 
  374.                      *  1.没有压缩直接将生成的bitmap返回去 
  375.                      */  
  376. //                  return BitmapFactory.decodeStream(inputStream);  
  377.                       
  378.                    /** 
  379.                     *  2.得到data后在这里把图片进行压缩 
  380.                     */  
  381.                      byte[] data = StreamTool.read(inputStream);   
  382.                      return  BitmapManager.scaleBitmap(context, data, 0.3f);  
  383. //                   return BitmapFactory.decodeStream(new FlushedInputStream(inputStream));  
  384.                 } finally {  
  385.                     if (inputStream != null) {  
  386.                         inputStream.close();  
  387.                     }  
  388.                     entity.consumeContent();  
  389.                 }  
  390.             }  
  391.         } catch (IOException e) {  
  392.             getRequest.abort();  
  393.         } catch (IllegalStateException e) {  
  394.             getRequest.abort();  
  395.         } catch (Exception e) {  
  396.             getRequest.abort();  
  397.         } finally {  
  398.             if ((client instanceof AndroidHttpClient)) {  
  399.                 ((AndroidHttpClient) client).close();  
  400.             }  
  401.         }  
  402.         return null;  
  403.     }  
  404.   
  405. }  

StreamTool.java

[java]  view plain copy
  1. public class StreamTool {  
  2.       
  3.     public static  byte[] read(InputStream in) throws Exception{  
  4.         ByteArrayOutputStream  out_byte = new ByteArrayOutputStream();  
  5.         byte[] buff = new byte[1024];  
  6.         int len=0;  
  7.         while((len = in.read(buff))!= -1){  
  8.              //写到内存中  字节流  
  9.             out_byte.write( buff, 0 , len);  
  10.         }     
  11.         out_byte.close();     
  12.         // 把内存数据返回  
  13.         return  out_byte.toByteArray();   
  14.     }  
  15. }  


BitmapManager.java ( 这个类里面对 网络资源的图片 进行了优化)

[java]  view plain copy
  1. public class BitmapManager {  
  2.       
  3.     /** 
  4.      * 按屏幕适配Bitmap 
  5.      */  
  6.     public static Bitmap scaleBitmap(Context context, byte[] data , float percent) {  
  7.   
  8.       //这里我不获取了,假设是下面这个分辨率  
  9.           int screenWidth =   540;  
  10.       int screenrHeight = 950;  
  11.         //设置 options  
  12.         BitmapFactory.Options options = new BitmapFactory.Options();  
  13.         /** 
  14.          *  BitmapFactory.Options这个类,有一个字段叫做 inJustDecodeBounds.SDK中对这个成员的说明是这样的: 
  15.          *  If set to true, the decoder will return null (no bitmap), but the out… 
  16.          *  也就是说,如果我们把它设为true,那么BitmapFactory.decodeFile(String path, Options opt)并不会真的返回一个Bitmap给你, 
  17.          *  它仅仅会把它的宽,高取回来给你,这样就不会占用太多的内存,也就不会那么频繁的发生OOM了。 
  18.          */  
  19.         options.inJustDecodeBounds = true;  
  20.     
  21.         //读取  
  22.         Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);  
  23.           
  24.         int imgWidth = options.outWidth;  
  25.         int imgHeight = options.outHeight;  
  26.   
  27.         //如果比你设置的宽高大  就进行缩放,  
  28.         if(imgWidth > screenWidth * percent || imgHeight > screenrHeight * percent) {  
  29.             options.inSampleSize = calculateInSampleSize(options, screenWidth, screenrHeight, percent);  
  30.         }  
  31.           
  32.           
  33.         /** 
  34.          * If set to true, the decoder will return null (no bitmap), but the out... fields will still be set, allowing the caller  
  35.          * to query the bitmap without having to allocate the memory for its pixels. 
  36.          *  
  37.          * 如果设置成 true,这个编码将会返回1个null , 但是那个区域仍将被设置(也就是存在),允许(调用者)去查询那个没有分配 内存的像素  bitmap  
  38.          */  
  39.         options.inJustDecodeBounds = false;  
  40.          
  41.         /** 
  42.          *  Android的Bitmap.Config给出了bitmap的一个像素所对应的存储方式, 
  43.          *  有RGB_565,ARGB_8888,ARGB_4444,ALPHA_8四种。RGB_565表示的是红绿蓝三色分别用5,6,5个比特来存储, 
  44.          *  一个像素占用了5+6+5=16个比特。ARGB_8888表示红绿蓝和半透明分别用8,8,8,8个比特来存储, 
  45.          *  一个像素占用了8+8+8+8=32个比特。这样的话如果图片是以RGB_8888读入的,那么占用内存的大小将是RGB_565读入方式的2倍。 
  46.          *  通常我们给Imagview加载图片是通过setDrawable或者在xml文件中用android:src来设置 
  47.          *  默认的加载图片大小的方式是以RGB_8888读入的。 
  48.          *  
  49.          */  
  50.         options.inPreferredConfig = Bitmap.Config.RGB_565;  
  51.           
  52.         /** 
  53.          * If this is set to true, then the resulting bitmap will allocate its pixels such that they can be purged  
  54.          * if the system needs to reclaim memory. 
  55.          *  
  56.          * 如果设置成 true, 这个结果bitmap 将会被分配像素,这样他们就能被 系统回收了,当系统需要回收内存的时候 
  57.          */  
  58.         options.inPurgeable = true;  
  59.           
  60.         /** 
  61.          * This field works in conjuction with inPurgeable. 
  62.          * 这个方法是在   inPurgeable 的基础上工作的 
  63.          */  
  64.         options.inInputShareable = true;  
  65.           
  66.       
  67.         bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);  
  68.           
  69.         System.out.println("data==="+  data.length +"  change == bitmap byte "+ bitmap.getByteCount());  
  70.         return bitmap;  
  71.     }  
  72.       
  73.       
  74.       
  75.     //                                                    options       reqWidth 屏幕宽      reqHeight屏幕高      你的view是屏幕的多大  
  76.     public static int calculateInSampleSize(BitmapFactory.Options options, int screenWidth, int screenHeight ,float percent) {  
  77.           
  78.         // 原始图片宽高  
  79.         final int height = options.outHeight;  
  80.         final int width = options.outWidth;  
  81.         // 倍数  
  82.         int inSampleSize = 1;  
  83.     
  84.         if (height > screenHeight * percent || width > screenWidth * percent) {  
  85.               
  86.             // 计算目标宽高与原始宽高的比值  
  87.             final int inSampleSize_h = Math.round((float) height / (float)( screenHeight * percent));  
  88.               
  89.             final int inSampleSize_w = Math.round((float) width / (float)( screenWidth * percent));  
  90.                   
  91.             // 选择两个比值中较小的作为inSampleSize的  
  92.             inSampleSize = inSampleSize_h < inSampleSize_w ? inSampleSize_h : inSampleSize_w;  
  93.              
  94.             System.out.println("inSampleSize===="+ inSampleSize);  
  95.             //   
  96.             if(inSampleSize < 1) {  
  97.                 inSampleSize = 1;  
  98.             }  
  99.         }  
  100.        //简单说这个数字就是 缩小为原来的几倍,根据你的image需要占屏幕多大动态算的(比如你用的权重设置layout)  
  101.         return inSampleSize;  
  102.     }  
  103. }  

这个是代码输出的最多给这个进程分配的内存 128M



可以看到我上面的bitmapManager 里面有个   options.inPreferredConfig   注释写的很清楚,可以上去看一下,接下来贴几种格式的效果图

rgb565  和  argb_444  所占的内存              (54000)



看一下 argb_8888                    (  108000)



当然可能仔细看的人会看到我一开始截的 鸣人的效果图 上半部分 和 下半部分的颜色会有点问题。上面的rgb_565 生成的,和原图色彩可能会有点出入。

但是内存真心少了一半,所以各种取舍就看个人了,代码注释都谢的很清楚了。


至于 : MyImageLoaderLru.java  其实就是    MyImageLoader.java

先贴出代码不同地方的代码 : 就是在强引用的地方  把  LinkedHashMap 换成了 LruCache

[java]  view plain copy
  1.  // 获取单个进程可用内存的最大值    
  2.     // 方式一:使用ActivityManager服务(计量单位为M)    
  3.        /*int memClass = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();*/    
  4.     // 方式二:使用Runtime类(计量单位为Byte)    
  5.      final static int memClass = (int) Runtime.getRuntime().maxMemory();    
  6.  // 3. 定义一级缓存容器  强引用       (catch_num /2,0.75f,true) 默认参数                                                                                                                        2.加载因子默认        3.排序模式 true  
  7.   final static int  max = memClass/5;  
  8.       
  9.   // LruCache 用强引用将  图片放入     LinkedHashMap   
  10.   private static LruCache<String, Bitmap> lrucache = new LruCache<String, Bitmap>(max) {  
  11.       protected int sizeOf(String key, Bitmap value) {    
  12.              if(value != null) {    
  13.                  // 计算存储bitmap所占用的字节数    
  14.                  return value.getRowBytes() * value.getHeight();    
  15.              } else {    
  16.                  return 0;    
  17.              }    
  18.          }    
  19.              
  20.          @Override    
  21.          protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {    
  22.              if(oldValue != null) {    
  23.                  // 当硬引用缓存容量已满时,会使用LRU算法将最近没有被使用的图片转入软引用缓存    
  24.               current_hashmap.put(key, new SoftReference<Bitmap>(oldValue));    
  25.              }    
  26.          }    
  27. };  


1. 强引用:LruCache 后面再说,其实他内的内部封装的就是1个 LinkedHashMap 。LinkedHashMap 是线程不安全的,所以上面都会用到同步。

2. 软引用:ConcurrentHashMap 是线程安全的,并且支持高并发很有效率,这个后面也会说到,为什么要用 软引用 SoftReference,这个是在系统将要oom时,就会回收

                    软引用的对象资源,所以才会用到他,防止程序出异常 。

3. 磁盘缓存: 这个经常会看到网易新闻等,应用有些界面你看了很多图片,往上翻很多, 其实没有再次访问网络,会将部分image缓存在sdcard里。

4. 其中1个优化: 当比如用户快速滑动到 最底部,其实是最先加载显示给用户的部分的内容的,这样就是用户看到哪加载哪,1个是快,1个是避免资源浪费。


原理: 当用户进入界面加载图片 ,首先会从1级缓存强引用中找,找不到回去2级缓存软引用中找,找不到再去sdcard中找,再找不到才会去请求网络加载资源。

            当然sdcard的缓存 看个人需求是否需要。


注: android 4.0 后 对 SoftReference 的回收机制进行了改变,所以你是可以不用 2级缓存的,直接去掉就好了。

          只要控制好你的 lrucache 或者 linkedhashmap就好了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值