Android圖片加載的緩存類
本文為大家分享了Android圖片加載的緩存類,供大家參考,具體內(nèi)容如下
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.SoftReference; import java.net.HttpURLConnection; import java.net.URL; import java.util.LinkedHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Build; import android.os.Handler; import android.text.TextUtils; /** * 圖片加載器,主要功能是從網(wǎng)絡(luò)中下載圖片并緩存。這里之所以另寫一個(gè)功能類似重復(fù)的原因是 之前舊的圖片加載邏輯感覺(jué)非常復(fù)雜,我這里寫個(gè)輕量級(jí)的 * * @author H3c * */ public class ImageLoaderEngine { public static final int LOAD_IMG_SUCCESS = 2010; private final int MAX_CAPACITY = Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1 ? 50 : 10;// 一級(jí)緩存緩存圖片數(shù) private static ImageLoaderEngine instance; private static Handler mHandler; private ExecutorService pool;// 后臺(tái)線程池 // 這里用LinkedHashMap不用LruCache的原因是LruCache直接申請(qǐng)內(nèi)存大小而不是圖片個(gè)數(shù)。此App已經(jīng)有一個(gè)全局的LruCache了,重復(fù)申請(qǐng)內(nèi)存大小對(duì)應(yīng)用不利 private LinkedHashMap<String, Bitmap> mFirstLevelCache;// <momentId>一級(jí)緩存,硬鏈接bitmap,只保留最近用的圖片。 private ConcurrentHashMap<String, SoftReference<Bitmap>> mSecondLevelCache;// <momentId> public static ImageLoaderEngine getInstance(Handler handler) { if (instance == null) { instance = new ImageLoaderEngine(); } if(handler != null) { mHandler = handler; } return instance; } private ImageLoaderEngine() { pool = Executors.newFixedThreadPool(4);// 默認(rèn)線程池大小為6 initCache(); } private void initCache() { mFirstLevelCache = new LinkedHashMap<String, Bitmap>(MAX_CAPACITY / 2, 0.75f, true) { private static final long serialVersionUID = 1L; protected boolean removeEldestEntry(Entry<String, Bitmap> eldest) { if (size() > MAX_CAPACITY) {// 超過(guò)一級(jí)緩存大小后會(huì)挪到二級(jí)緩存中 mSecondLevelCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue())); return true; } return false; }; }; mSecondLevelCache = new ConcurrentHashMap<String, SoftReference<Bitmap>>();// <momentId> } /** * 移除緩存 * @param key */ public void deleteCacheByKey(String key) { String sdCacheingPath = IOHelper.getCachedPicturePath( Global.packageName, key); String sdCacheedPath = sdCacheingPath +".png"; File file = new File(sdCacheingPath); if(file.exists()) { file.delete(); } file = new File(sdCacheedPath); if(file.exists()) { file.delete(); } mFirstLevelCache.remove(key); mSecondLevelCache.remove(key); } /** * 釋放資源 */ public void recycleImageLoader() { new Thread(new Runnable() { @Override public void run() { if (pool != null) { pool.shutdownNow(); } if (mFirstLevelCache != null) { for (Bitmap bmp : mFirstLevelCache.values()) { if (bmp != null) { bmp.recycle(); bmp = null; } } mFirstLevelCache.clear(); mFirstLevelCache = null; } if (mSecondLevelCache != null) { mSecondLevelCache.clear(); } mHandler = null; } }).start(); } /** * 后臺(tái)請(qǐng)求圖片 * * @param item */ public void loadImageByMoment(final NMoment moment,String photoTag) { if (moment.isPicture() || moment.isVideo()) { String id = moment.id + photoTag; loadImageByUrl(id+"", moment.getPicture(Global.widthPixels/3*2),moment.orientation); } } /** * 后臺(tái)請(qǐng)求圖片 * @param key * @param url */ public void loadImageByUrl(final String key,final String url,final int orientation) { pool.submit(new Runnable() { public void run() { LogHelper.e("ImageLoaderEngine","從網(wǎng)絡(luò)中下載"); // 如果內(nèi)存中有就算了 if (mFirstLevelCache.get(key) != null || mSecondLevelCache.get(key) != null) {// 如果圖片已經(jīng)緩存了 LogHelper.e("ImageLoaderEngine","下載圖片錯(cuò)誤 1"); return; } // 如果SD卡緩存中有就算了 final String sdCacheingPath = IOHelper.getCachedPicturePath( Global.packageName, key); File cacheingFile = new File(sdCacheingPath); if (cacheingFile.exists()) {// 如果正在緩存就算了 long currentTime = System.currentTimeMillis(); if((currentTime - cacheingFile.lastModified()) >2 * 60 * 1000) { LogHelper.e("ImageLoaderEngine","2分鐘都還沒(méi)下載完,準(zhǔn)備刪除它.."+currentTime+"="+cacheingFile.lastModified()); cacheingFile.delete(); } else { getBitmapFromNetworkAndAddToMemory(url, key, orientation); LogHelper.e("ImageLoaderEngine","第二次進(jìn)來(lái)應(yīng)該走這里.."); return; } } String sdCacheedPath = sdCacheingPath + ".png";// 緩存完成后會(huì)改名字,否則會(huì)導(dǎo)致緩存錯(cuò)誤,圖片變黑 File cacheedFile = new File(sdCacheedPath); if (cacheedFile.exists()) {// 如果緩存了就算了 LogHelper.e("ImageLoaderEngine","下載圖片錯(cuò)誤 2"); return; } getBitmapFromNetworkAndAddToMemory(url, key, orientation); } }); } private void getBitmapFromNetworkAndAddToMemory(String url,String key,int orientation) { Bitmap bmp = getBitmapFromUrl(url); if(bmp!= null) { LogHelper.e("ImageLoaderEngine","下載網(wǎng)絡(luò)圖片成功"); if(key.endsWith("_DetailDaily")) { bmp = scaledBitmap(bmp, Global.getThumbWidth()); } if(orientation != 0) { mFirstLevelCache.put(key, ViewHelper.rotateBitmap(orientation, bmp));// 從網(wǎng)絡(luò)下載后直接顯示 } else { mFirstLevelCache.put(key, bmp);// 從網(wǎng)絡(luò)下載后直接顯示 } if (mHandler != null) { mHandler.removeMessages(LOAD_IMG_SUCCESS); mHandler.sendEmptyMessageDelayed( LOAD_IMG_SUCCESS, 600);// 延時(shí)提示沒(méi)有數(shù)據(jù)了 } final String sdCacheingPath = IOHelper.getCachedPicturePath( Global.packageName, key); saveBitmapToFile(sdCacheingPath, bmp); } else { LogHelper.e("ImageLoaderEngine","下載網(wǎng)絡(luò)圖片失敗..."); } } /** * 直接從網(wǎng)絡(luò)中獲取 * @param url * @return */ public Bitmap getBitmapFromUrl(String url) { URL myFileUrl = null; Bitmap bitmap = null; InputStream is = null; try { if (!UIUtils.isNetworkAvailable(MyApplication.getInstance())) { return null; } myFileUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection) myFileUrl .openConnection(); conn.setDoInput(true); conn.connect(); is = conn.getInputStream(); bitmap = BitmapFactory.decodeStream(is); } catch (Exception e) { try { if(is != null) { is.close(); } } catch (IOException e1) { e1.printStackTrace(); } e.printStackTrace(); } return bitmap; } public Bitmap getImageInMemory(NMoment moment) { return getImageInMemory(moment, ""); } /** * 新增接口,可以根據(jù)tag重新標(biāo)識(shí)Moment,這樣可以擴(kuò)展應(yīng)用場(chǎng)景,比如首頁(yè)需要大圖,進(jìn)入相集頁(yè)需要小圖 * @param moment * @param photoTag * @return */ public Bitmap getImageInMemory(NMoment moment, String photoTag) { String id = moment.id + photoTag; Bitmap bmp = null; // 1. 從一級(jí)緩存中獲取 bmp = getFromFirstLevelCache(id); if (bmp != null && !bmp.isRecycled()) { LogHelper.e("ImageLoaderEngine","一級(jí)緩存獲取:"+id); return bmp; } // 2. 從二級(jí)緩存中獲取 bmp = getFromSecondLevelCache(id); if (bmp != null && !bmp.isRecycled()) { LogHelper.e("ImageLoaderEngine","二級(jí)緩存獲取:"+id); return bmp; } if(bmp != null && bmp.isRecycled()) { return null; } else { return bmp; } } public void setImage(String key,Bitmap picture) { mFirstLevelCache.put(key, picture); } /** * 獲取圖片 */ public Bitmap getImage(NMoment moment) { return getImage(moment, ""); } public Bitmap getImage(NMoment moment, String photoTag) { String id = moment.id + photoTag; Bitmap bmp = null; // 1. 從一級(jí)緩存中獲取 bmp = getFromFirstLevelCache(id); if (bmp != null && !bmp.isRecycled()) { LogHelper.e("ImageLoaderEngine","一級(jí)緩存獲取:"+id); return bmp; } // 2. 從二級(jí)緩存中獲取 bmp = getFromSecondLevelCache(id); if (bmp != null && !bmp.isRecycled()) { LogHelper.e("ImageLoaderEngine","二級(jí)緩存獲取:"+id); return bmp; } // 3. 從SD卡緩存中獲取 bmp = getFromSDCache(moment, photoTag); if (bmp != null && !bmp.isRecycled()) { LogHelper.e("ImageLoaderEngine","SD卡緩存獲取:"+id); return bmp; } // 4. 從網(wǎng)絡(luò)中獲取 loadImageByMoment(moment, photoTag); // LogHelper.e("ImageLoaderEngine","本地獲取圖片失敗:"+moment.id+"="+moment.getPicture()); if(bmp != null && bmp.isRecycled()) { return null; } else { return bmp; } } public Bitmap getImage(String key,String url) { Bitmap bmp = null; // 1. 從一級(jí)緩存中獲取 bmp = getFromFirstLevelCache(key); if (bmp != null && !bmp.isRecycled()) { return bmp; } // 2. 從二級(jí)緩存中獲取 bmp = getFromSecondLevelCache(key); if (bmp != null && !bmp.isRecycled()) { return bmp; } // 3. 從SD卡緩存中獲取 bmp = getFromSDCacheByKey(key,0); if (bmp != null && !bmp.isRecycled()) { return bmp; } // 4. 從網(wǎng)絡(luò)中獲取 loadImageByUrl(key, url,0); if(bmp != null && bmp.isRecycled()) { return null; } else { return bmp; } } /** * 一級(jí)緩存獲取圖片 * * @param imgId * @return */ private Bitmap getFromFirstLevelCache(String imgId) { Bitmap bitmap = null; synchronized (mFirstLevelCache) { bitmap = mFirstLevelCache.get(imgId); if (bitmap != null) { mFirstLevelCache.remove(imgId); mFirstLevelCache.put(imgId, bitmap); } } return bitmap; } /** * 二級(jí)緩存獲取圖片 * * @param url * @return */ private Bitmap getFromSecondLevelCache(String imgId) { Bitmap bitmap = null; SoftReference<Bitmap> softReference = mSecondLevelCache.get(imgId); if (softReference != null) { bitmap = softReference.get(); if (bitmap == null) { mSecondLevelCache.remove(imgId); } } return bitmap; } /** * 從SD卡緩存獲取圖片,并放入一級(jí)緩存中 * * @param moment * @return * @throws IOException */ private Bitmap getFromSDCache(final NMoment moment,final String photoTag) { Bitmap drawable = null; String id = moment.id + photoTag; String sdCacheingPath = IOHelper.getCachedPicturePath(Global.packageName, id); String sdCacheedPath = sdCacheingPath + ".png"; if(moment.isLocal){ if(moment.isVideo()) { //獲取本地路徑 sdCacheedPath = moment.getPicture(Global.widthPixels/3*2); } else { sdCacheedPath = moment.local_res_path; } } File cacheFile = new File(sdCacheedPath); if (!cacheFile.exists()) {// 如果沒(méi)有緩存完成就退出 LogHelper.e("ImageLoaderEngine","找不到緩存文件:"+sdCacheedPath); if(!TextUtils.isEmpty(moment.local_res_path)) {// 如果本地有圖片,就先用本地圖片代替 sdCacheedPath = moment.local_res_path; cacheFile = new File(sdCacheedPath); if (cacheFile.exists() && !GlobalData.PHONE_MANUFACTURER.equalsIgnoreCase("samsung")) { LogHelper.e("ImageLoaderEngine","AK47...:"+GlobalData.PHONE_MANUFACTURER);// 先從本地找替代圖片.. new Thread(new Runnable() {// 從網(wǎng)絡(luò)下載 @Override public void run() { loadImageByMoment(moment, photoTag); } }).start(); return getFitPhoto(sdCacheedPath, moment, cacheFile); } else { return null; } } else { return null; } } drawable = getFitPhoto(sdCacheedPath, moment, cacheFile); if (drawable != null) { if (moment.orientation != 0) { drawable = ViewHelper .rotateBitmap(moment.orientation, drawable); } if(mFirstLevelCache != null) { mFirstLevelCache.put(id, drawable); } } else { cacheFile.delete(); } return drawable; } private Bitmap getFitPhoto(String sdCacheedPath,NMoment moment,File cacheFile) { FileInputStream fs = null; Bitmap result; try { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(sdCacheedPath, options); int hRatio = (int) Math.ceil(options.outHeight / (float) moment.picture_height); // 算高度 int wRatio = (int) Math.ceil(options.outWidth / (float) Global.widthPixels); // 算寬度 if (hRatio > 1 || wRatio > 1) { if (hRatio > wRatio) { options.inSampleSize = hRatio; } else options.inSampleSize = wRatio; } options.inPurgeable = true; options.inInputShareable = true; options.inDither = false; options.inJustDecodeBounds = false; try { fs = new FileInputStream(cacheFile); } catch (FileNotFoundException e) { e.printStackTrace(); } result = BitmapFactory.decodeFileDescriptor(fs.getFD(), null, options); } catch (Exception e) { throw new RuntimeException(e); } finally { if (fs != null) { try { fs.close(); } catch (IOException e) { e.printStackTrace(); } } } return result; } private Bitmap getFromSDCacheByKey(String key,int orientation) { Bitmap drawable = null; FileInputStream fs = null; String sdCacheedPath = IOHelper.getCachedPicturePath( Global.packageName, key) + ".png"; File cacheFile = new File(sdCacheedPath); if (!cacheFile.exists()) {// 如果沒(méi)有緩存完成就退出 return null; } try { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(sdCacheedPath, options); int wRatio = (int) Math.ceil(options.outWidth / (float) Global.widthPixels); // 算寬度 options.inSampleSize = wRatio; options.inPurgeable = true; options.inInputShareable = true; options.inDither = false; options.inJustDecodeBounds = false; try { fs = new FileInputStream(cacheFile); } catch (FileNotFoundException e) { e.printStackTrace(); } drawable = BitmapFactory.decodeFileDescriptor(fs.getFD(), null, options); if (drawable != null) { if(orientation != 0) { drawable = ViewHelper.rotateBitmap(orientation, drawable); } mFirstLevelCache.put(key, drawable); } else { cacheFile.delete(); } } catch (Exception e) { throw new RuntimeException(e); } finally { if (fs != null) { try { fs.close(); } catch (IOException e) { e.printStackTrace(); } } } return drawable; } /** * 創(chuàng)建一個(gè)灰色的默認(rèn)圖 * @param moment * @return */ public Bitmap getDefaultBitmap(NMoment moment) { return ImageHelper.createBitmap(moment.picture_width, moment.picture_height, R.color.image_bg_daily); } /** * 保存Bitmap文件到sd卡,傳入jpg結(jié)尾的路徑 * @param filePath * @param mBitmap */ public void saveBitmapToFile(String filePath, Bitmap mBitmap) { try { File file = new File(filePath); if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } if (file.exists() && file.length() > 0) { long currentTime = System.currentTimeMillis(); if ((currentTime - file.lastModified()) > 2 * 60 * 1000) { LogHelper.e("ImageLoaderEngine", "2分鐘都還沒(méi)下載完,準(zhǔn)備刪除它.." + currentTime + "=" + file.lastModified()); file.delete(); } else { return; } } else { file.createNewFile(); } FileOutputStream fOut = null; fOut = new FileOutputStream(file); mBitmap.compress(Bitmap.CompressFormat.JPEG, 80, fOut); fOut.flush(); fOut.close(); file.renameTo(new File(filePath+".png")); } catch (Exception e) { e.printStackTrace(); LogHelper.e("ImageLoaderEngine","保存圖片錯(cuò)誤:"+e); } LogHelper.e("ImageLoaderEngine","保存網(wǎng)絡(luò)圖片成功"+filePath+".png"); } /** * 保存文件至緩存,這里重寫而不用IOHelper里面的原因是IOHelper里面過(guò)于復(fù)雜 * * @param url * @param filePath * @return */ public boolean saveUrlBitmapToFile(String url, String filePath) { if (TextUtils.isEmpty(filePath)) { return false; } File iconFile = new File(filePath); if (iconFile.getParentFile() == null) { return false; } if (!iconFile.getParentFile().exists()) { iconFile.getParentFile().mkdirs(); } if (iconFile.exists() && iconFile.length() > 0) { long currentTime = System.currentTimeMillis(); if((currentTime - iconFile.lastModified()) >2 * 60 * 1000) { LogHelper.e("ImageLoaderEngine","2分鐘都還沒(méi)下載完,準(zhǔn)備刪除它.."+currentTime+"="+iconFile.lastModified()); iconFile.delete(); } else { return true; } } FileOutputStream fos = null; InputStream is = null; try { fos = new FileOutputStream(filePath); is = new URL(url).openStream(); int data = is.read(); while (data != -1) { fos.write(data); data = is.read(); } } catch (IOException e) { LogHelper.e("ImageLoaderEngine", "ImageLoaderEngine 下載圖片錯(cuò)誤" + e); iconFile.delete(); e.printStackTrace(); return false; } finally { try { if (is != null) { is.close(); } if (fos != null) { fos.close(); } } catch (IOException e) { e.printStackTrace(); } } iconFile.renameTo(new File(filePath+".png")); return true; } /** * 縮放bitmap * @param bmp * @param scaledValue縮放值 * @return */ public Bitmap scaledBitmap(Bitmap bmp,int scaledValue) { int bmpWidth = bmp.getWidth(); int bmpHeight = bmp.getHeight(); if(bmpWidth >= bmpHeight) {// 橫圖 bmpWidth = (bmpWidth * scaledValue / bmpHeight); bmpHeight = scaledValue; } else { bmpHeight = (bmpHeight * scaledValue / bmpWidth); bmpWidth = scaledValue; } Bitmap scaledBmp = Bitmap.createScaledBitmap(bmp,bmpWidth,bmpHeight,true); bmp.recycle(); bmp = null; return scaledBmp; } }
以上就是一個(gè)完整的Android圖片加載緩存類,希望對(duì)大家的學(xué)習(xí)有所幫助。
- Android中使用Bitmap類將矩形圖片轉(zhuǎn)為圓形的方法
- 非常實(shí)用的Android圖片工具類
- Android開發(fā)之多媒體文件獲取工具類實(shí)例【音頻,視頻,圖片等】
- Android開發(fā)之圖片壓縮工具類完整實(shí)例
- Android開發(fā)實(shí)現(xiàn)的IntentUtil跳轉(zhuǎn)多功能工具類【包含視頻、音頻、圖片、攝像頭等操作功能】
- Android開發(fā)之超強(qiáng)圖片工具類BitmapUtil完整實(shí)例
- Android圖片處理工具類BitmapUtils
- Android開發(fā)之圖片切割工具類定義與用法示例
- Android編程圖片加載類ImageLoader定義與用法實(shí)例分析
- Android編程圖片操作類定義與用法示例【拍照,相冊(cè)選圖及裁剪】
相關(guān)文章
Android實(shí)現(xiàn)萬(wàn)能自定義陰影控件實(shí)例代碼
這篇文章主要給大家介紹了關(guān)于Android實(shí)現(xiàn)萬(wàn)能自定義陰影控件的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)各位Android開發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08Android中Glide實(shí)現(xiàn)超簡(jiǎn)單的圖片下載功能
本篇文章主要介紹了Android中Glide實(shí)現(xiàn)超簡(jiǎn)單的圖片下載功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03android中ViewPager結(jié)合Fragment進(jìn)行無(wú)限滑動(dòng)
本篇文章中主要介紹了android中ViewPager結(jié)合Fragment進(jìn)行無(wú)限滑動(dòng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03Flutter啟動(dòng)頁(yè)(閃屏頁(yè))的具體實(shí)現(xiàn)及原理詳析
這篇文章主要給大家介紹了關(guān)于Flutter啟動(dòng)頁(yè)(閃屏頁(yè))的具體實(shí)現(xiàn)及原理的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Flutter具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04基于Android自定義控件實(shí)現(xiàn)刮刮樂(lè)效果
這篇文章主要介紹了基于Android自定義控件實(shí)現(xiàn)刮刮樂(lè)效果 的相關(guān)資料,需要的朋友可以參考下2015-12-12android原生實(shí)現(xiàn)多線程斷點(diǎn)續(xù)傳功能
這篇文章主要為大家詳細(xì)介紹了android原生實(shí)現(xiàn)多線程斷點(diǎn)續(xù)傳功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07幾個(gè)Android編程時(shí)需要注意的 web 問(wèn)題
這篇文章主要介紹了幾個(gè)Android編程時(shí)需要注意的 web 問(wèn)題,需要的朋友可以參考下2014-12-12Retrofit 創(chuàng)建網(wǎng)絡(luò)請(qǐng)求接口實(shí)例過(guò)程
這篇文章主要為大家介紹了Retrofit 創(chuàng)建網(wǎng)絡(luò)請(qǐng)求接口實(shí)例過(guò)程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12Android自定義view實(shí)現(xiàn)太極效果實(shí)例代碼
這篇文章主要介紹了Android自定義view實(shí)現(xiàn)太極效果實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-05-05