自己封装双缓存管理框架Android 库

jopen 9年前

一、概述

Android开发中,网络请求是很重要的一部分,而缓存网络请求来的图片或者响应结果字符串或者结果流,既可以省流量,同时也可以帮助我们

解决无网或弱网情况下加载情况,当然也可以提升程序性能效率。纵所周知,缓存管理中肯定需要用到内存缓存,这里我们采用LruCache来管理内存的缓存。

LruCahce虽然速度快,但是只是内存级别的缓存,为了实现持久化的缓存,我们还需要文件级别的缓存,也就是说我们要把缓存保存到文件,而文件则是保存

到手机存储或者SD卡存储中,即实现Disk级别的缓存,这里我们借助DiskLruCache这个辅助工具类来实现。顾名思义,这个工具类的作用就是使用Lru算法

来存储信息到Disk上。

二、实例效果图

下面是个简单的实例演示效果图

自己封装双缓存管理框架Android 库

三、缓存管理框架的实现解

1、内存缓存类的实现

该类主要实现内存级别的缓存管理类MemoryCache,使用LruCache来实现,因为无论是内存缓存还是Disk缓存,都需要读写操作,

所以我们先抽象出一个缓存接口类:Cache接口:

public interface Cache {      String get(final String key);      void put(final String key, final String value);      boolean remove(final String key);  }

然后,我们的内存缓存类MemoryCache需要实现Cache这个接口:

/**   * 内存缓存类   * Created by caizhiming on 2015/12/4.   */  public class MemoryCache implements Cache {      private LruCache<String, String> mMemoryLruCache;      private EvictedListener mEvictedListener;        public MemoryCache() {          init();      }        public MemoryCache(EvictedListener listener) {          init();          this.mEvictedListener = listener;      }        public void setEvictedListener(EvictedListener listener) {          this.mEvictedListener = listener;      }        public boolean hasEvictedListener() {          return mEvictedListener != null;      }        private void init() {          // 计算可使用的最大内存          final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);          // 取可用内存空间的1/4作为缓存          final int cacheSize = maxMemory / 4;          mMemoryLruCache = new LruCache<String, String>(cacheSize) {              @Override              protected int sizeOf(String key, String value) {                  return value.getBytes().length;              }                @Override              protected void entryRemoved(boolean evicted, String key, String oldValue, String newValue) {                  if (evicted) {                      if (mEvictedListener != null) {                          mEvictedListener.handleEvictEntry(key, oldValue);                      }                  }              }          };      }        @Override      public String get(String key) {          return mMemoryLruCache.get(key);      }        @Override      public void put(String key, String value) {          mMemoryLruCache.put(key, value);      }        @Override      public boolean remove(String key) {          return Boolean.parseBoolean(mMemoryLruCache.remove(key));      }        /**       * called when mMemoryLruCache evict entrys,       * <p/>       * using by CacheManager.Strategy.MEMORY_FIRST       */      public interface EvictedListener {          void handleEvictEntry(String evictKey, String evictValue);      }

2、文件级别的Disk缓存类实现

接下来我们需要写一个用于Disk缓存管理的类:DiskCache类,该类我们也实现Cache接口,

该类的主要功能也是提供Disk缓存的读取和写入操作管理。

/**   * Disk磁盘缓存类   * Created by caizhiming on 2015/12/4.   */  public class DiskCache implements Cache{        private DiskLruCache mDiskLruCache = null;      public DiskCache(Context context){          init(context);      }      /**       * 初始化 DiskLruCache       */      public void init(Context context){          try {              File cacheDir = getDiskCacheDir(context, "http_cache");              if (!cacheDir.exists()) {                  cacheDir.mkdirs();              }              Log.v("czm", "cache file=" + cacheDir.getAbsolutePath());              mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);          } catch (IOException e) {              e.printStackTrace();          }      }      @Override      public String get(String key) {          String result = null;          try {              DiskLruCache.Snapshot snapShot = mDiskLruCache.get(hashKeyForDisk(key));              if (snapShot != null) {                  result = snapShot.getString(0);                  return result;              }          } catch (IOException e) {              e.printStackTrace();              return null;          }          return result;      }        @Override      public void put(String key, String value) {          DiskLruCache.Editor editor = null;          try {              editor = mDiskLruCache.edit(hashKeyForDisk(key));              if (editor != null) {                  editor.set(0, value);                  editor.commit();              }              mDiskLruCache.flush();          } catch (IOException e) {              e.printStackTrace();          }      }        @Override      public boolean remove(String key) {          try {              return mDiskLruCache.remove(hashKeyForDisk(key));          } catch (IOException e) {              e.printStackTrace();          }          return false;      }        public Bitmap getImageCache(String key){          Bitmap bitmap = null;          try {              DiskLruCache.Snapshot snapShot = mDiskLruCache.get(hashKeyForDisk(key));              if (snapShot != null) {                  InputStream is = snapShot.getInputStream(0);                  bitmap = BitmapFactory.decodeStream(is);                  return bitmap;              }          } catch (IOException e) {              e.printStackTrace();          }          return bitmap;      }      public void putImageCache(final String key){          new Thread(new Runnable() {              @Override              public void run() {                  try {                      DiskLruCache.Editor editor = mDiskLruCache.edit(hashKeyForDisk(key));                      if (editor != null) {                          OutputStream outputStream = editor.newOutputStream(0);                          if (downloadUrlToStream(key, outputStream)) {                              editor.commit();                          } else {                              editor.abort();                          }                      }                      mDiskLruCache.flush();                  } catch (IOException e) {                      e.printStackTrace();                  }              }          }).start();      }      private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {          HttpURLConnection urlConnection = null;          BufferedOutputStream out = null;          BufferedInputStream in = null;          try {              final URL url = new URL(urlString);              urlConnection = (HttpURLConnection) url.openConnection();              in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);              out = new BufferedOutputStream(outputStream, 8 * 1024);              int b;              while ((b = in.read()) != -1) {                  out.write(b);              }              return true;          } catch (final IOException e) {              e.printStackTrace();          } finally {              if (urlConnection != null) {                  urlConnection.disconnect();              }              CloseUtils.closeCloseable(out);              CloseUtils.closeCloseable(in);          }          return false;      }          public String hashKeyForDisk(String key) {          String cacheKey;          try {              final MessageDigest mDigest = MessageDigest.getInstance("MD5");              mDigest.update(key.getBytes());              cacheKey = bytesToHexString(mDigest.digest());          } catch (NoSuchAlgorithmException e) {              cacheKey = String.valueOf(key.hashCode());          }          return cacheKey;      }        private String bytesToHexString(byte[] bytes) {          StringBuilder sb = new StringBuilder();          for (int i = 0; i < bytes.length; i++) {              String hex = Integer.toHexString(0xFF & bytes[i]);              if (hex.length() == 1) {                  sb.append('0');              }              sb.append(hex);          }          return sb.toString();      }      public File getDiskCacheDir(Context context, String uniqueName) {          String cachePath;          if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())                  || !Environment.isExternalStorageRemovable()) {              cachePath = context.getExternalCacheDir().getPath();          } else {              cachePath = context.getCacheDir().getPath();          }          return new File(cachePath + File.separator + uniqueName);      }        public int getAppVersion(Context context) {          try {              PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);              return info.versionCode;          } catch (PackageManager.NameNotFoundException e) {              e.printStackTrace();          }          return 1;      }      }

