1. 程式人生 > >Android 圖片三級快取載入框架原理解析與程式碼實現

Android 圖片三級快取載入框架原理解析與程式碼實現

本文主要介紹三級快取的原理解析與實現方式。以前一直覺得三級快取圖片載入是一個很難理解的東西,但是自己看了一下午再試著寫了一遍之後感覺還是隻要沉下心思考還時很容易熟悉掌握的。

所謂三級快取:首先是記憶體-檔案(外存)-網路三級快取機制。


首先:

框架需要一個接入方法NGImageloadHelper.java:

/**
 * 圖片載入框架使用幫助類
* Created by nangua on 2016/7/8.
 */
public class NGImageloadHelper {
    /**
     * 處理圖片
* @param view
* @param url
*/
public static void 
displayImage(ImageView view, String url) { NGDownloadImage.getInstance().addTask(url, view); NGDownloadImage.getInstance().doTask(); } }
然後,使用具體的快取實現類NGDownloadImage:

首先判斷傳入的url對應圖片是否在內外存中,如果不在,則新增進執行緒池的自定義任務佇列中,這裡傳入的任務是自定義的實現Callble介面的任務TaskWithResult,在帶回調引數的執行方法call中執行一個自定義的handler----TaskHandler,該TaskHandler的handlerMessage方法內部根據傳入的圖片型別判斷,執行相應的下載方法(通過HttpUrlConnection實現)並移除taskmap中對應的圖片任務。

/**
 * 圖片載入類
* Created by nangua on 2016/7/8.
 */
