Android 使用三级缓存实现对图片的加载
一、概述:
現(xiàn)在android應(yīng)用中不可避免的要使用圖片,有些圖片是可以變化的,需要每次啟動時(shí)從網(wǎng)絡(luò)拉取,這種場景在有廣告位的應(yīng)用以及純圖片應(yīng)用(比如百度美拍)中比較多。
現(xiàn)在有一個(gè)問題:假如每次啟動的時(shí)候都從網(wǎng)絡(luò)拉取圖片的話,勢必會消耗很多流量。在當(dāng)前的狀況下,對于非wifi用戶來說,流量還是很貴的,一個(gè)很耗流量的應(yīng)用,其用戶數(shù)量級肯定要受到影響。當(dāng)然,我想,向百度美拍這樣的應(yīng)用,必然也有其內(nèi)部的圖片緩存策略。總之,圖片緩存是很重要而且是必須的。
二、實(shí)現(xiàn)原理:
實(shí)現(xiàn)圖片緩存也不難,需要有相應(yīng)的cache策略。這里我采用 內(nèi)存-文件-網(wǎng)絡(luò) 三層cache機(jī)制,其中內(nèi)存緩存包括強(qiáng)引用緩存和軟引用緩存(SoftReference),其實(shí)網(wǎng)絡(luò)不算cache,這里姑且也把它劃到緩存的層次結(jié)構(gòu)中。當(dāng)根據(jù)url向網(wǎng)絡(luò)拉取圖片的時(shí)候,先從內(nèi)存中找,如果內(nèi)存中沒有,再從緩存文件中查找,如果緩存文件中也沒有,再從網(wǎng)絡(luò)上通過http請求拉取圖片。在鍵值對(key-value)中,這個(gè)圖片緩存的key是圖片url的hash值,value就是bitmap。所以,按照這個(gè)邏輯,只要一個(gè)url被下載過,其圖片就被緩存起來了。
但這里不使用SoftReference,而使用LruCache進(jìn)行圖片的緩存
為什么使用LruCache:
這個(gè)類非常適合用來緩存圖片,它的主要算法原理是把最近使用的對象用強(qiáng)引用存儲在 LinkedHashMap 中,并且把最近最少使用的對象在緩存值達(dá)到預(yù)設(shè)定值之前從內(nèi)存中移除。
在過去,我們經(jīng)常會使用一種非常流行的內(nèi)存緩存技術(shù)的實(shí)現(xiàn),即軟引用或弱引用 (SoftReference or WeakReference)。但是現(xiàn)在已經(jīng)不再推薦使用這種方式了,因?yàn)閺?Android 2.3 (API Level 9)開始,垃圾回收器會更傾向于回收持有軟引用或弱引用的對象,這讓軟引用和弱引用變得不再可靠。另外,Android 3.0 (API Level 11)中,圖片的數(shù)據(jù)會存儲在本地的內(nèi)存當(dāng)中,因而無法用一種可預(yù)見的方式將其釋放,這就有潛在的風(fēng)險(xiǎn)造成應(yīng)用程序的內(nèi)存溢出并崩潰。
三、具體實(shí)現(xiàn):
1)在構(gòu)造方法里初始化LruCache mCache
if (mCache == null) {// 最大使用的內(nèi)存空間int maxSize = (int) (Runtime.getRuntime().freeMemory() / 4);mCache = new LruCache<String, Bitmap>(maxSize) {@Overrideprotected int sizeOf(String key, Bitmap value) {return value.getRowBytes() * value.getHeight();}};}2)去內(nèi)存中取
Bitmap bitmap = mCache.get(url);if (bitmap != null) {// 直接顯示iv.setImageBitmap(bitmap);return;}3)去硬盤上取
bitmap = loadBitmapFromLocal(url);if (bitmap != null) {// 直接顯示iv.setImageBitmap(bitmap);return;}4)從網(wǎng)絡(luò)加載
loadBitmapFromNet(iv, url);四、詳細(xì)代碼:
/*** @項(xiàng)目名: 3G緩存加載圖片* @包名: com.android.news.tools* @類名: ImageHelper* @創(chuàng)建者: chen.lin* @創(chuàng)建時(shí)間: 2015-4-27 上午10:50:37* */ public class ImageHelper {// 內(nèi)存緩存池// private Map<String, SoftReference<Bitmap>> mCache = new// LinkedHashMap<String, SoftReference<Bitmap>>();// LRUCahce 池子private static LruCache<String, Bitmap> mCache;private static Handler mHandler;private static ExecutorService mThreadPool;private static Map<ImageView, Future<?>> mTaskTags = new LinkedHashMap<ImageView, Future<?>>();private Context mContext;public ImageHelper(Context context) {this.mContext = context;if (mCache == null) {// 最大使用的內(nèi)存空間int maxSize = (int) (Runtime.getRuntime().freeMemory() / 4);mCache = new LruCache<String, Bitmap>(maxSize) {@Overrideprotected int sizeOf(String key, Bitmap value) {return value.getRowBytes() * value.getHeight();}};}if (mHandler == null) {mHandler = new Handler();}if (mThreadPool == null) {// 最多同時(shí)允許的線程數(shù)為3個(gè)mThreadPool = Executors.newFixedThreadPool(3);}}public void display(ImageView iv, String url) {// 1.去內(nèi)存中取Bitmap bitmap = mCache.get(url);if (bitmap != null) {// 直接顯示iv.setImageBitmap(bitmap);return;}// 2.去硬盤上取bitmap = loadBitmapFromLocal(url);if (bitmap != null) {// 直接顯示iv.setImageBitmap(bitmap);return;}// 3. 去網(wǎng)絡(luò)獲取圖片loadBitmapFromNet(iv, url);}private void loadBitmapFromNet(ImageView iv, String url) {// 開線程去網(wǎng)絡(luò)獲取// 使用線程池管理// new Thread(new ImageLoadTask(iv, url)).start();// 判斷是否有線程在為 imageView加載數(shù)據(jù)Future<?> futrue = mTaskTags.get(iv);if (futrue != null && !futrue.isCancelled() && !futrue.isDone()) {System.out.println("取消 任務(wù)");// 線程正在執(zhí)行futrue.cancel(true);futrue = null;}// mThreadPool.execute(new ImageLoadTask(iv, url));futrue = mThreadPool.submit(new ImageLoadTask(iv, url));// Future 和 callback/Runable// 返回值,持有正在執(zhí)行的線程// 保存mTaskTags.put(iv, futrue);System.out.println("標(biāo)記 任務(wù)");}class ImageLoadTask implements Runnable {private String mUrl;private ImageView iv;public ImageLoadTask(ImageView iv, String url) {this.mUrl = url;this.iv = iv;}@Overridepublic void run() {// HttpUrlconnectiontry {// 獲取連接HttpURLConnection conn = (HttpURLConnection) new URL(mUrl).openConnection();conn.setConnectTimeout(30 * 1000);// 設(shè)置連接服務(wù)器超時(shí)時(shí)間conn.setReadTimeout(30 * 1000);// 設(shè)置讀取響應(yīng)超時(shí)時(shí)間// 連接網(wǎng)絡(luò)conn.connect();// 獲取響應(yīng)碼int code = conn.getResponseCode();if (200 == code) {InputStream is = conn.getInputStream();// 將流轉(zhuǎn)換為bitmapBitmap bitmap = BitmapFactory.decodeStream(is);// 存儲到本地write2Local(mUrl, bitmap);// 存儲到內(nèi)存mCache.put(mUrl, bitmap);// 圖片顯示:不可取// iv.setImageBitmap(bitmap);mHandler.post(new Runnable() {@Overridepublic void run() {// iv.setImageBitmap(bitmap);display(iv, mUrl);}});}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}/*** 本地種去去圖片* * @param url*/private Bitmap loadBitmapFromLocal(String url) {// 去找文件,將文件轉(zhuǎn)換為bitmapString name;try {name = MD5Encoder.encode(url);File file = new File(getCacheDir(), name);if (file.exists()) {Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());// 存儲到內(nèi)存mCache.put(url, bitmap);return bitmap;}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}return null;}private void write2Local(String url, Bitmap bitmap) {String name;FileOutputStream fos = null;try {name = MD5Encoder.encode(url);File file = new File(getCacheDir(), name);fos = new FileOutputStream(file);// 將圖像寫到流中bitmap.compress(CompressFormat.JPEG, 100, fos);} catch (Exception e) {e.printStackTrace();} finally {if (fos != null) {try {fos.close();fos = null;} catch (IOException e) {e.printStackTrace();}}}}private String getCacheDir() {String state = Environment.getExternalStorageState();File dir = null;if (Environment.MEDIA_MOUNTED.equals(state)) {// 有sd卡dir = new File(Environment.getExternalStorageDirectory(), "/Android/data/" + mContext.getPackageName()+ "/icon");} else {// 沒有sd卡dir = new File(mContext.getCacheDir(), "/icon");}if (!dir.exists()) {dir.mkdirs();}return dir.getAbsolutePath();} }五、使用方法:
在Adapter 的getView方法里
ImageView iv = (contentView)findViewById(R.id.iv); String url = "http://localhost:8080/web/1.jpg"; new IamgeHelper(this).display(iv,url);———————————————————————
有需求者請加qq:136137465,非誠勿擾
(java 架構(gòu)師全套教程,共760G, 讓你從零到架構(gòu)師,每月輕松拿3萬)
01.高級架構(gòu)師四十二個(gè)階段高
02.Java高級系統(tǒng)培訓(xùn)架構(gòu)課程148課時(shí)
03.Java高級互聯(lián)網(wǎng)架構(gòu)師課程
04.Java互聯(lián)網(wǎng)架構(gòu)Netty、Nio、Mina等-視頻教程
05.Java高級架構(gòu)設(shè)計(jì)2016整理-視頻教程
06.架構(gòu)師基礎(chǔ)、高級片
07.Java架構(gòu)師必修linux運(yùn)維系列課程
08.Java高級系統(tǒng)培訓(xùn)架構(gòu)課程116課時(shí)
(送:hadoop系列教程,java設(shè)計(jì)模式與數(shù)據(jù)結(jié)構(gòu), Spring Cloud微服務(wù), SpringBoot入門)
——————————————————————–
總結(jié)
以上是生活随笔為你收集整理的Android 使用三级缓存实现对图片的加载的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 〖全域运营实战白宝书 - 运营角色认知篇
- 下一篇: SRA转fastq 软件下载