安卓图片三级缓存策略与实现
前言:
這里說的三級緩存,分別指的是:內(nèi)存緩存、文件緩存和網(wǎng)絡(luò)這三個(gè)層面。
一般來說,我們首次加載圖片,內(nèi)存和文件是沒有緩存的,這樣我們需要從網(wǎng)絡(luò)加載,加載完成后,我們會(huì)存到內(nèi)存和文件中去;當(dāng)再次加載圖片的時(shí)候,我們會(huì)先查找內(nèi)存有沒有,如果有就直接顯示內(nèi)存中的圖片,如果沒有,我們會(huì)接著查找文件中是否有,如果文件中有,我們會(huì)顯示文件中的圖片,并且把它存到內(nèi)存中去,這樣下次我們在內(nèi)存中就能找到它了。
我們之所以要做緩存,主要是為了提高效率,節(jié)省流量。但是為什么要做三級呢?為什么不只存在內(nèi)存或者只存在文件中呢?這是因?yàn)閮?nèi)存的讀取速度快,但是容易被回收,容量小,文件的讀取速度次之,不過容量大,不到不得已不會(huì)被回收。
有了以上的介紹,我們已經(jīng)知道了三級緩存的必要性和實(shí)施步驟,接下來,我們就要選擇在每級緩存的緩存策略了。
內(nèi)存緩存,最開始大家推崇的是用SoftRefrence(軟引用),它只有在內(nèi)存不夠的情況下才會(huì)被GC回收。但是高版本的安卓系統(tǒng)更傾向于回收SoftRefrence,這使得SoftRefrence不那么好用了。不過,安卓在3.0之后提供了LRUCache,它采用了最近最少使用的淘汰策略。本篇文章我們的內(nèi)存緩存使用的就是LruCache.?
文件緩存,我們使用的是DiskLruCache 點(diǎn)擊這里下載
網(wǎng)絡(luò)請求,這里我們使用Volley網(wǎng)絡(luò)請求框架 點(diǎn)擊這里下載。不懂的可以看這里?Android Volley入門到精通:定制自己的Request
點(diǎn)擊這里下載本文的源代碼
內(nèi)存緩存LruCache
我們定義ImageCacheUtil類來進(jìn)行圖片的緩存,它實(shí)現(xiàn)了Volley的ImageLoader.ImageCache接口,改接口需要實(shí)現(xiàn)兩個(gè)方法: 1.getBitmap : Volley請求的時(shí)候會(huì)先回調(diào)getBitmap看緩存是否有圖片,沒有的話才會(huì)去網(wǎng)絡(luò)請求 2.putBitmap : Volley下載完圖片的回調(diào),實(shí)現(xiàn)該方法可以進(jìn)行圖片緩存使用LruCache需要以下步驟 1.通過new LruCache得到LruCache的實(shí)例 // 獲取應(yīng)用可占內(nèi)存的1/8作為緩存int maxSize = (int) (Runtime.getRuntime().maxMemory() / 8);// 實(shí)例化LruCaceh對象mLruCache = new LruCache<String, Bitmap>(maxSize) {@Overrideprotected int sizeOf(String key, Bitmap bitmap) {return bitmap.getRowBytes() * bitmap.getHeight();}}; 2.在getBitmap函數(shù)中通過mLruCache.get(url)得到內(nèi)存的圖片,沒有時(shí)返回空。 3.在putBitmap函數(shù)中通過mLruCache.put(url,bitmap)把圖片存入內(nèi)存。 具體用法可以看下面我貼出的代碼。
文件緩存DiskLruCache
使用DiskLruCache需要以下步驟: 1.通過DiskLruCache.open(...)得到DiskLruCache的實(shí)例 //DiskLruCache實(shí)例,它的構(gòu)造方法是私有的,所以我們需要通過它提供的open方法來生成。try {mDiskLruCache = DiskLruCache.open(getDiskCacheDir(MyApplication.getContext(),CACHE_FOLDER_NAME),getAppVersion(MyApplication.getContext()) , 1, DISKMAXSIZE);} catch (IOException e) {e.printStackTrace();} 2.在getBitmap函數(shù)中如果mLruCache.get(url)返回空,通過mDiskLruCache.get(key)得到DiskLruCache.Snapshot,通過BitmapFratory(snapshot.getInputStream(0))得到圖片,沒有時(shí)返回空 String diskKey = MD5Utils.md5(s);try {if(mDiskLruCache.get(diskKey) != null){ //文件中有//從文件中取Log.d(TAG,"從文件中取");DiskLruCache.Snapshot snapshot = mDiskLruCache.get(diskKey);Bitmap bitmap = null;if(snapshot != null){bitmap = BitmapFactory.decodeStream(snapshot.getInputStream(0));//存入內(nèi)存mLruCache.put(s,bitmap);}return bitmap;}} catch (IOException e) {e.printStackTrace();} 3.在putBitmap函數(shù)中如果mDiskLruCache.get(key)==null則把圖片存入文件。 //存入文件String diskKey = MD5Utils.md5(s);try {if(mDiskLruCache.get(diskKey) == null){Log.d(TAG,"存入文件");DiskLruCache.Editor editor = mDiskLruCache.edit(diskKey);if(editor != null){OutputStream outputStream = editor.newOutputStream(0);if(bitmap.compress(Bitmap.CompressFormat.JPEG,100,outputStream)){editor.commit();}else{editor.abort();}}mDiskLruCache.flush();}} catch (IOException e) {e.printStackTrace();}下面是這個(gè)ImageCacheUtil類的全部
public class ImageCacheUtil implements ImageLoader.ImageCache {//緩存類private static LruCache<String, Bitmap> mLruCache;private static DiskLruCache mDiskLruCache;//磁盤緩存大小private static final int DISKMAXSIZE = 10 * 1024 * 1024;//路徑private static String CACHE_FOLDER_NAME = "YR_ImageCache";private String TAG = ImageCacheUtil.class.getSimpleName();public ImageCacheUtil() {// 獲取應(yīng)用可占內(nèi)存的1/8作為緩存int maxSize = (int) (Runtime.getRuntime().maxMemory() / 8);// 實(shí)例化LruCaceh對象mLruCache = new LruCache<String, Bitmap>(maxSize) {@Overrideprotected int sizeOf(String key, Bitmap bitmap) {return bitmap.getRowBytes() * bitmap.getHeight();}};//DiskLruCache實(shí)例,它的構(gòu)造方法是私有的,所以我們需要通過它提供的open方法來生成。try {mDiskLruCache = DiskLruCache.open(getDiskCacheDir(MyApplication.getContext(),CACHE_FOLDER_NAME),getAppVersion(MyApplication.getContext()) , 1, DISKMAXSIZE);} catch (IOException e) {e.printStackTrace();}}/*** volley請求的時(shí)候會(huì)先回調(diào)getBitmap查看緩存中是否有圖片,沒有再去請求* @param s* @return*/@Overridepublic Bitmap getBitmap(String s) {if(mLruCache.get(s) != null){ //內(nèi)存中有//從內(nèi)存獲取Log.d(TAG,"從內(nèi)存獲取");return mLruCache.get(s);}else {String diskKey = MD5Utils.md5(s);try {if(mDiskLruCache.get(diskKey) != null){ //文件中有//從文件中取Log.d(TAG,"從文件中取");DiskLruCache.Snapshot snapshot = mDiskLruCache.get(diskKey);Bitmap bitmap = null;if(snapshot != null){bitmap = BitmapFactory.decodeStream(snapshot.getInputStream(0));//存入內(nèi)存mLruCache.put(s,bitmap);}return bitmap;}} catch (IOException e) {e.printStackTrace();}}Log.d(TAG,"從網(wǎng)絡(luò)中取");return null;}/*** 當(dāng)Volley下載完圖片后會(huì)來回調(diào)putBitmap方法來將圖片進(jìn)行緩存* @param s* @param bitmap*/@Overridepublic void putBitmap(String s, Bitmap bitmap) {//存入內(nèi)存Log.d(TAG,"存入內(nèi)存");mLruCache.put(s,bitmap);//存入文件String diskKey = MD5Utils.md5(s);try {if(mDiskLruCache.get(diskKey) == null){Log.d(TAG,"存入文件");DiskLruCache.Editor editor = mDiskLruCache.edit(diskKey);if(editor != null){OutputStream outputStream = editor.newOutputStream(0);if(bitmap.compress(Bitmap.CompressFormat.JPEG,100,outputStream)){editor.commit();}else{editor.abort();}}mDiskLruCache.flush();}} catch (IOException e) {e.printStackTrace();}}//該方法會(huì)判斷當(dāng)前sd卡是否存在,然后選擇緩存地址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();}Log.d(TAG,cachePath + File.separator + uniqueName);return new File(cachePath + File.separator + uniqueName);}//獲得應(yīng)用version號碼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;} }Volley下載圖片
不知道Volley可以看這篇博客:?Android Volley入門到精通:定制自己的Request 關(guān)于Volley進(jìn)行圖片下載的具體可以看這篇博客:?Android Volley入門到精通:使用Volley加載網(wǎng)絡(luò)圖片
Volley需要我們聲明一個(gè)RequestQueue來維持請求隊(duì)列,我們定義RequestQueueManager來進(jìn)行管理。
public class RequestQueueManager {public static RequestQueue mRequestQueue = Volley.newRequestQueue(MyApplication.getContext());public static void addRequest(Request<?> request, Object object){if (object != null){request.setTag(object);}mRequestQueue.add(request);}public static void cancelAll(Object tag) {mRequestQueue.cancelAll(tag);} }Volley給我們提供了ImageLoader類和ImageCache類用于圖片下載。我們可以用ImageLoader的get(url,ImageLoader.ImageListeer,width,height)方法來下載圖片 ImageLoader的使用步驟如下: 1.用new ImageLoader方法得到ImageLoader的一個(gè)實(shí)例。其中構(gòu)造方法需要傳入requestQueue和ImageCache(我們在介紹內(nèi)存的時(shí)候的ImageCaheUtil類實(shí)現(xiàn)了ImageCache) private static ImageCacheUtil mImagetCache = new ImageCacheUtil();public static ImageLoader mImageLoader = new ImageLoader(RequestQueueManager.mRequestQueue,mImagetCache);2.用get方法進(jìn)行圖片下載 public static void loadImage(String url,ImageLoader.ImageListener imageListener){mImageLoader.get(url,imageListener,0,0);}public static void loadImage(String url,ImageLoader.ImageListener imageListener,int maxWidth,int maxHeight){mImageLoader.get(url,imageListener,maxWidth,maxHeight);}3.在Activity中調(diào)用 ImageCacheManager.loadImage("http://img0.bdstatic.com/img/image/shouye/xiaoxiao/%E5%AE%A0%E7%89%A983.jpg",new ImageLoader.ImageListener() {@Overridepublic void onResponse(ImageLoader.ImageContainer imageContainer, boolean b) {progressLy.setVisibility(View.GONE);if(imageContainer.getBitmap() != null){imageView.setImageBitmap(imageContainer.getBitmap());}}@Overridepublic void onErrorResponse(VolleyError volleyError) {progressLy.setVisibility(View.GONE);}});好了,以上就是安卓三級緩存的實(shí)現(xiàn)。下載源碼點(diǎn)擊這里
總結(jié)
以上是生活随笔為你收集整理的安卓图片三级缓存策略与实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 1. Pandas 导入导出数据
- 下一篇: Android 集成Chrome 浏览器