android实现缓存图片等数据

采用LinkedHashMap自带的LRU 算法缓存数据, 可检测对象是否已被虚拟机回收,并且重新计算当前缓存大小,清除缓存中无用的键值对象(即已经被虚拟机回收但未从缓存清除的数据);
 * 默认内存缓存大小为: 4 * 1024 * 1024 可通过通过setMaxCacheSize重新设置缓存大小,可手动清空内存缓存
 * <br>支持内存缓存和磁盘缓存方式, 通过 {@link cc.util.cache.NetByteWrapper} 支持HTTP缓存 (注:详细参考cc.util.http包); 注:使用JDK7

package cc.util.cache;

import java.io.Serializable;
import java.util.Objects;

/**
  封装网络数据, 将数据的Etag、lastModified获取到, 下次请求的时候提取出来到服务器比对
 * Help to wrap byte data which obtains from network, It will work with {@link cc.util.cache.NetChacheManager}
 * @author wangcccong
 * @version 1.1406
 * <br> create at: Tues, 10 Jun 2014
 */
public class NetByteWrapper implements Serializable {

  private final static long serialVersionUID = 1L;

  /** data from network */
  private byte[] data;
  /** data size */
  int contentLength;
  /** latested modify time */
  private long lastModified;
  /** ETag: look up HTTP Protocol */
  private String ETag;

  public NetByteWrapper(byte[] data, long lastModified, String Etag) {
    this.data = data;
    this.lastModified = lastModified;
    this.ETag = Etag;
  }

  public byte[] getData() {
    return data;
  }
  public void setData(byte[] data) {
    this.data = data;
  }

  public long getLastModified() {
    return lastModified;
  }
  public void setLastModified(long lastModified) {
    this.lastModified = lastModified;
  }

  public String getETag() {
    return ETag;
  }

  public void setETag(String eTag) {
    this.ETag = eTag;
  }

  public int getContentLength() {
    return Objects.isNull(data) ? 0 : data.length;
  }
}

package cc.util.cache;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;

/**采用软引用方式将数据存放起来
 * enclose {@link cc.util.cache.NetByteWrapper} with {@link java.lang.ref.SoftReference}, In order to recycle the memory
 * @author wangcccong
 * @version 1.1406
 * <br> create at: Tues, 10 Jun. 2014
 */
public class NetByteSoftReference extends SoftReference<NetByteWrapper> {

  private String key = "";
  private long length = 0;

  public NetByteSoftReference(String key, NetByteWrapper arg0) {
    this(key, arg0, null);
  }

  public NetByteSoftReference(String key, NetByteWrapper arg0,
      ReferenceQueue<? super NetByteWrapper> arg1) {
    super(arg0, arg1);
    // TODO Auto-generated constructor stub
    this.key = key;
    this.length = arg0.getContentLength();
  }

  public String getKey() {
    return key;
  }

  public long getLength() {
    return length;
  }

}

package cc.util.cache;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Objects;

/**
 * 采用LinkedHashMap自带的LRU 算法缓存数据, 可检测对象是否已被虚拟机回收,并且重新计算当前缓存大小,清除缓存中无用的键值对象(即已经被虚拟机回收但未从缓存清除的数据);
 * 默认内存缓存大小为: 4 * 1024 * 1024 可通过通过setMaxCacheSize重新设置缓存大小,可手动清空内存缓存,支持采用内存映射方式读取缓存
 * <br>支持内存缓存和磁盘缓存方式, 通过 {@link cc.util.cache.NetByteWrapper} 支持HTTP缓存 (注:详细参考cc.util.http包)
 * @author wangcccong
 * @version 1.1406
 * <br> create at: Tues, 10 Jun 2014
 */
public class NetCacheManager {

  /** max cache size */
  private long MAX_CACHE_SIZE = 4 * 1024 * 1024;
  private long cacheSize = 0; 

  private static NetCacheManager instance = null;

  private final ReferenceQueue<NetByteWrapper> referenceQueue;
  private final LinkedHashMap<String, NetByteSoftReference> cacheMap;