public class NGDownloadImage {
    private ExecutorService executorService; //執行緒池服務
private NGImageMemoryCache imageMemoryCache;
    private NGImageFileCache imageFileCache;
    private NGDownloadImageMode downloadImageMode; //圖片例項
private Map<String, View> taskMap
; private static NGDownloadImage instance; //自身私有化例項 private int POOL_SIZE = 5;//執行緒池自定義大小 private NGDownloadImage() { final int cpuNums = Runtime.getRuntime().availableProcessors();//cpuexecutorService = Executors.newFixedThreadPool(cpuNums * POOL_SIZE); imageMemoryCache = new NGImageMemoryCache(); imageFileCache = new NGImageFileCache(); downloadImageMode = new NGDownloadImageMode(); taskMap = new HashMap<>(); } //獲得唯一例項 public static synchronized NGDownloadImage getInstance() { if (instance == null) { instance = new NGDownloadImage(); } return instance; } /** * 新增任務 * * @param url * @param img */ public void addTask(String url, ImageView img) { addTask(null, url, img, null); } public void addTask(Object parent, String url, View img, NGImageCallback callback) { if (img == null) { return; } if (TextUtils.isEmpty(url)) { return; } if (callback != null) { downloadImageMode = new NGDownloadImageMode(); downloadImageMode.setCallback(callback); downloadImageMode.setParent(parent); downloadImageMode.setImgUrl(url); img.setTag(downloadImageMode); } else { img.setTag(url); } //生成Bitmap final Bitmap bitmap = imageMemoryCache.getBitmapFromCache(url); //如果快取裡有 if (bitmap != null) { //如果有實現的回撥介面,則用回撥介面載入圖片 if (callback != null) { callback.imageLoaded(parent, img, bitmap, downloadImageMode); } else { //如果沒有,則直接設定該圖片為bitmap if (img instanceof ImageView) ((ImageView) img).setImageBitmap(bitmap); } } else { //如果快取沒有這個圖片 if (taskMap != null) { //新增到任務集合裡去 synchronized (taskMap) { final String mapKey = Integer.toString(img.hashCode()); if (!taskMap.containsKey(mapKey)) { taskMap.put(mapKey, img); } } } } } public void doTask() { if (taskMap == null) { return; } else { synchronized (taskMap) { Collection<View> collection = taskMap.values(); for (View view : collection) { if (view != null) { Object object = view.getTag(); String url = ""; if (object instanceof NGDownloadImageMode) { url = ((NGDownloadImageMode) object).getImgUrl(); } else { url = (String) object; } if (!TextUtils.isEmpty(url)) { loadImage(url, view); } } } } } } private void loadImage(final String url, final View img) { loadImage(url, img, null); } private void loadImage(final String url, final View img, NGImageCallback callback) { executorService.submit(new TaskWithResult(new TaskHandler(url, img, callback), url)); } private class TaskWithResult implements Callable<String> { private String url; private Handler handler; public TaskWithResult(Handler handler, String url) { this.url = url; this.handler = handler; } @Override public String call() throws Exception { // TODO Auto-generated method stub final Message message = handler.obtainMessage(0, getBitmap(url)); handler.sendMessage(message); return url; } } private class TaskHandler extends Handler { private String url; private View img; private NGImageCallback callback; public TaskHandler(String url, View img, NGImageCallback callback) { this.url = url; this.img = img; this.callback = callback; } @Override public void handleMessage(Message msg) { final Object object = img.getTag(); if (object instanceof NGDownloadImageMode) { final NGDownloadImageMode imageMode = (NGDownloadImageMode) object; imageMode.getCallback().imageLoaded(imageMode.getParent(), img, (Bitmap) msg.obj, imageMode); if (taskMap != null) { taskMap.remove(Integer.toString(img.hashCode())); } } else if (object instanceof String) { if (callback != null) { callback.imageLoaded(null, img, (Bitmap) msg.obj, url); } else { if (object.equals(url) && msg.obj != null) { final Bitmap bitmap = (Bitmap) msg.obj; if (bitmap != null) { if (img instanceof ImageView) { ((ImageView) img).setImageBitmap(bitmap); } } } } if (taskMap != null) { taskMap.remove(Integer.toString(img.hashCode())); } } } } /** * @param url * @return Bitmap */ public Bitmap getBitmap(String url) { Bitmap bitmap = imageMemoryCache.getBitmapFromCache(url); if (bitmap == null) { bitmap = imageFileCache.getImage(url); if (bitmap == null) { bitmap = getBitmapFromUrl(url); if (bitmap != null) { imageMemoryCache.addBitmapToCache(url, bitmap); imageFileCache.saveBmpToSd(url,bitmap); } } else { imageMemoryCache.addBitmapToCache(url, bitmap); } } return bitmap; } public static Bitmap getBitmapFromUrl(String path) { try { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5000); conn.setRequestMethod("GET"); if (conn.getResponseCode() == 200) { InputStream inputStream = conn.getInputStream(); Bitmap bitmap = BitmapFactory.decodeStream(inputStream); return bitmap; } } catch (Exception e) { e.printStackTrace(); } return null; } public interface NGImageCallback { public void imageLoaded(Object parent, View img, Bitmap imageBitmap, NGDownloadImageMode callBackTag); public void imageLoaded(Object parent, View img, Bitmap imageBitmap, String imageUrl); } }
記憶體快取實現:

通過強&軟引用配合使用實現記憶體快取機制,強引用使用HashMap實現,軟引用使用執行緒安全的 ConcurrentHashMap實現(實現原理是鎖分離技術,使用多個鎖來控制對hash表的不同部分的修改,內部使用段(Segment)來表示這些不同的部分,每個段是一個小的hashtable,可併發執行),淘汰演算法如下:

    /**
     * 初始化
     * 淘汰最老的鍵
     */
    protected NGImageMemoryCache() {
        //使用LinkedHashMap保證有序讀取
        hashMap = new LinkedHashMap<String, Bitmap>(MAX_CACHE_CAPACITY, 0.75f, true) {
            //移除hashmap中最老的鍵值
            @Override
            protected boolean removeEldestEntry(LinkedHashMap.Entry<String, Bitmap> eldest) {
                if (size() > MAX_CACHE_CAPACITY) {
                    mSoftBitmapCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue()));
                    return true; //返回true則移除最老的鍵值
                } else {
                    return false;
                }
            }
        };
    }
類似於LRU演算法實現?(逃~)

其他增刪進入記憶體方法就不貼上來了~

外存快取實現:

很簡單這裡就只講一下思路了,把圖片檔案儲存到本地指定資料夾中,注意進行剩餘容量判斷及時清除最老的圖片就行了。

*********************************************************************分隔線************************************************************************

近段時間換了一家公司實習,不過創業公司也有創業公司の可怕啊= =沒有下班の概念。。。希望能儘快參與到新專案開發,學到更多的東西。