Bitmap引起的OOM問題
1.什么是OOM?為什么會引起OOM?
答:Out Of Memory(內(nèi)存溢出),我們都知道Android系統(tǒng)會為每個(gè)APP分配一個(gè)獨(dú)立的工作空間,或者說分配一個(gè)單獨(dú)的Dalvik虛擬機(jī),這樣每個(gè)APP都可以獨(dú)立運(yùn)行而不相互影響!而Android對于每個(gè)Dalvik虛擬機(jī)都會有一個(gè)最大內(nèi)存限制,如果當(dāng)前占用的內(nèi)存加上我們申請的內(nèi)存資源超過了這個(gè)限制,系統(tǒng)就會拋出OOM錯(cuò)誤!另外,這里別和RAM混淆了,即時(shí)當(dāng)前RAM中剩余的內(nèi)存有1G多,但是OOM還是會發(fā)生!別把RAM(物理內(nèi)存)和OOM扯到一起!另外RAM不足的話,就是殺應(yīng)用了,而不是僅僅是OOM了!而這個(gè)Dalvik中的最大內(nèi)存標(biāo)準(zhǔn),不同的機(jī)型是不一樣的,可以調(diào)用:
ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
Log.e("HEHE","最大內(nèi)存:" + activityManager.getMemoryClass());獲得正常的最大內(nèi)存標(biāo)準(zhǔn),又或者直接在命令行鍵入:
adb shell getprop | grep dalvik.vm.heapgrowthlimit
你也可以打開系統(tǒng)源碼/system/build.prop文件,看下文件中這一部分的信息得出:
dalvik.vm.heapstartsize=8m dalvik.vm.heapgrowthlimit=192m dalvik.vm.heapsize=512m dalvik.vm.heaptargetutilization=0.75 dalvik.vm.heapminfree=2m dalvik.vm.heapmaxfree=8m
你也可以試試自己手頭的機(jī)子~
好啦,不扯了,關(guān)于OOM問題的產(chǎn)生,就扯到這里,再扯就到內(nèi)存管理那一塊了,可是個(gè)大塊頭,現(xiàn)在還啃不動...下面我們來看下避免Bitmap OOM的一些技巧吧!
2.避免Bitmap引起的OOM技巧小結(jié)
1)采用低內(nèi)存占用量的編碼方式
上一節(jié)說了BitmapFactory.Options這個(gè)類,我們可以設(shè)置下其中的inPreferredConfig屬性,默認(rèn)是Bitmap.Config.ARGB_8888,我們可以修改成Bitmap.Config.ARGB_4444Bitmap.Config ARGB_4444:每個(gè)像素占四位,即A=4,R=4,G=4,B=4,那么一個(gè)像素點(diǎn)占4+4+4+4=16位Bitmap.Config ARGB_8888:每個(gè)像素占八位,即A=8,R=8,G=8,B=8,那么一個(gè)像素點(diǎn)占8+8+8+8=32位默認(rèn)使用ARGB_8888,即一個(gè)像素占4個(gè)字節(jié)!
2)圖片壓縮
同樣是BitmapFactory.Options,我們通過inSampleSize設(shè)置縮放倍數(shù),比如寫2,即長寬變?yōu)樵瓉淼?/2,圖片就是原來的1/4,如果不進(jìn)行縮放的話設(shè)置為1即可!但是不能一味的壓縮,畢竟這個(gè)值太小的話,圖片會很模糊,而且要避免圖片的拉伸變形,所以需要我們在程序中動態(tài)的計(jì)算,這個(gè)inSampleSize的合適值,而Options中又有這樣一個(gè)方法:inJustDecodeBounds,將該參數(shù)設(shè)置為true后,decodeFiel并不會分配內(nèi)存空間,但是可以計(jì)算出原始圖片的長寬,調(diào)用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; // 設(shè)置了此屬性一定要記得將值設(shè)置為false
Bitmap bitmap = null;
bitmap = BitmapFactory.decodeFile(url, options);
options.inSampleSize = computeSampleSize(options,128,128);
options.inPreferredConfig = Bitmap.Config.ARGB_4444;
/* 下面兩個(gè)字段需要組合使用 */
options.inPurgeable = true;
options.inInputShareable = true;
options.inJustDecodeBounds = false;
try {
bitmap = BitmapFactory.decodeFile(url, options);
} catch (OutOfMemoryError e) {
Log.e(TAG, "OutOfMemoryError");
}3.及時(shí)回收圖像
如果引用了大量的Bitmap對象,而應(yīng)用又不需要同時(shí)顯示所有圖片??梢詫簳r(shí)不用到的Bitmap對象及時(shí)回收掉。對于一些明確知道圖片使用情況的場景可以主動recycle回收,比如引導(dǎo)頁的圖片,使用完就recycle,幀動畫,加載一張,畫一張,釋放一張!使用時(shí)加載,不顯示時(shí)直接置null或recycle!比如:imageView.setImageResource(0); 不過某些情況下會出現(xiàn)特定圖片反復(fù)加載,釋放,再加載等,低效率的事情...
4.其他方法
下面這些方法,我并沒有用過,大家可以自行查閱相關(guān)資料:
1.簡單通過SoftReference引用方式管理圖片資源
建個(gè)SoftReference的hashmap使用圖片時(shí)先查詢這個(gè)hashmap是否有softreference, softreference里的圖片是否為空,如果為空就加載圖片到softreference并加入hashmap。無需再代碼里顯式的處理圖片的回收與釋放,gc會自動處理資源的釋放。這種方式處理起來簡單實(shí)用,能一定程度上避免前一種方法反復(fù)加載釋放的低效率。但還不夠優(yōu)化。
示例代碼:
private Map<String, SoftReference<Bitmap>> imageMap
= new HashMap<String, SoftReference<Bitmap>>();
public Bitmap loadBitmap(final String imageUrl,final ImageCallBack imageCallBack) {
SoftReference<Bitmap> 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>(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 ;
}
// 從網(wǎng)上下載圖片
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來進(jìn)行cache處理,當(dāng)存儲Image的大小大于LruCache 設(shè)定的值,那么近期使用次數(shù)最少的圖片就會被回收掉,系統(tǒng)會自動釋放內(nèi)存!
使用示例:
步驟:
1)要先設(shè)置緩存圖片的內(nèi)存大小,我這里設(shè)置為手機(jī)內(nèi)存的1/8,手機(jī)內(nèi)存的獲取方式:int MAXMEMONRY = (int) (Runtime.getRuntime() .maxMemory() / 1024);
2)LruCache里面的鍵值對分別是URL和對應(yīng)的圖片
3)重寫了一個(gè)叫做sizeOf的方法,返回的是圖片數(shù)量。
private LruCache<String, Bitmap> mMemoryCache;
private LruCacheUtils() {
if (mMemoryCache == null)
mMemoryCache = new LruCache<String, Bitmap>(
MAXMEMONRY / 8) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// 重寫此方法來衡量每張圖片的大小,默認(rèn)返回圖片數(shù)量。
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)下面的方法分別是清空緩存、添加圖片到緩存、從緩存中取得圖片、從緩存中移除。
移除和清除緩存是必須要做的事,因?yàn)閳D片緩存處理不當(dāng)就會報(bào)內(nèi)存溢出,所以一定要引起注意。
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();
}
}
}到此這篇關(guān)于Bitmap引起的OOM問題的文章就介紹到這了,更多相關(guān)Bitmap OOM內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android 自定義組件成JAR包的實(shí)現(xiàn)方法
這篇文章主要介紹了Android 自定義組件成JAR包的實(shí)現(xiàn)方法的相關(guān)資料,偶爾會用到這樣的功能,如果你自己自定義的組件很好,需要的朋友可以參考下2016-11-11
Android 創(chuàng)建依賴庫的方法(保姆級教程)
這篇文章主要介紹了Android 創(chuàng)建依賴庫的方法(保姆級教程),本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
安卓Android6.0權(quán)限動態(tài)獲取操作示例
這篇文章主要介紹了安卓Android6.0權(quán)限動態(tài)獲取操作,結(jié)合實(shí)例形式分析了Android6.0針對權(quán)限的動態(tài)獲取、授權(quán)等相關(guān)操作技巧,需要的朋友可以參考下2018-02-02
Android ImageView的selector效果實(shí)例詳解
這篇文章主要介紹了Android ImageView的selector效果實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-07-07
Android使用http請求手機(jī)號碼歸屬地查詢代碼分享
這篇文章主要介紹了Android使用http請求手機(jī)號碼歸屬地查詢代碼分享的相關(guān)資料,需要的朋友可以參考下2016-06-06
Android實(shí)現(xiàn)左右滑動效果的方法詳解
本篇文章是對Android實(shí)現(xiàn)左右滑動效果的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
Android View的事件分發(fā)機(jī)制深入分析講解
事件分發(fā)從手指觸摸屏幕開始,即產(chǎn)生了觸摸信息,被底層系統(tǒng)捕獲后會傳遞給Android的輸入系統(tǒng)服務(wù)IMS,通過Binder把消息發(fā)送到activity,activity會通過phoneWindow、DecorView最終發(fā)送給ViewGroup。這里就直接分析ViewGroup的事件分發(fā)2023-01-01