  private NetCacheManager(){
    referenceQueue = new ReferenceQueue<NetByteWrapper>();
    cacheMap = new LinkedHashMap<String, NetByteSoftReference>(16, 0.75f, true) {

      private static final long serialVersionUID = -8378285623387632829L;
      @Override
      protected boolean removeEldestEntry(
          java.util.Map.Entry<String, NetByteSoftReference> eldest) {
        // TODO Auto-generated method stub
        boolean shouldRemove = cacheSize > MAX_CACHE_SIZE;
        if (shouldRemove) {
          cacheSize -= eldest.getValue().getLength();
          System.gc();
        }
        return shouldRemove;
      }
    };
  }

  /** singleton model */
  public static synchronized NetCacheManager newInstance(){
    if (Objects.isNull(instance)) {
      instance = new NetCacheManager();
    }
    return instance;
  }

  /**
   * reset the memory cache size
   * @param cacheSize
   */
  public void setMaxCacheSize(long cacheSize) {
    this.MAX_CACHE_SIZE = cacheSize;
  }

  /**
   * 获取当前内存缓存大小
   * @return
   */
  public long getCacheSize() {
    return cacheSize;
  }

  /**
   * 将数据缓存至内存, 如果http返回的数据<b>不支持</b>缓存则采用此方法,缓存的key一般为请求的url
   * @param key
   * @param value
   */
  public void cacheInMemory(String key, byte[] value) {
    this.cacheInMemory(key, value, 0, null);
  }

  /**
   * 将数据缓存至内存, 如果http返回的数据<b>支持</b>缓存则采用此方法
   * @param key
   * @param value
   * @param lastModified
   */
  public void cacheInMemory(String key, byte[] value, long lastModified) {
    this.cacheInMemory(key, value, lastModified, null);
  }

  /**
   * 将数据缓存至内存, 如果http返回的数据<b>支持</b>缓存则采用此方法
   * @param key
   * @param value
   * @param Etags
   */
  public void cacheInMemory(String key, byte[] value, String Etags) {
    this.cacheInMemory(key, value, 0, Etags);
  }

  /**
   * 将数据缓存至内存, 如果http返回的数据<b>支持</b>缓存则采用此方法
   * @param key
   * @param value
   * @param lastModified
   * @param Etags
   */
  private void cacheInMemory(String key, byte[] value, long lastModified, String Etags) {
    Objects.requireNonNull(key, "key must not be null");
    clearRecycledObject();
    NetByteWrapper wrapper = new NetByteWrapper(value, lastModified, Etags);
    NetByteSoftReference byteRef = new NetByteSoftReference(key, wrapper, referenceQueue);
    cacheMap.put(key, byteRef);
    value = null;
    wrapper = null;
  }

  /**
   * 缓存至磁盘, 默认不首先缓存到内存
   * @param key
   * @param value
   * @param path
   */
  public void cacheInDisk(String key, byte[] value, String path) {
    cacheInDisk(key, value, path, false);
  }

  /**
   *
   * @param key
   * @param value
   * @param path
   * @param cacheInMemory
   */
  public void cacheInDisk(String key, byte[] value, String path, boolean cacheInMemory) {
    this.cacheInDisk(key, value, 0, null, path, cacheInMemory);
  }

  /**
   *
   * @param key
   * @param value
   * @param lastModified
   * @param Etags
   * @param path
   * @param cacheInMemory
   */
  private void cacheInDisk(String key, byte[] value, long lastModified, String Etags, String path, boolean cacheInMemory) {
    if (cacheInMemory) cacheInMemory(key, value, lastModified, Etags);
    try (FileOutputStream fos = new FileOutputStream(path);
        ObjectOutputStream oos = new ObjectOutputStream(fos)) {
        NetByteWrapper wrapper = new NetByteWrapper(value, lastModified, Etags);
        oos.writeObject(wrapper);
    } catch (Exception e) {
        // TODO: handle exception
      e.printStackTrace();
    }
  }

  /**
   * get {@link cc.util.cache.NetByteWrapper} from memory according to key
   * @param key
   * @return {@link cc.util.cache.NetByteWrapper}
   */
  public NetByteWrapper getFromMemory(String key) {
    SoftReference<NetByteWrapper> softReference = cacheMap.get(key);
    return Objects.nonNull(softReference) ? softReference.get() : null;
  }

  /**
   * get byte[] from memory according to key
   * @param context
   * @param key
   * @return
   */
  public byte[] getByteFromMemory(String key) {
    NetByteWrapper wrapper = getFromMemory(key);
    return Objects.nonNull(wrapper) ? wrapper.getData() : null;
  }

