Android載入圖片OOM錯誤解決方案
前幾天做項目的時候,甲方要求是PAD (SAMSUNG P600 10.1寸 2560*1600)的PAD上顯示高解析度的大圖片。
SQLITE採用BOLD方式儲存圖片,這個存取過程就不說了哈,網上一大堆。
但是在載入/讀取/顯示圖片的時候會報OOM錯誤,上網查了很多解決方案還繞了很多彎路,最後還是找到了原因所在,下面從幾個方面來解釋一下OOM問題的解決方案。
(謝謝周同學的“尺寸”提醒,不然我可能一輩子都要蒙在鼓裡)
1.Android APP記憶體
做一個APP開發的時候,還是不要想著去擴大Android系統賦予的記憶體上限了,部分老機型老系統16M,大部分都是24M了。
一些大型遊戲用dalvik.system.VMRuntime來幹涉GC過程(這個類我還沒有學過。。。剛聽說不久)。
據說用NDK開發時候,C可以動態申請多餘的記憶體空間,但是我沒用過NDK,今後也不打算用了(個人原因)。
2.圖片檔案大小
甲方給了一大堆檔案大小不一的圖片,在載入資料庫->讀取出來->顯示出來的這個過程中,出現了很多OOM,分為:
a.載入圖片時使用ByteArrayStream建立流,size為Height * Width * 4,OOM;
b.讀取圖片時候OOM,同上;
c.顯示圖片時候OOM,decodeResource函數報錯,OOM;
開始時候以為是檔案大小問題,後來發現有些2M的圖片都能顯示,但是某些1M的圖片確報錯,所以在一定範圍內,可以證明,圖片OOM問題與檔案大小無關。
3.圖片尺寸(解析度)
調查這些能顯示的圖片和不能顯示的圖片的不同,發現長寬差距很大,那些能成功顯示的圖片為1005*1500大小,而其他圖片都是3000*5000以上。
我用PAINT(一款適合小白的影像處理軟體,雖然不如PS但是功能已經非常強大了,最新版需要安裝NET 4.5)壓縮了圖片大小,壓縮到1005*1500,顯示成功。
4.函數調用
閱讀了這位大神的部落格:
http://blog.csdn.net/huangbiao86/article/details/8072128
摘取其中最精華的部分吧:
盡量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource來設定一張大圖,因為這些函數在完成decode後,最終都是通過java層的createBitmap來完成的,需要消耗更多記憶體。
因此,改用先通過BitmapFactory.decodeStream方法,建立出一個bitmap,再將其設為ImageView的 source,decodeStream最大的秘密在於其直接調用JNI>>nativeDecodeAsset()來完成decode,無需再使用java層的createBitmap,從而節省了java層的空間。
如果在讀取時加片的Config參數,可以跟有效減少載入的記憶體,從而跟有效阻止拋out of Memory異常
另外,decodeStream直接拿的圖片來讀取位元組碼了, 不會根據機器的各種解析度來自動適應, 使用了decodeStream之後,需要在hdpi和mdpi,ldpi中配置相應的圖片資源, 否則在不同解析度機器上都是同樣大小(像素點數量),顯示出來的大小就不對了。
看來讀底層源碼還是很有用的,setImageBitmap 和 setImageResource 和 decodeResource在執行過程中還是調用了createBitmap來建立一個新的bitmap,建立bitmap會加劇記憶體消耗,所以不推薦使用了,應該使用decodeStream方法。
圖片的壓縮過程也可以設定一個合適的百分比來控制大小。
多張圖片的使用中,請注意流的flush與close(我沒有及時flush和close的時候也運行正確了,這個有待研究一下,但是為了保證一個良好的習慣還是注意下吧)。
5.關於options方法
網上還有很多文章用BitmapFactory.Options來作為decodeStream時候的一個參數,這個我暫時沒有具體實驗過,哪位朋友實驗過了可以回複交流哈