Android基礎入門教程——8.2.2 Bitmap引起的OOM問題

來源:互聯網
上載者:User

Android基礎入門教程——8.2.2 Bitmap引起的OOM問題
Android基礎入門教程——8.2.2 Bitmap引起的OOM問題

標籤(空格分隔): Android基礎入門教程

本節引言:

上節,我們已經學習了Bitmap的基本用法,而本節我們要來探討的Bitmap的OOM問題,
大家在實際開發中可能遇到過,或者沒遇到過因為Bitmap引起的OOM問題,本節我們
就來圍繞這個話題來進行學習~瞭解什麼是OOM,為什麼會引起OOM,改善因Bitmap引起的
OOM問題~

1.什麼是OOM?為什麼會引起OOM?

答:Out Of Memory(記憶體溢出),我們都知道Android系統會為每個APP分配一個獨立的工作空間,
或者說分配一個單獨的Dalvik虛擬機器,這樣每個APP都可以獨立運行而不相互影響!而Android對於每個
Dalvik虛擬機器都會有一個最大記憶體限制,如果當前佔用的記憶體加上我們申請的記憶體資源超過了這個限制
,系統就會拋出OOM錯誤!另外,這裡別和RAM混淆了,即時當前RAM中剩餘的記憶體有1G多,但是OOM還是會發生!別把RAM(實體記憶體)和OOM扯到一起!另外RAM不足的話,就是殺應用了,而不是僅僅是OOM了!
而這個Dalvik中的最大記憶體標準,不同的機型是不一樣的,可以調用:

ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);Log.e(HEHE,最大記憶體: + activityManager.getMemoryClass());

>
獲得正常的最大記憶體標準,又或者直接在命令列鍵入:

adb shell getprop | grep dalvik.vm.heapgrowthlimit

你也可以開啟系統源碼/system/build.prop檔案,看下檔案中這一部分的資訊得出:

dalvik.vm.heapstartsize=8mdalvik.vm.heapgrowthlimit=192mdalvik.vm.heapsize=512mdalvik.vm.heaptargetutilization=0.75dalvik.vm.heapminfree=2mdalvik.vm.heapmaxfree=8m

我們關注的地方有三個:heapstartsize堆記憶體的初始大小,heapgrowthlimit標準的應用的最大堆
記憶體大小,heapsize則是設定了使用android:largeHeap的應用的最大堆記憶體大小!
我這裡試了下手頭幾個機型的正常最大記憶體配置標準:

你也可以試試自己手頭的機子~
好啦,不扯了,關於OOM問題的產生,就扯到這裡,再扯就到記憶體管理那一塊了,可是個大塊頭,
現在還啃不動…下面我們來看下避免Bitmap OOM的一些技巧吧!

2.避免Bitmap引起的OOM技巧小結1)採用低記憶體佔用量的編碼方式

上一節說了BitmapFactory.Options這個類,我們可以設定下其中的inPreferredConfig屬性,
預設是Bitmap.Config.ARGB_8888,我們可以修改成Bitmap.Config.ARGB_4444
Bitmap.Config ARGB_4444:每個像素佔四位,即A=4,R=4,G=4,B=4,那麼一個像素點佔4+4+4+4=16位
Bitmap.Config ARGB_8888:每個像素佔八位,即A=8,R=8,G=8,B=8,那麼一個像素點佔8+8+8+8=32位
預設使用ARGB_8888,即一個像素佔4個位元組!

2)圖片壓縮

同樣是BitmapFactory.Options,我們通過inSampleSize設定縮放倍數,比如寫2,即長寬變為原來的1/2,圖片就是原來的1/4,如果不進行縮放的話設定為1即可!但是不能一味的壓縮,畢竟這個值太小
的話,圖片會很模糊,而且要避免圖片的展開變形,所以需要我們在程式中動態計算,這個
inSampleSize的合適值,而Options中又有這樣一個方法:inJustDecodeBounds,將該參數設定為
true後,decodeFiel並不會分配記憶體空間,但是可以計算出原始圖片的長寬,調用
options.outWidth/outHeight擷取出圖片的寬高,然後通過一定的演算法,即可得到適合的
inSampleSize,這裡感謝街神提供的代碼——摘自鴻洋blog!

    public static int caculateInSampleSize(BitmapFactory.Options options, int reqWidth,                                           int reqHeight) {        int width = options.outWidth;        int height = options.outHeight;        int inSampleSize = 1;        if (width > reqWidth || height > reqHeight) {            int widthRadio = Math.round(width * 1.0f / reqWidth);            int heightRadio = Math.round(height * 1.0f / reqHeight);            inSampleSize = Math.max(widthRadio, heightRadio);        }        return inSampleSize;    }

然後使用下上述的方法即可:

 BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; // 設定了此屬性一定要記得將值設定為false Bitmap bitmap = null; bitmap = BitmapFactory.decodeFile(url, options); options.inSampleSize = computeSampleSize(options,128,128); options.inPreferredConfig = Bitmap.Config.ARGB_4444; /* 下面兩個欄位需要組合使用 */   options.inPurgeable = true; options.inInputShareable = true; options.inJustDecodeBounds = false; try {        bitmap = BitmapFactory.decodeFile(url, options);    } catch (OutOfMemoryError e) {            Log.e(TAG, OutOfMemoryError); }
3.及時回收映像

如果引用了大量的Bitmap對象,而應用又不需要同時顯示所有圖片。可以將暫時不用到的Bitmap對象
及時回收掉。對於一些明確知道圖片使用方式的情境可以主動recycle回收,比如引導頁的圖片,使用
完就recycle,幀動畫,載入一張,畫一張,釋放一張!使用時載入,不顯示時直接置null或recycle!
比如:imageView.setImageResource(0);
不過某些情況下會出現特定圖片反覆載入,釋放,再載入等,低效率的事情…

4.其他方法

下面這些方法,我並沒有用過,大家可以自行查閱相關資料:

1.簡單通過SoftReference引用方式管理圖片資源

建個SoftReference的hashmap
使用圖片時先查詢這個hashmap是否有softreference, softreference裡的圖片是否為空白,
如果為空白就載入圖片到softreference並加入hashmap。
無需再代碼裡顯式的處理圖片的回收與釋放,gc會自動處理資源的釋放。
這種方式處理起來簡單實用,能一定程度上避免前一種方法反覆載入釋放的低效率。但還不夠最佳化。

範例程式碼:

private Map> imageMap                                            = new HashMap>();public Bitmap loadBitmap(final String imageUrl,final ImageCallBack imageCallBack) {        SoftReference 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));                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.LruCache + sd的緩衝方式

Android 3.1版本起,官方還提供了LruCache來進行cache處理,當儲存Image的大小大於LruCache
設定的值,那麼近期使用次數最少的圖片就會被回收掉,系統會自動釋放記憶體!

使用樣本:
步驟:
1)要先設定緩衝圖片的記憶體大小,我這裡設定為手機記憶體的1/8,
手機記憶體的擷取方式:int MAXMEMONRY = (int) (Runtime.getRuntime() .maxMemory() / 1024);
2)LruCache裡面的索引值對分別是URL和對應的圖片
3)重寫了一個叫做sizeOf的方法,返回的是圖片數量。

private LruCache mMemoryCache;private LruCacheUtils() {        if (mMemoryCache == null)            mMemoryCache = new LruCache(                    MAXMEMONRY / 8) {                @Override                protected int sizeOf(String key, Bitmap bitmap) {                    // 重寫此方法來衡量每張圖片的大小,預設返回圖片數量。                    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();            }        }    }

上述內容摘自——圖片緩衝之記憶體緩衝技術LruCache,軟引用

本節小結:

本節給大家講解了OOM問題的發生緣由,也總結了一下網上給出的一些避免因Bitmap而引起OOM
的一些方案,因為公司做的APP都是地圖類的,很少涉及到圖片,所以筆者並沒有遇到過OOM的問題,
所以對此並不怎麼熟悉~後續在進階課程的記憶體管理,我們再慢慢糾結這個OOM的問題,好的,
本節就到這裡,謝謝~

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.