  /**
   * 从磁盘获取数据
   * @param path
   * @return {@link cc.util.cache.NetByteWrapper}
   */
  public NetByteWrapper getFromDisk(String path) {
    try (FileInputStream fis = new FileInputStream(path);
        ObjectInputStream ois = new ObjectInputStream(fis)) {
      NetByteWrapper wrapper = (NetByteWrapper) ois.readObject();
      return wrapper;
    } catch (Exception e) {
      // TODO: handle exception
      e.printStackTrace();
      return null;
    }
  }

  /**
   * 采用内存映射的方式从磁盘获取数据(加快读取缓存的大文件)
   * @param path
   * @return
   */
  public NetByteWrapper getFromDiskByMapped(String path) {
    try (FileInputStream fis = new FileInputStream(path);
        FileChannel channel= fis.getChannel();
        ByteArrayOutputStream baos = new ByteArrayOutputStream()){
      MappedByteBuffer mbb = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
      byte[] bts = new byte[1024];
      int len = (int) channel.size();
      for (int offset = 0; offset < len; offset += 1024) {
        if (len - offset > 1024) mbb.get(bts);
        else mbb.get((bts = new byte[len - offset]));
        baos.write(bts);
      }
      ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
      ObjectInputStream ois = new ObjectInputStream(bais);
      NetByteWrapper wrapper = (NetByteWrapper) ois.readObject();
      bais.close();
      ois.close();
      return wrapper;
    } catch (Exception e) {
      // TODO: handle exception
      e.printStackTrace();
      return null;
    }
  }

  /**
   * 从磁盘获取缓存的byte[] 数据
   * @param path
   * @return
   */
  public byte[] getByteFromDisk(String path) {
    NetByteWrapper wrapper = getFromDisk(path);
    return Objects.isNull(wrapper) ? null : wrapper.getData();
  }

  /**
   * 通过内存映射放射从磁盘获取缓存的byte[] 数据
   * @param path
   * @return
   */
  public byte[] getByteFromDiskByMapped(String path) {
    NetByteWrapper wrapper = getFromDiskByMapped(path);
    return Objects.isNull(wrapper) ? null : wrapper.getData();
  }

  /**
   * calculate the size of the cache memory
   */
  private void clearRecycledObject() {
    NetByteSoftReference ref = null;
    //检测对象是否被回收,如果被回收则从缓存中移除死项
    while (Objects.nonNull((ref = (NetByteSoftReference) referenceQueue.poll()))) {
      cacheMap.remove(ref.getKey());
    }
    cacheSize = 0;
    Iterator<String> keys = cacheMap.keySet().iterator();
    while (keys.hasNext()) {
      cacheSize += cacheMap.get(keys.next()).getLength();
    }
  }

  /**
   * clear the memory cache
   */
  public void clearCache() {
    clearRecycledObject();
    cacheMap.clear();
    System.gc();
    System.runFinalization();
  }

}

以上所述就是本文的全部内容了,希望大家能够喜欢。

(0)

