關於android中使用很多大圖片的一些做法)

來源:互聯網
上載者:User

最近在xoom上開發應用,碰到ui設計都是使用圖片,而且是多個activity。開始沒覺得怎麼樣,就開始做唄。等做完了,開始在前三個activity運行沒問題,一切ok。但在最後一個activity裡,會經常出現oom(out of memory),由於在最後一個activity,需要開啟一個pdf,然後render,隨著multi-touch,reander的pdf頁縮放,由於reander的圖片本身就比較大(比如,如果pdf放大到當前螢幕的兩倍,pdf圖片佔用的記憶體為1280*800*4*2/(1024*1024),約等於8m),而且由於為了視覺上感受好,會在其中緩衝圖片(為了不讓使用者在使用過程中感受操作有停滯感),所以總是導致oom異常。

oh,my god!最怕碰到這種情況,android對於記憶體heap size限制讓人比較崩潰,ios雖然也號稱一個應用有記憶體限制,但是在實際使用中一個應用使用的記憶體往往可以超過100m,所以還是挺容易做一個效能滿意的應用程式。

我的應用程式到底哪些地方使用了這麼多記憶體,因為android3.0預設heap size為48m,按道理來說還是可以接受的,怎麼應用沒跑幾下就oom呢?沒辦法,只能通過ddms來分析,在ddms中“update heap”-》“cause gc”,來查看應用的記憶體使用量情況,發現每進入一個activity,1-byte array(byte[], boolean[])的值總是會相應的增加,到最後一個activity的時候啥都不幹,heap size已經快30m了,oh。。。怎麼會這樣。。。冷靜冷靜。。。通過分析,1-byte
array就是bitmap的佔用空間,這就說明不斷有新的bitmap在記憶體中。由於ui使用了很多圖片,比如大背景圖,按鈕圖片等等,看來是這些圖片都會存在記憶體中,即使當前activity已經銷毀進入下一個activity,前一個activity的圖片資源也沒有銷毀。

原因找到了,但不是太想得通。因為在onCreate中我用mBtn.setBackgroundResource(R.drawable.splash)為控制項設定背景圖,然後在onDestroy中會用((BitmapDrawable)mBtn.getBackground()).setCallback(null)清理背景圖。按道理來說圖片資源應該已經清理掉了的。百思不得其解,仔細看Bitmap的原始碼,它其實起的作用是銷毀java對象BitmapDrawable,而android為了提高效率,Bitmap真正的位元影像資料是在ndk中用c寫的,所以用setCallback是不能銷毀位元影像資料的,應該調用Bitmap的recycle()來清理記憶體。

所以想當然的在onDestroy加上((BitmapDrawable)mBtn.getBackground()).getBitmap().recycle(),這樣跑下來,記憶體情況很理想,不管在哪個activity中,使用的資源僅僅是當前activity用到的,就不會象之前到最後一個activity的時候,所有之前使用的資源都累積在記憶體中。在每個activity資源和class等使用的記憶體都在10m左右,已經很理想了(當然如果是在android低版本比如1.5,16時還是不行的,這得重新構架應用),可以為顯示pdf預留了比較多記憶體了。

但新的問題又出現了,當返回之前的activity時,會出現“try to use a recycled bitmap"的異常。這真是按了葫蘆起了瓢啊,內心那個沮喪。。。沒辦法,繼續分析。看來是後加上recycle引起的, 位元影像肯定在記憶體中有引用,在返回之前的activity時,因為位元影像資料其實已經被銷毀了,所以才造成目前的情況。在看了setBackgroundResource的源碼以後,恍然大悟,android對於直接通過資源id載入的資源其實是做了cache的了,這樣下次再需要此資源的時候直接從cache中得到,這也是為效率考慮。但這樣做也造成了用過的資源都會在記憶體中,這樣的設計不是很適合使用了很多大圖片資源的應用,這樣累積下來應用的記憶體峰值是很高的。看了sdk後,我用:

Bitmap bm = BitmapFactory.decodeResource(this.getResources(), R.drawable.splash);
BitmapDrawable bd = new BitmapDrawable(this.getResources(), bm);

mBtn.setBackgroundDrawable(bd);

來代替mBtn.setBackgroundResource(R.drawable.splash)。

銷毀的時候使用:

BitmapDrawable bd = (BitmapDrawable)mBtn.getBackground();

mBtn.setBackgroundResource(0);//別忘了把背景設為null,避免onDraw重新整理背景時候出現used a recycled bitmap錯誤

bd.setCallback(null);
bd.getBitmap().recycle();

這樣調整後,避免了在應用裡緩衝所有的資源,節省了寶貴的記憶體,而其實這樣也不會造成太大效率問題,畢竟重新載入資源是非常快速,不會對效能造成很嚴重的影響,在xoom裡我沒有感受到和之前有什麼區別。

總之,在android上使用大量位元影像是個比較痛苦的事,記憶體限制的存在對應用是個很大的瓶頸。但不用因噎費食,其實弄明白了它裡面的機制,應用可以突破這些限制的。這隻是其中的一種處理方法,還可以考慮BitmapFactory.Options的inSampleSize來減少記憶體佔用。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.