Android 框架修煉-本身封裝雙緩存管理框架庫

1、概述

  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、源碼下載

源碼下載: www.demodashi.com/demo/12143.html

 真題園網http://www.zhentiyuan.com

相關文章
相關標籤/搜索