相关推荐

  • Android开发笔记之图片缓存、手势及OOM分析

    把图片缓存.手势及OOM三个主题放在一起,是因为在Android应用开发过程中,这三个问题经常是联系在一起的.首先,预览大图需要支持手势缩放,旋转,平移等操作:其次,图片在本地需要进行缓存,避免频繁访问网络:最后,图片(Bitmap)是Android中占用内存的大户,涉及高清大图等处理时,内存占用非常大,稍不谨慎,系统就会报OOM错误. 庆幸的是,这三个主题在Android开发中属于比较普遍的问题,有很多针对于此的通用的开源解决方案.因此,本文主要说明笔者在开发过程中用到的一些第三方开源库.主要

  • Android图片缓存之初识Glide(三)

    前言: 前面总结学习了图片的使用以及Lru算法,今天来学习一下比较优秀的图片缓存开源框架.技术本身就要不断的更迭,从最初的自己使用SoftReference实现自己的图片缓存,到后来做电商项目自己的实现方案不能满足项目的需求改用Afinal,由于Afinal不再维护而选择了师出同门的Xutils,中间也接触过别的开源框架比如Picasso,对Picasso的第一次印象就不太好,初次接触是拿到了公司刚从外包公司接手过来的图片社交类app,对内存占用太大,直接感受就是导致ListView滑动有那么一

  • Android中Glide加载库的图片缓存配置究极指南

    零.选择Glide 为什么图片加载我首先推荐Glide? 图片加载框架用了不少,从afinal框架的afinalBitmap,Xutils的BitmapUtils,老牌框架universalImageLoader,著名开源组织square的picasso,google推荐的glide到FaceBook推出的fresco.这些我前前后后都体验过,那么面对这么多的框架,该如何选择呢?下面简单分析下我的看法. afinal和Xuils在github上作者已经停止维护了,开源社区最新的框架要属KJFra

  • android上的一个网络接口和图片缓存框架enif简析

    1.底层网络接口采用apache的httpclient连接池框架: 2.图片缓存采用基于LRU的算法: 3.网络接口采用监听者模式: 4.包含图片的OOM处理(及时回收处理技术的应用): 图片核心处理类:CacheView.java 复制代码 代码如下: package xiaogang.enif.image; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; imp

  • android异步加载图片并缓存到本地实现方法

    在android项目中访问网络图片是非常普遍性的事情,如果我们每次请求都要访问网络来获取图片,会非常耗费流量,而且图片占用内存空间也比较大,图片过多且不释放的话很容易造成内存溢出.针对上面遇到的两个问题,首先耗费流量我们可以将图片第一次加载上面缓存到本地,以后如果本地有就直接从本地加载.图片过多造成内存溢出,这个是最不容易解决的,要想一些好的缓存策略,比如大图片使用LRU缓存策略或懒加载缓存策略.今天首先介绍一下本地缓存图片. 首先看一下异步加载缓存本地代码: 复制代码 代码如下: public

  • android中图片的三级缓存cache策略(内存/文件/网络)

    1.简介 现在android应用中不可避免的要使用图片,有些图片是可以变化的,需要每次启动时从网络拉取,这种场景在有广告位的应用以及纯图片应用(比如百度美拍)中比较多. 现在有一个问题:假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量.在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响.当然,我想,向百度美拍这样的应用,必然也有其内部的图片缓存策略.总之,图片缓存是很重要而且是必须的. 2.图片缓存的原理 实现图片缓存也不难,需要有相

  • Android图片缓存之Bitmap详解(一)

    前言: 最近准备研究一下图片缓存框架,基于这个想法觉得还是先了解有关图片缓存的基础知识,今天重点学习一下Bitmap.BitmapFactory这两个类.  Bitmap: Bitmap是Android系统中的图像处理的最重要类之一.用它可以获取图像文件信息,进行图像剪切.旋转.缩放等操作,并可以指定格式保存图像文件.  重要函数  •public void recycle() // 回收位图占用的内存空间,把位图标记为Dead  •public final boolean isRecycled

  • Android中使用二级缓存、异步加载批量加载图片完整案例

    一.问题描述 Android应用中经常涉及从网络中加载大量图片,为提升加载速度和效率,减少网络流量都会采用二级缓存和异步加载机制,所谓二级缓存就是通过先从内存中获取.再从文件中获取,最后才会访问网络.内存缓存(一级)本质上是Map集合以key-value对的方式存储图片的url和Bitmap信息,由于内存缓存会造成堆内存泄露, 管理相对复杂一些,可采用第三方组件,对于有经验的可自己编写组件,而文件缓存比较简单通常自己封装一下即可.下面就通过案例看如何实现网络图片加载的优化. 二.案例介绍 案例新

  • Android使用缓存机制实现文件下载及异步请求图片加三级缓存

    首先给大家介绍Android使用缓存机制实现文件下载 在下载文件或者在线浏览文件时,或者为了保证文件下载的正确性,需要使用缓存机制,常使用SoftReference来实现. SoftReference的特点是它的一个实例保存对一个Java对象的软引用,该软引用的存在不妨碍垃圾收集线程对该Java对象的回收.也就是说,一旦SoftReference保存了对一个Java对象的软引用后,在垃圾线程对这个Java对象回收前,SoftReference类所提供的get()方法返回Java对象的强引用.另外

  • Android图片缓存之Lru算法(二)

    前言: 上篇我们总结了Bitmap的处理,同时对比了各种处理的效率以及对内存占用大小,点击查看.我们得知一个应用如果使用大量图片就会导致OOM(out of memory),那该如何处理才能近可能的降低oom发生的概率呢?之前我们一直在使用SoftReference软引用,SoftReference是一种现在已经不再推荐使用的方式,因为从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让软引用变得不再可靠,所以今天我们来认识一种新的缓

随机推荐