自己封裝雙快取管理框架 Android 庫
一、概述
Android開發中,網路請求是很重要的一部分,而快取網路請求來的圖片或者響應結果字串或者結果流,既可以省流量,同時也可以幫助我們解決無網或弱網情況下載入情況,當然也可以提升程式效能效率。縱所周知,快取管理中肯定需要用到記憶體快取,這裡我們採用LruCache來管理記憶體的快取。
LruCahce雖然速度快,但是隻是記憶體級別的快取,為了實現持久化的快取,我們還需要檔案級別的快取,也就是說我們要把快取儲存到檔案,而檔案則是儲存到手機儲存或者SD卡儲存中,即實現Disk級別的快取,這裡我們藉助DiskLruCache這個輔助工具類來實現。顧名思義,這個工具類的作用就是使用Lru演算法來儲存資訊到Disk上。
二、例項效果圖
下面是個簡單的例項演示效果圖
三、快取管理框架的實現解
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
相關文章
- Android DiskLruCache快取工具封裝Android快取封裝
- Android RxJava+Retrofit完美封裝(快取,請求,生命週期管理)AndroidRxJava封裝快取
- 手把手教你封裝自己的圖片快取工具-ImageLoader封裝快取
- 自己動手實現Android中的三級快取框架Android快取框架
- 二次封裝的快取框架wCache(支援快取時間、預設值)--微信小程式封裝快取框架微信小程式
- Android Room封裝成一個類似Redis的快取資料庫的效果AndroidOOM封裝Redis快取資料庫
- 封裝一個自己的js庫封裝JS
- uni-app快取器的封裝APP快取封裝
- 清除快取資料--工具類封裝快取封裝
- 快取淘汰、快取穿透、快取擊穿、快取雪崩、資料庫快取雙寫一致性快取穿透資料庫
- Swift 檔案快取--DDGDataCache_Swift庫高階封裝用法Swift快取封裝
- iOS資料庫離線快取思路和網路層封裝iOS資料庫快取封裝
- Android架構系列-封裝自己的okhttpAndroid架構封裝HTTP
- Android 擼起袖子,自己封裝 DialogFragmentAndroid封裝Fragment
- 【JavaScript框架封裝】公共框架的封裝JavaScript框架封裝
- Java 雙快取Java快取
- Android圖片快取框架GlideAndroid快取框架IDE
- 輕量級Android快取框架ASimpleCacheAndroid快取框架
- Android ImageLoader 框架之圖片快取Android框架快取
- android ListView非同步載入圖片(雙快取)AndroidView非同步快取
- 自己動手寫Android資料庫框架Android資料庫框架
- RxRetrofit – 終極封裝 – 深入淺出 & 資料快取封裝快取
- RxRetrofit - 終極封裝 - 深入淺出 & 資料快取封裝快取
- Android元件化開發實戰:封裝許可權管理請求框架Android元件化封裝框架
- 快取、快取演算法和快取框架簡介快取演算法框架
- android 快取管理及LRU演算法Android快取演算法
- 對Flutter路由管理庫Fluro的封裝Flutter路由封裝
- kicad6 封裝庫的管理封裝
- 快取框架積累快取框架
- SpringBoot快取管理(一) 預設快取管理Spring Boot快取
- Android 封裝AsyncTask操作Sqlite資料庫Android封裝SQLite資料庫
- Android技術積累:圖片快取管理Android快取
- App快取管理APP快取
- 資料庫與快取雙寫一致性資料庫快取
- Fresco的封裝和使用說明以及獲取快取中的Bitmap物件封裝快取物件
- LLFMDB 高度封裝的FMDB 傻瓜式操作一鍵快取封裝快取
- Swift - Alamofire與Cache封裝實現網路快取、下載Swift封裝快取
- Android 記憶體快取框架 LruCache 的原始碼分析Android記憶體快取框架原始碼