android图片三级缓存原理

概述:

网上加载图片的框架已经有很多,例如:

Picasso所能实现的功能,Glide都能做,无非是所需的设置不同。但是Picasso体积比起Glide小太多如果项目中网络请求本身用的就是okhttp或者retrofit(本质还是okhttp),那么建议用Picasso,体积会小很多(Square全家桶的干活)。Glide的好处是大型的图片流,比如gif、Video,如果你们是做美拍、爱拍这种视频类应用,建议使用。此外还有okhpptutils、smartimageview、imageloader、xutils等。

这些框架都已经封装得很好,很成熟了,这里只是自己的一个demo加深对图片三级缓存的理解。

加载网络图片:

/**
 * 从网络中获取图片
 */
public void getNetBitmap(ImageView imageView ,  String url){
    //异步加载图片
    MyAsyncTask task = new MyAsyncTask();
    task.execute(imageView, url);
}

/**
 * 第一个泛型--异步任务执行的时候,通过execute传过来的参数; 第二个泛型--更新进度; 第三个泛型--异步任务执行以后返回的结果
 */
private class MyAsyncTask extends AsyncTask<Object , Void , Bitmap> {
    private HttpURLConnection connection;
    private ImageView imgeview;
    private String url;
    // 耗时任务执行之前 --主线程
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }
    /**
     * 耗时操作 --- 子线程
     */
    @Override
    protected Bitmap doInBackground(Object... params) {
        imgeview = (ImageView) params[0];
        url = (String) params[1];
        try {
            URL mUrl = new URL(url);
            connection = (HttpURLConnection) mUrl.openConnection();
            connection.setConnectTimeout(5000);//连接超时
            connection.setReadTimeout(5000);//读取超时
            connection.setRequestMethod("GET");//请求方法
            connection.connect();//开启连接
            int responseCode = connection.getResponseCode();//获取响应码
            if(responseCode == 200){
                InputStream inputStream = connection.getInputStream();//响应成功,获取网络反应回来的输入流
                //获取输入流之后,开始设置图片压缩参数,将图片进行压缩
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inSampleSize = 2;// 将图片的宽高都压缩为原来的一半,在开发中此参数需要根据图片展示的大小来确定,否则可能展示的不正常
                options.inPreferredConfig = Bitmap.Config.RGB_565;// 这个压缩的最小
                Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options);// 经过压缩的图片
                imgeview.setTag(url);// 为了保证ImageView控件和URL一一对应,给ImageView设定一个标记
                return bitmap;
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            connection.disconnect();//断开连接
        }
        return null;
    }
    /**
     * 更新进度 --- 主线程
     */
    @Override
    protected void onProgressUpdate(Void... values) {
        super.onProgressUpdate(values);
    }
    /**
     * 耗时操作执行之后----主线程
     */
    @Override
    protected void onPostExecute(Bitmap bitmap) {
        super.onPostExecute(bitmap);
        String mUrl = (String) imgeview.getTag();
        if(mUrl != null && url.equals(mUrl)){
            if(bitmap != null){
                imgeview.setImageBitmap(bitmap);
                // 从网络加载完之后,将图片保存到本地SD卡一份,保存到内存中一份
                LocalCacheUtils.setLocalBitmap(url , bitmap);
            }
        }
    }

内存缓存,考虑的问题:

         1. 你的设备可以为每个应用程序分配多大的内存?以前Android默认是16M。测试的小米2A是64M
         2. 设备屏幕上一次最多能显示多少张图片?有多少图片需要进行预加载,因为有可能很快也会显示在屏幕上?
         3. 你的设备的屏幕大小和分辨率分别是多少?一个超高分辨率的设备(例如 Galaxy Nexus) 比起一个较低分辨率的设备(例如 Nexus S),在持有相同数量图片的时候,
            需要更大的缓存空间。
         4. 图片的尺寸和大小,还有每张图片会占据多少内存空间。
         5. 图片被访问的频率有多高?会不会有一些图片的访问频率比其它图片要高?如果有的话,你也许应该让一些图片常驻在内存当中,或者使用多个LruCache
            对象来区分不同组的图片。
         6. 你能维持好数量和质量之间的平衡吗?有些时候,存储多个低像素的图片,而在后台去开线程加载高像素的图片会更加的有效。
    以上是Google对LruCache的描述,其实LruCache的使用非常简单,跟Map非常相近,只是在创建LruCache对象的时候需要指定它的最大允许内存,
    一般设置为当前应用程序的最大运行内存的八分之一即可

