Android圖片內存優化的幾點心得
1、將圖片轉化為縮略圖再加載:
BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 2; Bitmap img = BitmapFactory.decodeFile("/sdcard/1.png", options);
該段代碼即是讀取1.png的縮略圖,長度、寬度都隻有原圖片的1/2。圖片大小減少,占用的內存自然也變小了。這麼做的弊端是圖片質量變差,inSampleSize的值越大,圖片的質量就越差。由於各手機廠商縮放圖片的算法不同,在不同手機上的縮放圖片質量可能會不同。筆者就遭遇過moto手機上圖片縮放後質量可以接受,三星手機上同樣的縮放比例,質量卻差很多的情況。
2、用ARBG_4444顏色模式加載圖片:
Android中有四種,分別是:
ALPHA_8:每個像素占用1byte內存
ARGB_4444:每個像素占用2byte內存
ARGB_8888:每個像素占用4byte內存
RGB_565:每個像素占用2byte內存
Android默認的顏色模式為ARGB_8888,這個顏色模式色彩最細膩,顯示質量最高。但同樣的,占用的內存也最大。
BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.ARGB_4444; Bitmap img = BitmapFactory.decodeFile("/sdcard/1.png", options);
以上代碼即是將1.png以ARGB_4444模式讀出。內存減少雖然不如第一種方法明顯,但是對於大多數圖片,看不出與ARGB_8888模式有什麼差別。不過在讀取有漸變效果的圖片時,可能有顏色條出現。另外,會影響圖片的特效處理。
3、調用圖片的recycle()方法:
這個其實不是真正降低圖片內存的方法。主要目的是標記圖片對象,方便回收圖片對象的本地數據。圖片對象的本地數據占用的內存最大,而且與程序Java部分的內存是分開計算的。所以經常出現Java heap足夠使用,而圖片發生OutOfMemoryError的情況。在圖片不使用時調用該方法,可以有效降低圖片本地數據的峰值,從而減少OutOfMemoryError的概率。不過調用了recycle()的圖片對象處於“廢棄”狀態,調用時會造成程序錯誤。所以在無法保證該圖片對象絕對不會被再次調用的情況下,不建議使用該方法。特別要注意已經用setImageBitmap(Bitmap img)方法分配給控件的圖片對象,可能會被係統類庫調用,造成程序錯誤。
例如在小米手機上:
Bitmap old = img; img = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true); old.recycle();
這段代碼會發生Bitmap對象已經被recycle的錯誤。在三星、moto、HTC、華為和其他一些廠商的機器上這段代碼運行正常。雖然是廠商定製的係統實現機製不同造成的問題。但確實是需要注意的問題。 4、使用Matrix對象放大的圖片如何更改顏色模式: 雖然使用Matrix對象放大圖片,必定會耗費更多的內存,但有時候也不得不這樣做。放大後的圖片使用的ARGB_8888顏色模式,就算原圖片是ARGB_4444顏色模式也一樣,而且沒有辦法在放大時直接指定顏色模式。可以采用以下辦法更改圖片顏色模式。
Matrix matrix = new Matrix(); float newWidth = 200;//圖片放大後的寬度 float newHeight = 300;//圖片放大後的長度 matrix.postScale(newWidth / img.getWidth(), newHeight/ img.getHeight()); Bitmap img1 = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);//得到放大的圖片 img2 = img1.copy(Bitmap.Config.ARGB_4444, false);//得到ARGB_4444顏色模式的圖片 img = null; img1 = null;
這裏比起原來的圖片額外生成了一個圖片對象img1。但是係統會自動回收img1,所以實際內存還是減少了。
歸結起來還是以縮略圖模式讀取圖片和減少圖片中每個像素占用的內存。這兩種方法雖然有效,但是也有各自的弊端。實際開發中還是應該根據情況酌情使用。最王道的辦法,還是避免垃圾對象的產生。例如在ListView的使用中,複用convertView等。如果使用AsyncTask加載圖片,要及時將引用的ImageView對象置為null。因為AsyncTask是用線程池實現的,所以其中引用的對象可能會擁有很長的生命周期,造成GC無法釋放。我還是相信Android的內存回收機製的,recycle什麼的雖然一定程度上有效,但總覺得不符合Java內存回收的原則。(最後這句完全是著魔了)
最後更新:2017-04-02 16:48:14