編輯:關於Android編程
眾所周知,每個Android應用程序在運行時都有一定的內存限制,限制大小一般為16MB或24MB(視手機而定)。一般我們可以通過獲取當前線程的可運行內存來判斷,比如系統分給當前運行內存只有16M,而你的圖片就有16M,這肯定會oom的。
相關知識介紹
1.顏色模型
常見的顏色模型有RGB、YUV、CMYK等,在大多數圖像API中采用的都是RGB模型,Android也是如此;另外,在Android中還有包含透明度Alpha的顏色模型,即ARGB。
2.計算機中顏色值的數字化編碼
(1)浮點數編碼:比如float: (1.0, 0.5, 0.75),每個顏色分量各占1個float字段,其中1.0表示該分量的值為全紅或全綠或全藍;
(2)24位的整數編碼:比如24-bit:(255, 128, 196),每個顏色分量各占8位,取值范圍0-255,其中255表示該分量的值為全紅或全綠或全藍;
(3)16位的整數編碼:比如16-bit:(31, 45, 31),第1和第3個顏色分量各占5位,取值范圍0-31,第2個顏色分量占6位,取值范圍0-63;
3.Bitmap在內存中的存儲區域
http://www.7dot9.com/2010/08/android-bitmap%E5%86%85%E5%AD%98%E9%99%90%E5%88%B6/一文中對Android內存限制問題做了一些探討,作者認為Bitmap對象通過棧上的引用來指向堆上的Bitmap對象,而Bitmap對象又對應了一個使用了外部存儲的native圖像,實際上使用的是byte[]來存儲的內存空間。但為了確保外部分配內存成功,應該保證當前已分配的內存加上當前需要分配的內存值,大小不能超過當前堆的最大內存值,而且內存管理上將外部內存完全當成了當前堆的一部分。
4.Java對象的引用類型
(1)強引用(StrongReference)如果一個對象具有強引用,那垃圾回收器絕不會回收它。當內存空間不足,Java虛擬機寧願拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具有強引用的對象來解決內存不足的問題。
(2)軟引用(SoftReference)如果一個對象只具有軟引用,則內存空間足夠,垃圾回收器就不會回收它;如果內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就可以被程序使用。
(3)弱引用(WeakReference)弱引用與軟引用的區別在於:只具有弱引用的對象擁有更短暫的生命周期。在垃圾回收器線程掃描它所管轄的內存區域的過程中,一旦發現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存。
(4)虛引用(PhantomReference)“虛引用”顧名思義,就是形同虛設,與其他幾種引用都不同,虛引用並不會決定對象的生命周期。如果一個對象僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。
有了上面的基礎儲備,我們來談談圖片的oom解決方案:
(1)緩存圖像到內存,采用軟引用緩存到內存,而不是在每次使用的時候都從新加載到內存;
(2)調整圖像大小,手機屏幕尺寸有限,分配給圖像的顯示區域本身就更小,有時圖像大小可以做適當調整;
(3)采用低內存占用量的編碼方式,比如Bitmap.Config.ARGB_4444比Bitmap.Config.ARGB_8888更省內存;
(4)及時回收圖像,如果引用了大量Bitmap對象,而應用又不需要同時顯示所有圖片,可以將暫時用不到的Bitmap對象及時回收掉;
(5)自定義堆內存分配大小,優化Dalvik虛擬機的堆內存分配;(這裡可以參照一些第三方的圖片緩存框架)
場景演示
為了說明出現OOM的場景和解決OOM的方法,我們選取了兩款不同的機型來做比較:
(1)該應用展示一個gallery,該gallery只加載圖片,gallery的adapter中傳入圖片的路徑而不是圖片對象本身,adapter動態加載圖片;
(2)演示所用的圖片預存儲到sdcard的cache目錄下,文件名分別為a.jpg,b.jpg…r.jpg,總共18張;
(3)圖片為規格1920*1200的jpg圖片,文件大小在423KB-1.48MB范圍內;
(4)運行環境:模擬器——android2.2版本系統——480*320屏幕尺寸;Moto Defy——2.3.4版本CM7系統——854*480屏幕尺寸;
1.演示一
首先采用最簡單的圖片加載方式,不帶任何圖片緩存、調整大小或者回收,SimpleImageLoader.class便是承擔此職責。加載圖片部分的代碼如下:
@Override
public Bitmap loadBitmapImage(String path) {
return BitmapFactory.decodeFile(path);
}
@Override
public Drawable loadDrawableImage(String path) {
return new BitmapDrawable(path);
}
演示結果:在模擬器上圖片只能加載1-3張,之後便會出現OOM錯誤;在Defy上不會出現錯誤;原因是兩者內存限制不同,Defy上運行的是第三方ROM,內存分配有40MB。另外gallery每次顯示一張圖片時,都要重新解析獲得一張圖片,盡管在Defy上還未曾出錯,但當圖片量加大,GC回收不及時時,還是有可能出現OOM。
2.演示二
為圖片加載的添加一個軟引用緩存,每次圖片從緩存中獲取圖片對象,若緩存中不存在,才會從Sdcard加載圖片,並將該對象加入緩存。同時軟引用的對象也有助於GC在內存不足的時候回收它們。常見的Discache就是這個原理,大家有興趣的可以自行研究。關鍵代碼
private HashMap> mImageCache;
@Override
public Bitmap loadBitmapImage(String path) {
if(mImageCache.containsKey(path)) {
SoftReferencesoftReference = mImageCache.get(path);
Bitmap bitmap = softReference.get();
if(null != bitmap)
return bitmap;
}
Bitmap bitmap = BitmapFactory.decodeFile(path);
mImageCache.put(path, new SoftReference(bitmap));
return bitmap;
}
@Override
public Drawable loadDrawableImage(String path) {
return new BitmapDrawable(loadBitmapImage(path));
}
3.演示三
為了進一步避免OOM,除了緩存,還可以對圖片進行壓縮,進一步節省內存,多數情況下調整圖片大小並不會影響應用的表現力。這個也是我之前必定做的,就是對圖片進行壓縮。
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;//如果該 值設為true那麼將不返回實際的bitmap,也不給其分配內存空間,這樣就避免內存溢出.
BitmapFactory.decodeFile(path, options);
可以對圖片按尺寸壓縮,也是不錯的方案:
options.inSampleSize = Util.computeSampleSize(options, 600, (int) (1 * 1024 * 1024));
options.inJustDecodeBounds = false;
options.inDither = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bitmap = BitmapFactory.decodeFile(path, options);
4.演示四
在有些情況下,嚴重縮小圖片還是會影響應用的顯示效果的,所以有必要在盡可能少地縮小圖片的前提下展示圖片,手動去回收圖片就變得尤為重要。所以這也是一些第三方圖片庫管理的時候必定用到lrucache算法的原因。
可能開發安卓的人大多數都用過很多下拉刷新的開源組件,但是今天用了官方v4支持包的SwipeRefreshLayout覺得效果也蠻不錯的,特拿出來分享。簡介:SwipeRe
前面章節我們說了如何定義屬性、如何定義寬高,這樣之後組件的簡單外形或輪廓就已經出來,或者說已經定義出了畫布的大小,解下來就是如何在畫布上揮毫潑墨了。組件(除了容器組件)實
本篇我們來做一個類似於微信的圖片點擊浏覽的效果,點擊小圖圖片後會放大至全屏顯示,且中間有一個2D平滑過渡的效果。 思路如下: 首先,從圖片縮略界面跳轉到圖片詳情頁面,應該
一、前言那麼今天,我們繼續來看一篇關於Android中的UI篇,如何自定義視圖View的進階篇,關於前奏篇之前已經寫過了,在這篇文章中我主要介紹了自定義View的一些基礎