每當碰到一些大圖片的時候,我們如果不對圖片進行處理就會報OOM異常, 這個問題曾經讓我覺得很煩惱,后來終于得到了解決, 那么現(xiàn)在就讓我和大家一起分享一下吧。 這篇博文要講的圖片緩存機制,我接觸到的有兩鐘,一種是軟引用,另一種是內存緩存技術。 先來看下兩者的使用方式,再來作比較。 除了加載圖片時要用到緩存處理,還有一個比較重要的步驟要做,就是要先壓縮圖片。
1、壓縮圖片 至于要壓縮到什么狀態(tài)就要看自己當時的處境了,壓縮圖片的時候既要達到一個小的值,又不能讓其模糊 ,更不能拉伸圖片。
- /**
- * 加載內存卡圖片
- */
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true; // 設置了此屬性一定要記得將值設置為false
- Bitmap bitmap = null;
- bitmap = BitmapFactory.decodeFile(url, options);
- int be = (int) ((options.outHeight > options.outWidth ? options.outHeight / 150
- : options.outWidth / 200));
- if (be <= 0) // 判斷200是否超過原始圖片高度
- be = 1; // 如果超過,則不進行縮放
- options.inSampleSize = be;
- options.inPreferredConfig = Bitmap.Config.ARGB_4444;
- options.inPurgeable = true;
- options.inInputShareable = true;
- options.inJustDecodeBounds = false;
- try {
- bitmap = BitmapFactory.decodeFile(url, options);
- } catch (OutOfMemoryError e) {
- System.gc();
- Log.e(TAG, "OutOfMemoryError");
- }
2、軟引用: 只要有足夠的內存,就一直保持對象,直到發(fā)現(xiàn)內存吃緊且沒有Strong Ref時才回收對象。 我們可以這樣定義:map里面的鍵是用來放圖片地址的,既可以是網絡上的圖片地址,也可以SDcard上的圖片地址, map里面的值里面放的是持有軟引用的Bitmap,當然如果你要放Drawable,那也是可以的。
- private Map<String, SoftReference<Bitmap>> imageMap
- = new HashMap<String, SoftReference<Bitmap>>();
接下來就讓我再介紹一下如何具體加載圖片: 步驟:(1)先通過URL查看緩存中是否有圖片,如果有,則直接去緩存中取得。 如果沒有,就開線程重新去網上下載。 (2)下載完了之后,就把圖片放在緩存里面,方便下次可以直接從緩存中取得。
- public Bitmap loadBitmap(final String imageUrl,final ImageCallBack imageCallBack) {
- SoftReference<Bitmap> reference = imageMap.get(imageUrl);
- if(reference != null) {
- if(reference.get() != null) {
- return reference.get();
- }
- }
- final Handler handler = new Handler() {
- public void handleMessage(final android.os.Message msg) {
- //加入到緩存中
- Bitmap bitmap = (Bitmap)msg.obj;
- imageMap.put(imageUrl, new SoftReference<Bitmap>(bitmap));
- if(imageCallBack != null) {
- imageCallBack.getBitmap(bitmap);
- }
- }
- };
- new Thread(){
- public void run() {
- Message message = handler.obtainMessage();
- message.obj = downloadBitmap(imageUrl);
- handler.sendMessage(message);
- }
- }.start();
- return null ;
- }
- // 從網上下載圖片
- private Bitmap downloadBitmap (String imageUrl) {
- Bitmap bitmap = null;
- try {
- bitmap = BitmapFactory.decodeStream(new URL(imageUrl).openStream());
- return bitmap ;
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
- public interface ImageCallBack{
- void getBitmap(Bitmap bitmap);
- }
2、內存緩存技術 另外一種圖片緩存的方式就是內存緩存技術。在Android中,有一個叫做LruCache類專門用來做圖片緩存處理的。 它有一個特點,當緩存的圖片達到了預先設定的值的時候,那么近期使用次數(shù)最少的圖片就會被回收掉。 步驟:(1)要先設置緩存圖片的內存大小,我這里設置為手機內存的1/8, 手機內存的獲取方式:int MAXMEMONRY = (int) (Runtime.getRuntime() .maxMemory() / 1024); (2)LruCache里面的鍵值對分別是URL和對應的圖片 (3)重寫了一個叫做sizeOf的方法,返回的是圖片數(shù)量。
- private LruCache<String, Bitmap> mMemoryCache;
- private LruCacheUtils() {
- if (mMemoryCache == null)
- mMemoryCache = new LruCache<String, Bitmap>(
- MAXMEMONRY / 8) {
- @Override
- protected int sizeOf(String key, Bitmap bitmap) {
- // 重寫此方法來衡量每張圖片的大小,默認返回圖片數(shù)量。
- return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
- }
- @Override
- protected void entryRemoved(boolean evicted, String key,
- Bitmap oldValue, Bitmap newValue) {
- Log.v("tag", "hard cache is full , push to soft cache");
-
- }
- };
- }
(4)下面的方法分別是清空緩存、添加圖片到緩存、從緩存中取得圖片、從緩存中移除。 移除和清除緩存是必須要做的事,因為圖片緩存處理不當就會報內存溢出,所以一定要引起注意。
- public void clearCache() {
- if (mMemoryCache != null) {
- if (mMemoryCache.size() > 0) {
- Log.d("CacheUtils",
- "mMemoryCache.size() " + mMemoryCache.size());
- mMemoryCache.evictAll();
- Log.d("CacheUtils", "mMemoryCache.size()" + mMemoryCache.size());
- }
- mMemoryCache = null;
- }
- }
- public synchronized void addBitmapToMemoryCache(String key, Bitmap bitmap) {
- if (mMemoryCache.get(key) == null) {
- if (key != null && bitmap != null)
- mMemoryCache.put(key, bitmap);
- } else
- Log.w(TAG, "the res is aready exits");
- }
- public synchronized Bitmap getBitmapFromMemCache(String key) {
- Bitmap bm = mMemoryCache.get(key);
- if (key != null) {
- return bm;
- }
- return null;
- }
- /**
- * 移除緩存
- *
- * @param key
- */
- public synchronized void removeImageCache(String key) {
- if (key != null) {
- if (mMemoryCache != null) {
- Bitmap bm = mMemoryCache.remove(key);
- if (bm != null)
- bm.recycle();
- }
- }
- }
4、兩者的比較 說到這里,我覺得有必要來進行一下比較了。 網上有很多人使用軟引用加載圖片的多 ,但是現(xiàn)在已經不再推薦使用這種方式了, (1)因為從 Android 2.3 (API Level 9)開始,垃圾回收器會更傾向于回收持有軟引用或弱引用的對象, 這讓軟引用和弱引用變得不再可靠。 (2)另外,Android 3.0 (API Level 11)中,圖片的數(shù)據(jù)會存儲在本地的內存當中, 因而無法用一種可預見的方式將其釋放,這就有潛在的風險造成應用程序的內存溢出并崩潰, 所以我這里用得是LruCache來緩存圖片,當存儲Image的大小大于LruCache設定的值,系統(tǒng)自動釋放內存, 這個類是3.1版本中提供的,如果你是在更早的Android版本中開發(fā),則需要導入android-support-v4的jar包。
后記:我一直有強調一件事件,就是人應該要不停地進步,沒有人生來就會編碼, 更沒有人一開始就能找到很好的解決方案,我介紹了這兩種用法,其實就是想說, 這些都是我的技術進步的一個歷程。如果大家有好的建議或者有什么好的看法, 記得提出來,很高興能和大家分享。
|