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

善姐
• 阅读 1779
    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<String, Bitmap> cache = Collections
            .synchronizedMap(new LinkedHashMap<String, Bitmap>(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<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() {
        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<String, SoftReference<Bitmap>> cache = Collections
            .synchronizedMap(new HashMap<String, SoftReference<Bitmap>>());

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

    public void put(String id, Bitmap bitmap) {
        cache.put(id, new SoftReference<Bitmap>(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<ImageView, String> imageViews = Collections
            .synchronizedMap(new WeakHashMap<ImageView, String>());
    // 线程池
    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系统

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

点赞
收藏
评论区
推荐文章
Jacquelyn38 Jacquelyn38
4年前
Vue.js的图片加载性能优化你可以试试
前言图片加载优化对于一个网站性能好坏起着至关重要的作用。所以我们使用Vue来操作一波。备注以下的优化一、优化二栏目都是我自己封装在Vue的工具函数里,所以请认真看完,要不然直接复制的话,容易出错的。资源Vue.jsElementUI优化一:图片加载动画只有当图片加载完成后才可以显示图片,加载动画结束。我们使用ElementUI
Wesley13 Wesley13
3年前
java 商城系统源码分享
目的snowflake是常见的id(编号)生成算法,由时间戳业务id机器id序列号组合而成,在电商系统(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fwww.javamall.com.cn%2F)中,用于订单号的生成、支付单号的生成等等。本发号器主要解决在容器化的部署情
刘望舒 刘望舒
4年前
Android包管理机制(一)PackageInstaller的初始化
Android框架层Android包管理机制Android框架层本文首发于微信公众号「刘望舒」前言包管理机制是Android中的重要机制,是应用开发和系统开发需要掌握的知识点之一。包指的是Apk、jar和so文件等等,它们被加载到Android内存中,由一个包转变成可执行的代码,这就需要一个机制来进行包的加载、解析、管理等操作,这就是包管理机制。包管理
CuterCorley CuterCorley
4年前
uni-app入门教程(5)接口的基本使用
前言本文主要介绍uniapp提供的一些基础接口,包括:网络请求接口,用于通过指定的请求方法,携带特定的数据,向特定的地址请求并返回请求结果;图片处理接口,包括选择、预览、获取信息、保存到本地等接口;文件处理接口,包括文件上传和下载接口;数据缓存接口,包括以同步或异步的方式保存、获取或删除数据的接口。一、网络请求小程序要想正常运转,都需要与服务器端进
浩浩 浩浩
4年前
【Flutter实战】图片和Icon
3.5图片及ICON3.5.1图片Flutter中,我们可以通过Image组件来加载并显示图片,Image的数据源可以是asset、文件、内存以及网络。ImageProviderImageProvider是一个抽象类,主要定义了图片数据获取的接口load(),从不同的数据源获取图片需要实现不同的ImageProvi
Wesley13 Wesley13
3年前
UGUI 自定义滚动选择列表 ListView
列表在游戏的UI中是非常常见的,例如选服页面,商城页面,奖励页面等等都会有列表的存在。文中我们将这些列表称为ListView(类似于fgui的GList),而列表中的每项称作Item。首先我们来分析下,我们的ListView需要实现哪些功能,以及如何实现功能解决思路可以通过滑动来显示ListView中的Item可以使用UGUI的Scrol
Wesley13 Wesley13
3年前
vivo 全球商城:从 0 到 1 代销业务的融合之路
代销是vivo商城已经落地的成熟业务,本文提供给各位读者vivo商城代销业务中两个异构系统业务融合的对接经验和架构思路。一、业务背景近两年,内销商城业务的发展十分迅速,vivo商城系统的架构也完成了从单体往分布式的演进。我们在vivo商城服务化方向做了很多的努力,基础服务的能力逐渐沉淀下来。2019年我们也开始在产品功
Stella981 Stella981
3年前
Netty入门
一、是什么  Netty是一个高性能、异步事件驱动、基于JavaNIO的异步的可扩展的客户端/服务器网络编程框架。  Netty提供了对TCP、UDP和文件传输的支持,作为一个异步NIO框架,Netty的所有IO操作都是异步非阻塞的,通过FutureListener机制,用户可以方便的主动获取或者通过通知机制获得IO操作结果
Stella981 Stella981
3年前
Android加载网络图片
AndroidManifest.xml文件中加入以下权限设置:    <usespermissionandroid:name"android.permission.INTERNET"/    <usespermissionandroid:name"android.permission.WRITE\_EXTERNAL\_STORAGE
Stella981 Stella981
3年前
AndroidUniversalImageLoader图片异步加载类库的使用
开发App过程中,免不了要进行网络请求操作进行数据交换,比如下载图片,如果自己写一个下载图片的类进行操作的话,要考虑太多太多内容,必须线程池,内存溢出,图片磁盘缓存操作,图片内存缓存操作等等,相当麻烦。好在伟大的开源者们已经写好了一个比较完美的开源类库供大家使用Android(https://www.oschina.net/action/GoTo
善姐
善姐
Lv1
仍怜故乡水,万里送行舟。
文章
5
粉丝
0
获赞
0