3、搭建封装双缓存管理框架类XCCacheManager

有了上面的内存缓存类MemoryCache和Disk缓存类DiskCache,我们就可以搭建封装真正的缓存管理类XCCacheManager了。

(1) 首先我们采用线程池技术来实现多线程缓存的读写操作

这样可以提高程序的性能,同时能处理任务量比较大的并发读写操作。

private static XCCacheManager mInstance = null;    private Strategy mStrategy = Strategy.MEMORY_FIRST;  //线程池  private ExecutorService mExecutor = null;  //内存缓存  private MemoryCache mMemoryCache;  //Disk缓存  private DiskCache mDiskCache;    /**       * 初始化 DiskLruCache       */      private void init(Context context) {          mExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());          mDiskCache = new DiskCache(context);          mMemoryCache = new MemoryCache();      }

(2)其次XCCacheManager管理类采用单例实现

public static XCCacheManager getInstance(Context context, Strategy strategy) {      if (mInstance == null) {          synchronized (XCCacheManager.class) {              if (mInstance == null) {                  mInstance = new XCCacheManager(context.getApplicationContext(), strategy);              }          }      } else {          mInstance.setStrategy(strategy);      }      return mInstance;  }

(3)缓存策略

这里我们定义了缓存策略,便于适应各种不同业务需求,可以灵活使用不同的策略

enum Strategy {          MEMORY_ONLY(0), MEMORY_FIRST(1), DISK_ONLY(3);          int id;            Strategy(int id) {              this.id = id;          }      }

默认采用内存优先MEMORY_FIRST这种策略

(4)根据对应的策略从缓存中读取内容

/**       * 从缓存中读取value       */      public String readCache(final String key) {          Future<String> ret = mExecutor.submit(new Callable<String>() {              @Override              public String call() throws Exception {                  String result = null;                  switch (mStrategy) {                      case MEMORY_ONLY:                          result = mMemoryCache.get(key);                          break;                      case MEMORY_FIRST:                          result = mMemoryCache.get(key);                          if (result == null) {                              result = mDiskCache.get(key);                          }                          break;                      case DISK_ONLY:                          result = mDiskCache.get(key);                          break;                  }                  return result;              }          });          try {              return ret.get();          } catch (InterruptedException e) {              e.printStackTrace();          } catch (ExecutionException e) {              e.printStackTrace();          }          return null;      }

(5)将内容写入到缓存中

/**       * 将value 写入到缓存中       */      public void writeCache(final String key, final String value) {          mExecutor.submit(new Runnable() {              @Override              public void run() {                  switch (mStrategy) {                      case MEMORY_FIRST:                          if (!mMemoryCache.hasEvictedListener()) {                              mMemoryCache.setEvictedListener(new MemoryCache.EvictedListener() {                                  @Override                                  public void handleEvictEntry(String evictKey, String evictValue) {                                      mDiskCache.put(evictKey, evictValue);                                  }                              });                          }                          mMemoryCache.put(key, value);                          break;                      case MEMORY_ONLY:                          if (mMemoryCache.hasEvictedListener())                              mMemoryCache.setEvictedListener(null);                          mMemoryCache.put(key, value);                          break;                      case DISK_ONLY:                          mDiskCache.put(key, value);                          break;                  }              }          });      }

到此为止,框架的开发到此完成。希望对有需要的人有所帮助。

四、源码下载

源码下载 : http://download.csdn.net/detail/jczmdeveloper/9348031

GitHub地址:https://github.com/jczmdeveloper/XCCacheManager

真题园网http://www.zhentiyuan.com

原文 http://www.cnblogs.com/JczmDeveloper/p/5039970.html