资讯专栏INFORMATION COLUMN

wemall app商城源码Android之ListView异步加载网络图片(优化缓存机制)

luqiuwen / 2144人阅读

摘要:本文分享商城源码之异步加载网络图片优化缓存机制代码信息,供技术员参考学习。采用线程池内存缓存文件缓存内存缓存中网上很多是采用来防止堆溢出,这儿严格限制只能使用最大内存的对下载的图片进行按比例缩放,以减少内存的消耗具体的代码里面说明。

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

1、采用线程池
2、内存缓存+文件缓存
3、内存缓存中网上很多是采用SoftReference来防止堆溢出,这儿严格限制只能使用最大JVM内存的1/4
4、对下载的图片进行按比例缩放,以减少内存的消耗
具体的代码里面说明。先放上内存缓存类的代码MemoryCache.java:

public class MemoryCache {

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

    public MemoryCache() {
        // use 25% of available heap size
        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 id) {
        try {
            if (!cache.containsKey(id))
                return null;
            return cache.get(id);
        } catch (NullPointerException ex) {
            return null;
        }
    }

    public void put(String id, Bitmap bitmap) {
        try {
            if (cache.containsKey(id))
                size -= getSizeInBytes(cache.get(id));
            cache.put(id, 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> iter = cache.entrySet().iterator();
            while (iter.hasNext()) {
                Entry 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() {
        cache.clear();
    }

    /**
     * 图片占用的内存
     * 
     * @param bitmap
     * @return
     */
    long getSizeInBytes(Bitmap bitmap) {
        if (bitmap == null)
            return 0;
        return bitmap.getRowBytes() * bitmap.getHeight();
    }
}

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

public class MemoryCache {
    
    private Map> cache = Collections
            .synchronizedMap(new HashMap>());

    public Bitmap get(String id) {
        if (!cache.containsKey(id))
            return null;
        SoftReference ref = cache.get(id);
        return ref.get();
    }

    public void put(String id, Bitmap bitmap) {
        cache.put(id, new SoftReference(bitmap));
    }

    public void clear() {
        cache.clear();
    }

}

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

public class FileCache {

    private File cacheDir;

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

    public File getFile(String url) {
        // 将url的hashCode作为缓存的文件名
        String filename = String.valueOf(url.hashCode());
        // Another possible solution
        // 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();
    }

}

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

public class ImageLoader {

    MemoryCache memoryCache = new MemoryCache();
    FileCache fileCache;
    private Map imageViews = Collections
            .synchronizedMap(new WeakHashMap());
    // 线程池
    ExecutorService executorService;

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

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

    // 最主要的方法
    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 Bitmap getBitmap(String url) {
        File f = fileCache.getFile(url);

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

        // 最后从指定的url中下载图片
        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();
            OutputStream os = new FileOutputStream(f);
            CopyStream(is, os);
            os.close();
            bitmap = decodeFile(f);
            return bitmap;
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

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

            // Find the correct scale value. It should be the power of 2.
            final int REQUIRED_SIZE = 70;
            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;
            }

            // decode with inSampleSize
            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inSampleSize = scale;
            return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
        } catch (FileNotFoundException e) {
        }
        return null;
    }

    // Task for the queue
    private class PhotoToLoad {
        public String url;
        public ImageView imageView;

        public PhotoToLoad(String u, ImageView i) {
            url = u;
            imageView = i;
        }
    }

    class PhotosLoader implements Runnable {
        PhotoToLoad photoToLoad;

        PhotosLoader(PhotoToLoad photoToLoad) {
            this.photoToLoad = photoToLoad;
        }

        @Override
        public void run() {
            if (imageViewReused(photoToLoad))
                return;
            Bitmap bmp = getBitmap(photoToLoad.url);
            memoryCache.put(photoToLoad.url, bmp);
            if (imageViewReused(photoToLoad))
                return;
            BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad);
            // 更新的操作放在UI线程中
            Activity a = (Activity) photoToLoad.imageView.getContext();
            a.runOnUiThread(bd);
        }
    }

    /**
     * 防止图片错位
     * 
     * @param photoToLoad
     * @return
     */
    boolean imageViewReused(PhotoToLoad photoToLoad) {
        String tag = imageViews.get(photoToLoad.imageView);
        if (tag == null || !tag.equals(photoToLoad.url))
            return true;
        return false;
    }

    // 用于在UI线程中更新界面
    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();
    }

    public static 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) {
        }
    }
}

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

a.runOnUiThread(...);

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

ImageLoader imageLoader=new ImageLoader(context);
...
imageLoader.DisplayImage(url, imageView);
 比如你的放在你的ListView的adapter的getView()方法中,当然也适用于GridView。

wemall官网地址:http://www.wemallshop.com
原文详情地址:http://Git.oschina.NET/zzunet...
wemall doraemonAndroid app商城详情地址:http://www.koahub.com/home/pr...
wemall 开源微商城 ,微信商城,商城源码,三级分销,微生鲜,微水果,微外卖,微订餐---专业的o2o系统

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/66267.html

相关文章

  • wemall app商城源码Android 获取XML网络数据并绑定到ListView

    摘要:是基于的商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改。本文分享获取网络数据并绑定到,对数据进行获取并显示代码信息,供技术员参考学习。 wemall-mobile是基于WeMall的android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改。本文分享Android 获取XML网络数据并绑定到ListView,对数...

    Benedict Evans 评论0 收藏0
  • wemall app商城源码Android支付宝通知处理类

    摘要:是基于的商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改。本文分享商城源码之处理支付宝各接口通知返回,供技术员参考学习。 wemall-mobile是基于WeMall的Android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改。本文分享wemall app商城源码Android之处理支付宝各接口通知返回,供技术员...

    Crazy_Coder 评论0 收藏0
  • wemall app商城源码Android支付宝接口公用函数

    摘要:本文分享商城源码之支付宝接口公用函数,该类是请求通知返回两个文件所调用的公用函数核心处理文件,供技术员参考学习。 wemall-mobile是基于WeMall的Android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改。本文分享wemall app商城源码Android之 支付宝接口公用函数,该类是请求、通知返回两个文件所调用的公用函数核心处理文...

    Lemon_95 评论0 收藏0
  • wemall app商城源码AndroidNative(原生)支付模式一demo

    摘要:本文分享原生支付模式一,供技术员参考学习。官网地址模式一商户按固定格式生成链接二维码,用户扫码后调微信会将和用户发送到商户设置的链接上,商户收到请求生成订单,调用统一支付接口下单提交到微信,微信会返回给商户。 wemall-mobile是基于WeMall的Android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改。本文分享Native(原生)支付...

    source 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<