關於圖片的處理,必不可少要用到三級緩存技術。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信息:
到這裏三級緩存能夠看明白了吧。
這只是三級緩存的簡單實現,實際項目中,有許多優秀的第三方框架已經將緩存機制優化的很是棒了,有興趣的朋友能夠繼續看個人博客,最近沒有工做,想要好好整理一下之前的東西了~再不整理都忘記啦。。。