public MemoryCacheUtils() {
    // lruCache最大允许内存一般为Android系统分给每个应用程序内存大小的八分之一(推荐)
    // 获得当前应用程序运行的内存大小
    long mCurrentMemory = Runtime.getRuntime().maxMemory();
    int maxSize = (int) (mCurrentMemory / 8);
    // 给LruCache设置最大的内存
    lruCache = new LruCache<String, Bitmap>(maxSize) {
        @Override
        protected int sizeOf(String key, Bitmap value) {
            // 获取每张图片所占内存的大小
            // 计算方法是:图片显示的宽度的像素点乘以高度的像素点
            int byteCount = value.getRowBytes() * value.getHeight();// 获取图片占用内存大小
            return byteCount;
        }
    };
}
/**
 * 从内存中读取Bitmap
 */
public Bitmap getBitmapFromMemory(String url) {
    Bitmap bitmap = lruCache.get(url);
    LogUtils.e("从内存中获取图片: " + bitmap);
    return bitmap;
}
/**
 * 将图片保存到内存中
 */
public void setBitmap2Memory(String url, Bitmap bitmap) {
    lruCache.put(url, bitmap);
}


本地缓存:

设置图片的时候采用键值对的形式进行存储,将图片的url作为键,作为文件的名字,图片的Bitmap作位值来保存。由于url含有特殊字符,不能直接作为图片的名字来存储,故采用url的MD5值作为文件的名字
         1、向本地sd卡设置图片
         2、获取sd卡中的图片

public static final String FILE_PATH_MIRK = Environment.getExternalStorageDirectory().getAbsolutePath();
/**
 * 从本地sd中获取图片,key是之前用md5加密的值
 */
public static Bitmap getLocalBitmap(String url){
    try {
        String fileName = md5Code(url);
        File file = new File(FILE_PATH_MIRK, fileName);
        LogUtils.e("获取的sd卡路径:" + file.getAbsolutePath());
        if (file.exists()) {
            Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(
                    file));
            return bitmap;
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}
/**
 * 向本地sd卡设置保存图片
 */
public static void setLocalBitmap(String url , Bitmap bitmap){
    LogUtils.e("开始保存文件到sd卡");
    String fileName = md5Code(url);
    File f = new File(FILE_PATH_MIRK, fileName);
    if (f.exists()) {
        f.delete();
    }
    try {
        FileOutputStream out = new FileOutputStream(f);
        bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
        out.flush();
        out.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
/**
 * 进行简单的md5加密
 */
private static String md5Code(String url){
    //生成实现指定摘要算法的 MessageDigest 对象。
    String bufs = null;
    try {
        MessageDigest md =  MessageDigest.getInstance("MD5");
        //使用指定的字节数组更新摘要。
        md.update(url.getBytes());
        //通过执行诸如填充之类的最终操作完成哈希计算。
        byte b[] = md.digest();
        //生成具体的md5密码到buf数组
        int i;
        StringBuffer buf = new StringBuffer("");
        for (int offset = 0; offset < b.length; offset++) {
            i = b[offset];
            if (i < 0)
                i += 256;
            if (i < 16)
                buf.append("0");
            buf.append(Integer.toHexString(i));
        }
        bufs = buf.toString();
        // 32位的加密
        LogUtils.e("加密后的字符串:" + bufs);
        System.out.println("16位: " + buf.toString().substring(8, 24));// 16位的加密,其实就是32位加密后的截取
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return bufs;
}


给指定的ImageView加载图片:

第一个参数是imageview对象,第二个参数是加载的图片url

public void setImageView(ImageView imageView , String url){
    //从内存中获取图片
    bitmap = memoryCacheUtils.getBitmapFromMemory(url);
    if(bitmap != null){
        imageView.setImageBitmap(bitmap);
        LogUtils.e("从内存中获取图片: " + bitmap);
        return;
    }
    //从sd卡中获取图片
    bitmap = LocalCacheUtils.getLocalBitmap(url);
    if(bitmap != null){
        imageView.setImageBitmap(bitmap);
        memoryCacheUtils.setBitmap2Memory(url, bitmap);
        LogUtils.e("从sd卡中获取图片: " + bitmap);
        return;
    }
    //从网络中获取图片
    LogUtils.e("从网络中获取图片");
    netCacheUtils.getNetBitmap(imageView , url);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值