Android如何优雅的缓存网络图片

在Android开发中,基本上都会有与网络握手,如果没实现这个功能,那也就只能是在开发一个“单机游戏”咯。既然从网络获取数据,那就避免不了加载网络数据,那么问题来了,流量怎么办?大家都知道,加载网络图片比加载网络文本耗费的流量多得多。不用担心,今天来分享以下如何缓存网络图片到本地来:

  • 根据图片的url去加载,本地有就加载本地,没有则请求网络
  • 图片压缩,降低内存使用
  • 图片链接和图片上传

主要思路

在我们我实现图片显示的时候,一般都是在Adapter里面,或者其他地方,先调用我们工具类的主方法,主方法重载了两个,传入的参数不一样,一个是传ImageView加图片的url,一个是只传url,这个根据你的需求来。调用主方法后,工具类先会从内存中去加载,如果没有就去本地加载,本地也没有,就就只能去网络请求了,所以会有三个类是去做对应地方的操作。

不罗嗦,上代码

这是主方法,都有注释的,扯那么多理论也还是抵不过一行代码

``` java
import android.graphics.Bitmap;
import android.widget.ImageView;

import com.example.smilefood.R;

/**
 * 自定义图片加载工具类
 * 
 * @author Kevin
 * 
 */
public class MyBitmapUtils {

    private NetCacheUtils mNetCacheUtils;
    private LocalCacheUtils mLocalCacheUtils;
    private MemoryCacheUtils mMemoryCacheUtils;

    public MyBitmapUtils() {

        mMemoryCacheUtils = new MemoryCacheUtils();
        mLocalCacheUtils = new LocalCacheUtils();
        mNetCacheUtils = new NetCacheUtils(mLocalCacheUtils, mMemoryCacheUtils);

    }

    /**
     * 加载图片的核心api
     * 
     * @param ivPic
     *            ImageView对象
     * @param url
     *            图片链接
     */
    public void display(ImageView ivPic, String url) {
        // 从内存缓存读
        Bitmap bitmap = mMemoryCacheUtils.getBitmapFromMemory(url);
        if(bitmap!=null) {//如果内存存在,就直接设置并返回
            ivPic.setImageBitmap(bitmap);
            System.out.println("从内存读取图片");
            return;
        }

        // 从本地缓存读
        bitmap = mLocalCacheUtils.getBitmapFromLocal(url);
        if (bitmap != null) {//如果本地文件存在,就直接设置并返回
            ivPic.setImageBitmap(bitmap);
            System.out.println("从本地读取图片");
            mMemoryCacheUtils.putBitmapToMemory(url, bitmap);//设置内存图片
            return;
        }

        // 从网络缓存下载
        mNetCacheUtils.getBitmapFromNet(ivPic, url);
    }
    public Bitmap display( String url) {
        // 从内存缓存读
        Bitmap bitmap = mMemoryCacheUtils.getBitmapFromMemory(url);
        if(bitmap!=null) {//如果内存存在,就直接设置并返回

            System.out.println("从内存读取图片");
            return bitmap;
        }

        // 从本地缓存读
        bitmap = mLocalCacheUtils.getBitmapFromLocal(url);
        if (bitmap != null) {//如果本地文件存在,就直接设置并返回

            System.out.println("从本地读取图片");
            mMemoryCacheUtils.putBitmapToMemory(url, bitmap);//设置内存图片
            return bitmap;
        }

        // 从网络缓存下载
        //mNetCacheUtils.getBitmapFromNet(ivPic, url);
        return null;
    }
}

本地缓存工具类

public class LocalCacheUtils {

    private static final String LOCAL_PATH = Environment
            .getExternalStorageDirectory().getAbsolutePath() + "/smile_cache";

    /**
     * 从本地读取图片
     * 
     * @param url
     * @return
     */
    public Bitmap getBitmapFromLocal(String url) {

        try {
            String fileName = url;
            File file = new File(LOCAL_PATH, fileName);

            if (file.exists()) {
                // 图片压缩
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inSampleSize = 2;// 表示压缩比例,2表示宽高都压缩为原来的二分之一, 面积为四分之一
                options.inPreferredConfig = Config.RGB_565;// 设置bitmap的格式,565可以降低内存占用

                Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(
                        file), null, options);
                return bitmap;
            } else {
                return null;
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 向本地存图片
     * 
     * @param url
     * @param bitmap
     */
    public void putBitmapToLocal(String url, Bitmap bitmap) {
        try {
            String fileName =url;
            File file = new File(LOCAL_PATH, fileName);
            File parent = file.getParentFile();

            // 创建父文件夹
            if (!parent.exists()) {
                parent.mkdirs();
            }

            bitmap.compress(CompressFormat.JPEG, 100,
                    new FileOutputStream(file));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

内存缓存工具类

public class MemoryCacheUtils {

    // HashMap<String, SoftReference<Bitmap>> mMemoryCache = new HashMap<String,
    // SoftReference<Bitmap>>();
    LruCache<String, Bitmap> mMemoryCache;

    public MemoryCacheUtils() {
        int maxMemory = (int) Runtime.getRuntime().maxMemory();// 当前手机分配给app进程的最大内存,虚拟机默认16M

        System.out.println("maxMemory:" + maxMemory);
        mMemoryCache = new LruCache<String, Bitmap>(maxMemory / 8) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                int size = value.getRowBytes() * value.getHeight();// 返回bitmap占用的内存大小
                System.out.println("sizeof:" + size);
                return size;
            }
        };
    }

    /**
     * 从内存读取图片
     * 
     * @param url
     * @return
     */
    public Bitmap getBitmapFromMemory(String url) {
        // SoftReference<Bitmap> softBitmap = mMemoryCache.get(url);
        // System.out.println("读取内存图片。。。" + softBitmap);
        // if (softBitmap != null) {
        // Bitmap bitmap = softBitmap.get();
        // System.out.println("读取内存图片成功。。。" + bitmap);
        // return bitmap;
        // }

        Bitmap bitmap = mMemoryCache.get(url);
        return bitmap;
    }

    /**
     * 向内存存图片
     * 
     * @param url
     * @param bitmap
     */
    public void putBitmapToMemory(String url, Bitmap bitmap) {
        // System.out.println("设置内存图片。。。");
        // SoftReference<Bitmap> softBitmap = new
        // SoftReference<Bitmap>(bitmap);// 通过软引用对对象包装
        // mMemoryCache.put(url, softBitmap);
        mMemoryCache.put(url, bitmap);
    }

}

网络缓存工具类

public class NetCacheUtils {

    LocalCacheUtils mLocalCacheUtils;
    MemoryCacheUtils mMemoryCacheUtils;

    public NetCacheUtils(LocalCacheUtils localCacheUtils,
            MemoryCacheUtils memoryCacheUtils) {
        mLocalCacheUtils = localCacheUtils;
        mMemoryCacheUtils = memoryCacheUtils;
    }

    public void getBitmapFromNet(ImageView ivPic, String url) {
        BitmapTask task = new BitmapTask();
        task.execute(new Object[] { ivPic, url });
    }

    class BitmapTask extends AsyncTask<Object, Void, Bitmap> {

        private ImageView imageView;
        private String url;

        /**
         * 返回的对象会自动回传到onPostExecute里面
         */
        @Override
        protected Bitmap doInBackground(Object... params) {
            imageView = (ImageView) params[0];
            url = (String) params[1];
            imageView.setTag(url);
            Bitmap bitmap = downloadBitmap(url);
            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap result) {
            // 这里的result就是doInBackground返回回来的对象
            if (result != null) {
                String ivUrl = (String) imageView.getTag();
                if (url.equals(ivUrl)) {// 确保imageview设置的是正确的图片(因为有时候listview有重用机制,多个item会公用一个imageview对象,从而导致图片错乱)
                    imageView.setImageBitmap(result);
                    System.out.println("从网络缓存读取图片");

                    // 向本地保存图片文件
                    mLocalCacheUtils.putBitmapToLocal(url, result);
                    // 向内存保存图片对象
                    mMemoryCacheUtils.putBitmapToMemory(url, result);
                }
            }
        }
    }

    /**
     * 下载图片
     * 
     * @param url
     * @return
     */
    private Bitmap downloadBitmap(String url) {
        HttpURLConnection conn = null;
        try {
            conn = (HttpURLConnection) new URL(url).openConnection();
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(5000);
            conn.setRequestMethod("GET");
            conn.connect();

            int responseCode = conn.getResponseCode();
            if (responseCode == 200) {
                InputStream inputStream = conn.getInputStream();

                //图片压缩
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inSampleSize = 2;//表示压缩比例,2表示宽高都压缩为原来的二分之一, 面积为四分之一
                options.inPreferredConfig = Config.RGB_565;//设置bitmap的格式,565可以降低内存占用

                Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options);
                return bitmap;
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            conn.disconnect();
        }

        return null;
    }

}

最后说两句,想要加载图片的时候直接看下面

new MyBitmapUtils().display(holder.foodImg_img,mFoodList.get(position).getFoodUrl());

这里我是在Adapter里面要实现item里面一个ImageView显示图片

浏览器兼容

  1. 目前,本编辑器对Chrome浏览器支持最为完整。建议大家使用较新版本的Chrome。
  2. IE9以下不支持
  3. IE9,10,11存在以下问题
  1. 不支持离线功能
  2. IE9不支持文件导入导出
  3. IE10不支持拖拽文件导入