Chinaunix首页 | 论坛 | 博客
  • 博客访问: 53304
  • 博文数量: 48
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 260
  • 用 户 组: 普通用户
  • 注册时间: 2016-07-12 11:48
文章分类
文章存档

2016年(48)

我的朋友

分类: Java

2016-11-30 10:32:08

        wemall-mobile是基于WeMall的android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改。本文分享wemall app商城源码Android之ListView异步加载网络图片(优化缓存机制)代码信息,供技术员参考学习。

1、采用线程池

2、内存缓存+文件缓存

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

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

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


点击(此处)折叠或打开

  1. public class MemoryCache {

  2.     private static final String TAG = "MemoryCache";
  3.     // 放入缓存时是个同步操作
  4.     // LinkedHashMap构造方法的最后一个参数true代表这个map里的元素将按照最近使用次数由少到多排列,即LRU
  5.     // 这样的好处是如果要将缓存中的元素替换,则先遍历出最近最少使用的元素来替换以提高效率
  6.     private Map<String, Bitmap> cache = Collections
  7.             .synchronizedMap(new LinkedHashMap<String, Bitmap>(10, 1.5f, true));
  8.     // 缓存中图片所占用的字节,初始0,将通过此变量严格控制缓存所占用的堆内存
  9.     private long size = 0;// current allocated size
  10.     // 缓存只能占用的最大堆内存
  11.     private long limit = 1000000;// max memory in bytes

  12.     public MemoryCache() {
  13.         // use 25% of available heap size
  14.         setLimit(Runtime.getRuntime().maxMemory() / 4);
  15.     }

  16.     public void setLimit(long new_limit) {
  17.         limit = new_limit;
  18.         Log.i(TAG, "MemoryCache will use up to " + limit / 1024. / 1024. + "MB");
  19.     }

  20.     public Bitmap get(String id) {
  21.         try {
  22.             if (!cache.containsKey(id))
  23.                 return null;
  24.             return cache.get(id);
  25.         } catch (NullPointerException ex) {
  26.             return null;
  27.         }
  28.     }

  29.     public void put(String id, Bitmap bitmap) {
  30.         try {
  31.             if (cache.containsKey(id))
  32.                 size -= getSizeInBytes(cache.get(id));
  33.             cache.put(id, bitmap);
  34.             size += getSizeInBytes(bitmap);
  35.             checkSize();
  36.         } catch (Throwable th) {
  37.             th.printStackTrace();
  38.         }
  39.     }

  40.     /**
  41.      * 严格控制堆内存,如果超过将首先替换最近最少使用的那个图片缓存
  42.      *
  43.      */
  44.     private void checkSize() {
  45.         Log.i(TAG, "cache size=" + size + " length=" + cache.size());
  46.         if (size > limit) {
  47.             // 先遍历最近最少使用的元素
  48.             Iterator<Entry<String, Bitmap>> iter = cache.entrySet().iterator();
  49.             while (iter.hasNext()) {
  50.                 Entry<String, Bitmap> entry = iter.next();
  51.                 size -= getSizeInBytes(entry.getValue());
  52.                 iter.remove();
  53.                 if (size <= limit)
  54.                     break;
  55.             }
  56.             Log.i(TAG, "Clean cache. New size " + cache.size());
  57.         }
  58.     }

  59.     public void clear() {
  60.         cache.clear();
  61.     }

  62.     /**
  63.      * 图片占用的内存
  64.      *
  65.      * @param bitmap
  66.      * @return
  67.      */
  68.     long getSizeInBytes(Bitmap bitmap) {
  69.         if (bitmap == null)
  70.             return 0;
  71.         return bitmap.getRowBytes() * bitmap.getHeight();
  72.     }
  73. }
也可以使用SoftReference,代码会简单很多,但是我推荐上面的方法。



点击(此处)折叠或打开

  1. public class MemoryCache {
  2.     
  3.     private Map<String, SoftReference<Bitmap>> cache = Collections
  4.             .synchronizedMap(new HashMap<String, SoftReference<Bitmap>>());

  5.     public Bitmap get(String id) {
  6.         if (!cache.containsKey(id))
  7.             return null;
  8.         SoftReference<Bitmap> ref = cache.get(id);
  9.         return ref.get();
  10.     }

  11.     public void put(String id, Bitmap bitmap) {
  12.         cache.put(id, new SoftReference<Bitmap>(bitmap));
  13.     }

  14.     public void clear() {
  15.         cache.clear();
  16.     }

  17. }


 下面是文件缓存类的代码FileCache.java:


点击(此处)折叠或打开

  1. public class FileCache {

  2.     private File cacheDir;

  3.     public FileCache(Context context) {
  4.         // 如果有SD卡则在SD卡中建一个LazyList的目录存放缓存的图片
  5.         // 没有SD卡就放在系统的缓存目录中
  6.         if (android.os.Environment.getExternalStorageState().equals(
  7.                 android.os.Environment.MEDIA_MOUNTED))
  8.             cacheDir = new File(
  9.                     android.os.Environment.getExternalStorageDirectory(),
  10.                     "LazyList");
  11.         else
  12.             cacheDir = context.getCacheDir();
  13.         if (!cacheDir.exists())
  14.             cacheDir.mkdirs();
  15.     }

  16.     public File getFile(String url) {
  17.         // 将url的hashCode作为缓存的文件名
  18.         String filename = String.valueOf(url.hashCode());
  19.         // Another possible solution
  20.         // String filename = URLEncoder.encode(url);
  21.         File f = new File(cacheDir, filename);
  22.         return f;

  23.     }

  24.     public void clear() {
  25.         File[] files = cacheDir.listFiles();
  26.         if (files == null)
  27.             return;
  28.         for (File f : files)
  29.             f.delete();
  30.     }

  31. }


 最后最重要的加载图片的类,ImageLoader.java:


点击(此处)折叠或打开

  1. public class ImageLoader {

  2.     MemoryCache memoryCache = new MemoryCache();
  3.     FileCache fileCache;
  4.     private Map<ImageView, String> imageViews = Collections
  5.             .synchronizedMap(new WeakHashMap<ImageView, String>());
  6.     // 线程池
  7.     ExecutorService executorService;

  8.     public ImageLoader(Context context) {
  9.         fileCache = new FileCache(context);
  10.         executorService = Executors.newFixedThreadPool(5);
  11.     }

  12.     // 当进入listview时默认的图片,可换成你自己的默认图片
  13.     final int stub_id = R.drawable.stub;

  14.     // 最主要的方法
  15.     public void DisplayImage(String url, ImageView imageView) {
  16.         imageViews.put(imageView, url);
  17.         // 先从内存缓存中查找

  18.         Bitmap bitmap = memoryCache.get(url);
  19.         if (bitmap != null)
  20.             imageView.setImageBitmap(bitmap);
  21.         else {
  22.             // 若没有的话则开启新线程加载图片
  23.             queuePhoto(url, imageView);
  24.             imageView.setImageResource(stub_id);
  25.         }
  26.     }

  27.     private void queuePhoto(String url, ImageView imageView) {
  28.         PhotoToLoad p = new PhotoToLoad(url, imageView);
  29.         executorService.submit(new PhotosLoader(p));
  30.     }

  31.     private Bitmap getBitmap(String url) {
  32.         File f = fileCache.getFile(url);

  33.         // 先从文件缓存中查找是否有
  34.         Bitmap b = decodeFile(f);
  35.         if (b != null)
  36.             return b;

  37.         // 最后从指定的url中下载图片
  38.         try {
  39.             Bitmap bitmap = null;
  40.             URL imageUrl = new URL(url);
  41.             HttpURLConnection conn = (HttpURLConnection) imageUrl
  42.                     .openConnection();
  43.             conn.setConnectTimeout(30000);
  44.             conn.setReadTimeout(30000);
  45.             conn.setInstanceFollowRedirects(true);
  46.             InputStream is = conn.getInputStream();
  47.             OutputStream os = new FileOutputStream(f);
  48.             CopyStream(is, os);
  49.             os.close();
  50.             bitmap = decodeFile(f);
  51.             return bitmap;
  52.         } catch (Exception ex) {
  53.             ex.printStackTrace();
  54.             return null;
  55.         }
  56.     }

  57.     // decode这个图片并且按比例缩放以减少内存消耗,虚拟机对每张图片的缓存大小也是有限制的
  58.     private Bitmap decodeFile(File f) {
  59.         try {
  60.             // decode image size
  61.             BitmapFactory.Options o = new BitmapFactory.Options();
  62.             o.inJustDecodeBounds = true;
  63.             BitmapFactory.decodeStream(new FileInputStream(f), null, o);

  64.             // Find the correct scale value. It should be the power of 2.
  65.             final int REQUIRED_SIZE = 70;
  66.             int width_tmp = o.outWidth, height_tmp = o.outHeight;
  67.             int scale = 1;
  68.             while (true) {
  69.                 if (width_tmp / 2 < REQUIRED_SIZE
  70.                         || height_tmp / 2 < REQUIRED_SIZE)
  71.                     break;
  72.                 width_tmp /= 2;
  73.                 height_tmp /= 2;
  74.                 scale *= 2;
  75.             }

  76.             // decode with inSampleSize
  77.             BitmapFactory.Options o2 = new BitmapFactory.Options();
  78.             o2.inSampleSize = scale;
  79.             return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
  80.         } catch (FileNotFoundException e) {
  81.         }
  82.         return null;
  83.     }

  84.     // Task for the queue
  85.     private class PhotoToLoad {
  86.         public String url;
  87.         public ImageView imageView;

  88.         public PhotoToLoad(String u, ImageView i) {
  89.             url = u;
  90.             imageView = i;
  91.         }
  92.     }

  93.     class PhotosLoader implements Runnable {
  94.         PhotoToLoad photoToLoad;

  95.         PhotosLoader(PhotoToLoad photoToLoad) {
  96.             this.photoToLoad = photoToLoad;
  97.         }

  98.         @Override
  99.         public void run() {
  100.             if (imageViewReused(photoToLoad))
  101.                 return;
  102.             Bitmap bmp = getBitmap(photoToLoad.url);
  103.             memoryCache.put(photoToLoad.url, bmp);
  104.             if (imageViewReused(photoToLoad))
  105.                 return;
  106.             BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad);
  107.             // 更新的操作放在UI线程中
  108.             Activity a = (Activity) photoToLoad.imageView.getContext();
  109.             a.runOnUiThread(bd);
  110.         }
  111.     }

  112.     /**
  113.      * 防止图片错位
  114.      *
  115.      * @param photoToLoad
  116.      * @return
  117.      */
  118.     boolean imageViewReused(PhotoToLoad photoToLoad) {
  119.         String tag = imageViews.get(photoToLoad.imageView);
  120.         if (tag == null || !tag.equals(photoToLoad.url))
  121.             return true;
  122.         return false;
  123.     }

  124.     // 用于在UI线程中更新界面
  125.     class BitmapDisplayer implements Runnable {
  126.         Bitmap bitmap;
  127.         PhotoToLoad photoToLoad;

  128.         public BitmapDisplayer(Bitmap b, PhotoToLoad p) {
  129.             bitmap = b;
  130.             photoToLoad = p;
  131.         }

  132.         public void run() {
  133.             if (imageViewReused(photoToLoad))
  134.                 return;
  135.             if (bitmap != null)
  136.                 photoToLoad.imageView.setImageBitmap(bitmap);
  137.             else
  138.                 photoToLoad.imageView.setImageResource(stub_id);
  139.         }
  140.     }

  141.     public void clearCache() {
  142.         memoryCache.clear();
  143.         fileCache.clear();
  144.     }

  145.     public static void CopyStream(InputStream is, OutputStream os) {
  146.         final int buffer_size = 1024;
  147.         try {
  148.             byte[] bytes = new byte[buffer_size];
  149.             for (;;) {
  150.                 int count = is.read(bytes, 0, buffer_size);
  151.                 if (count == -1)
  152.                     break;
  153.                 os.write(bytes, 0, count);
  154.             }
  155.         } catch (Exception ex) {
  156.         }
  157.     }
  158. }


 主要流程是先从内存缓存中查找,若没有再开线程,从文件缓存中查找都没有则从指定的url中查找,并对bitmap进行处理,最后通过下面方法对UI进行更新操作。


点击(此处)折叠或打开

  1. a.runOnUiThread(...);


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

点击(此处)折叠或打开

  1. ImageLoader imageLoader=new ImageLoader(context);
  2. ...
  3. imageLoader.DisplayImage(url, imageView);


 比如你的放在你的ListView的adapter的getView()方法中,当然也适用于GridView。

 

wemall官网地址:

原文详情地址:

wemall doraemonAndroid app商城详情地址:

wemall 开源微商城 ,微信商城,商城源码,三级分销,微生鲜,微水果,微外卖,微订餐---专业的o2o系统

wemall

 

 

阅读(733) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~