Android開發中,網絡請求是很重要的一部分,而緩存網絡請求來的圖片或者響應結果字符串或者結果流,既能夠省流量,同時也能夠幫助咱們html
解決無網或弱網狀況下加載狀況,固然也能夠提高程序性能效率。縱所周知,緩存管理中確定須要用到內存緩存,這裏咱們採用LruCache來管理內存的緩存。算法
LruCahce雖然速度快,可是隻是內存級別的緩存,爲了實現持久化的緩存,咱們還須要文件級別的緩存,也就是說咱們要把緩存保存到文件,而文件則是保存緩存
到手機存儲或者SD卡存儲中,即實現Disk級別的緩存,這裏咱們藉助DiskLruCache這個輔助工具類來實現。顧名思義,這個工具類的做用就是使用Lru算法網絡
來存儲信息到Disk上。多線程
2、實例效果圖併發
下面是個簡單的實例演示效果圖app
3、緩存管理框架的實現解框架
一、內存緩存類的實現ide
該類主要實現內存級別的緩存管理類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); }
二、文件級別的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; } }
三、搭建封裝雙緩存管理框架類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)緩存策略
這裏咱們定義了緩存策略,便於適應各類不一樣業務需求,能夠靈活使用不一樣的策略
public void setStrategy(XCCacheManager.Strategy strategy) {
this.mStrategy = strategy;
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);
}
});
}
break;
case MEMORY_ONLY:
if (mMemoryCache.hasEvictedListener())
mMemoryCache.setEvictedListener(null);
break;
case DISK_ONLY:
break;
}
}
默認採用內存優先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:
mMemoryCache.put(key, value);
mDiskCache.put(key,value);
break;
case MEMORY_ONLY:
mMemoryCache.put(key, value);
break;
case DISK_ONLY:
mDiskCache.put(key, value);
break;
}
}
});
}
到此爲止,框架的開發到此完成。但願對有須要的人有所幫助。
4、源碼下載