Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 圖片三級緩存加載框架原理解析與代碼實現

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 taskMap;
    private static NGDownloadImage instance; //自身私有化實例
    private int POOL_SIZE = 5;//線程池自定義大小

    private NGDownloadImage() {
        final int cpuNums = Runtime.getRuntime().availableProcessors();//cpu數
        executorService = 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 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 {
        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(MAX_CACHE_CAPACITY, 0.75f, true) {
            //移除hashmap中最老的鍵值
            @Override
            protected boolean removeEldestEntry(LinkedHashMap.Entry eldest) {
                if (size() > MAX_CACHE_CAPACITY) {
                    mSoftBitmapCache.put(eldest.getKey(), new SoftReference(eldest.getValue()));
                    return true; //返回true則移除最老的鍵值
                } else {
                    return false;
                }
            }
        };
    }
類似於LRU算法實現?(逃~)

 

其他增刪進入內存方法就不貼上來了~

外存緩存實現:

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

 

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

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

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved