關於三級緩存技術

 

關於圖片的處理,必不可少要用到三級緩存技術。java

 

什麼是三級緩存?android

三級緩存通常分爲內存--文件--網絡三級緩存  緩存

內存(手機內存):內存相對於磁盤緩存,速度會快不少,可是缺點就是容量較小,不能存儲大容量數據,且容易被系統回收。LruCache服務器

磁盤(SD卡):相對於內存來講存儲空間很大,可以存儲較多的數據。DiskLruCache(DiskLruCache是非Google官方編寫,但得到官方認證的硬盤緩存類,該類沒有限定在Android內,因此理論上Java應用也可使用DiskLreCache來緩存。)網絡

網絡:服務器端,經過HTTP請求得到。app

具體流程就是,同一張圖片從網絡獲取一次,而後在本地緩存下來,以後加載同一張圖片的時候就從緩存中去加載。
先在內存中查找,找到就進行加載,不然去磁盤查找,找到將圖片添加到內存中,加載顯示,不然從網絡加載,而且緩存到內存和磁盤,並返回。這就是完整的三級緩存過程。框架

 

這裏須要介紹的是內存緩存一共分爲四類:強引用、軟引用、弱引用和虛引用(後面我會再專門整理一篇博客的~)異步

 

到這裏三級緩存應該大概明白了吧,下面是我寫的實現demoide

package simida.yz.com.imagecache.utils;

import android.app.Activity;
import android.app.DownloadManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.support.v4.util.LruCache;
import android.util.Log;
import android.widget.ImageView;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

/**
 * Created by Administrator on 2016/12/2.
 */
public class LoadImageCache {
    private Context mContext;
    //網絡地址
    private String mImageUrl;
    //磁盤存儲文件名
    private String mFileName;
    //內存緩存
    private LruCache<String, Bitmap> mLruCache;
    //磁盤目錄(sd卡的私有cache目錄)
    private String mFilePath;


    public LoadImageCache(Context context, String url) {
        this.mContext = context;
        this.mImageUrl = url;
        initLruCache();
        initFileCache();
    }


    public void loadBitmap(final ImageView imageView) {
        //如今內存中查找
        Bitmap bitmap = mLruCache.get(mImageUrl);
        if (bitmap != null) {
            Log.i("tag", "thumbnail from LruCache ");
            imageView.setImageBitmap(bitmap);
            return;
        } else {
            //去磁盤查找
            byte[] bytes = loadFromSD();
            if (bytes != null) {
                //保存縮略圖到cache
                saveThumbnailToLruCache(bytes, 500, 300);
                bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
                Log.i("tag", "bitmap from sdcard ");
                imageView.setImageBitmap(bitmap);
                return;
            } else {

                //網絡加載 使用OKHTTP異步實現
                OkHttpClient client = new OkHttpClient();
                Request request = new Request.Builder().url(mImageUrl).build();
                Call call = client.newCall(request);
                call.enqueue(new Callback() {
                    @Override
                    public void onFailure(Call call, IOException e) {

                    }

                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
                        byte[] fromNetWork = response.body().bytes();
                        //保存縮略圖到cache
                        saveThumbnailToLruCache(fromNetWork, 500, 300);
                        //保存到sdcard
                        saveImageToSD(fromNetWork);
                        final Bitmap bitmap1 = BitmapFactory.decodeByteArray(fromNetWork, 0, fromNetWork.length);
                        Log.i("tag", "bitmap from network ");
                        ((Activity)mContext).runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                imageView.setImageBitmap(bitmap1);
                            }
                        });

                    }
                });
            }

        }
    }


    //初始化磁盤緩存目錄
    private void initFileCache() {
        //獲取文件名稱
        mFileName = mImageUrl.substring(mImageUrl.lastIndexOf("/") + 1);
        //判斷sd卡是否掛載
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            //私有緩存目錄
            mFilePath = mContext.getExternalCacheDir().getAbsolutePath();
        } else {
            Log.i("tag", "sdcard is error! ");
        }

    }


    //初始化內存緩存
    private void initLruCache() {

        if (mLruCache == null) {
            //獲取運行時內存總大小
            long maxMemory = Runtime.getRuntime().maxMemory();
            //通常設置圖片緩存爲手機內存的1/8
            mLruCache = new LruCache<String, Bitmap>((int) (maxMemory / 8)) {
                //用來衡量每張圖片的大小,默認返回圖片的數量
                @Override
                protected int sizeOf(String key, Bitmap value) {
                    //圖片默認是ARGB_8888格式,每一個像素佔4個字節
//                    int values = value.getWidth() * value.getHeight() * 4;
                    int values = value.getRowBytes() * value.getHeight();
                    return values;
                }

                //結合軟引用使用時,會配合這個方法,如今已經基本不用軟引用,不用考慮
                @Override
                protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
                    super.entryRemoved(evicted, key, oldValue, newValue);
                }
            };
        }


    }

    //保存圖片到磁盤
    private void saveImageToSD(byte[] image) {
        BufferedOutputStream bos = null;
        try {
            bos = new BufferedOutputStream(new FileOutputStream(new File(mFilePath, mFileName)));
            bos.write(image, 0, image.length);
            bos.flush();
            bos.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //關流
            try {
                if (bos != null) {
                    bos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        Log.i("tag", "bitmap to sdcard ");

    }

    //磁盤查找
    private byte[] loadFromSD() {
        FileInputStream fis = null;
        BufferedInputStream bis = null;
        //內存流
        ByteArrayOutputStream baos = null;
        try {
            fis = new FileInputStream(new File(mFilePath, mFileName));
            bis = new BufferedInputStream(fis);
            baos = new ByteArrayOutputStream();

            byte[] bys = new byte[1024 * 8];
            int length = 0;
            while ((length = bis.read(bys)) != -1) {
                baos.write(bys, 0, length);
                baos.flush();
            }
            byte[] bytes = baos.toByteArray();
            return bytes;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return null;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            //關流
            try {
                if (baos != null) {
                    baos.close();
                }
                if (bis != null) {
                    bis.close();
                }
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            Log.i("tag", "bitmap from sdcard ");
        }
    }

    //保存圖片的縮略圖到內存緩存,(二次採樣技術)
    private void saveThumbnailToLruCache(byte[] image, int thumbWidth, int thumbHeight) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        //只採圖片的邊界
        options.inJustDecodeBounds = true;
        //獲取原圖的高度和寬度
        BitmapFactory.decodeByteArray(image, 0, image.length, options);
        int width = options.outWidth;
        int height = options.outHeight;

        //計算縮略圖寬高與原圖寬高比例,取較大值做爲最終縮放比例
        int size0 = (int) (thumbWidth / (float) width);
        int size1 = (int) (thumbHeight / (float) height);
        int size = size0 > size1 ? size0 : size1;


        options.inSampleSize = size;
        //設置圖片格式
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        //採圖片所有
        options.inJustDecodeBounds = false;

        Bitmap bitmap = BitmapFactory.decodeByteArray(image, 0, image.length, options);
        mLruCache.put(mImageUrl, bitmap);
        Log.i("tag", "thumbnail to cache ");
    }

}

  

Activity和佈局文件都比較簡單,佈局中只有一個imageView,實例化上面個工具類,調用loadBitmap方法便可,這裏就不貼出來了。工具

 

第一次打開時log信息:

關閉後打開log信息:

到這裏三級緩存能夠看明白了吧。

這只是三級緩存的簡單實現,實際項目中,有許多優秀的第三方框架已經將緩存機制優化的很是棒了,有興趣的朋友能夠繼續看個人博客,最近沒有工做,想要好好整理一下之前的東西了~再不整理都忘記啦。。。

相關文章
相關標籤/搜索