編輯:關於Android編程
標簽(空格分隔): Android基礎入門教程
上節,我們已經學習了Bitmap的基本用法,而本節我們要來探討的Bitmap的OOM問題,
大家在實際開發中可能遇到過,或者沒遇到過因為Bitmap引起的OOM問題,本節我們
就來圍繞這個話題來進行學習~了解什麼是OOM,為什麼會引起OOM,改善因Bitmap引起的
OOM問題~
答:Out Of Memory(內存溢出),我們都知道Android系統會為每個APP分配一個獨立的工作空間,
或者說分配一個單獨的Dalvik虛擬機,這樣每個APP都可以獨立運行而不相互影響!而Android對於每個
Dalvik虛擬機都會有一個最大內存限制,如果當前占用的內存加上我們申請的內存資源超過了這個限制
,系統就會拋出OOM錯誤!另外,這裡別和RAM混淆了,即時當前RAM中剩余的內存有1G多,但是OOM還是會發生!別把RAM(物理內存)和OOM扯到一起!另外RAM不足的話,就是殺應用了,而不是僅僅是OOM了!
而這個Dalvik中的最大內存標准,不同的機型是不一樣的,可以調用:
ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
Log.e(HEHE,最大內存: + activityManager.getMemoryClass());
>
獲得正常的最大內存標准,又或者直接在命令行鍵入:
adb shell getprop | grep dalvik.vm.heapgrowthlimit
你也可以打開系統源碼/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
我們關注的地方有三個:heapstartsize堆內存的初始大小,heapgrowthlimit標准的應用的最大堆
內存大小,heapsize則是設置了使用android:largeHeap的應用的最大堆內存大小!
我這裡試了下手頭幾個機型的正常最大內存分配標准:
你也可以試試自己手頭的機子~
好啦,不扯了,關於OOM問題的產生,就扯到這裡,再扯就到內存管理那一塊了,可是個大塊頭,
現在還啃不動…下面我們來看下避免Bitmap OOM的一些技巧吧!
上一節說了BitmapFactory.Options這個類,我們可以設置下其中的inPreferredConfig屬性,
默認是Bitmap.Config.ARGB_8888,我們可以修改成Bitmap.Config.ARGB_4444
Bitmap.Config ARGB_4444:每個像素占四位,即A=4,R=4,G=4,B=4,那麼一個像素點占4+4+4+4=16位
Bitmap.Config ARGB_8888:每個像素占八位,即A=8,R=8,G=8,B=8,那麼一個像素點占8+8+8+8=32位
默認使用ARGB_8888,即一個像素占4個字節!
同樣是BitmapFactory.Options,我們通過inSampleSize設置縮放倍數,比如寫2,即長寬變為原來的1/2,圖片就是原來的1/4,如果不進行縮放的話設置為1即可!但是不能一味的壓縮,畢竟這個值太小
的話,圖片會很模糊,而且要避免圖片的拉伸變形,所以需要我們在程序中動態的計算,這個
inSampleSize的合適值,而Options中又有這樣一個方法:inJustDecodeBounds,將該參數設置為
true後,decodeFiel並不會分配內存空間,但是可以計算出原始圖片的長寬,調用
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; // 設置了此屬性一定要記得將值設置為false
Bitmap bitmap = null;
bitmap = BitmapFactory.decodeFile(url, options);
options.inSampleSize = computeSampleSize(options,128,128);
options.inPreferredConfig = Bitmap.Config.ARGB_4444;
/* 下面兩個字段需要組合使用 */
options.inPurgeable = true;
options.inInputShareable = true;
options.inJustDecodeBounds = false;
try {
bitmap = BitmapFactory.decodeFile(url, options);
} catch (OutOfMemoryError e) {
Log.e(TAG, OutOfMemoryError);
}
如果引用了大量的Bitmap對象,而應用又不需要同時顯示所有圖片。可以將暫時不用到的Bitmap對象
及時回收掉。對於一些明確知道圖片使用情況的場景可以主動recycle回收,比如引導頁的圖片,使用
完就recycle,幀動畫,加載一張,畫一張,釋放一張!使用時加載,不顯示時直接置null或recycle!
比如:imageView.setImageResource(0);
不過某些情況下會出現特定圖片反復加載,釋放,再加載等,低效率的事情…
下面這些方法,我並沒有用過,大家可以自行查閱相關資料:
建個SoftReference的hashmap
使用圖片時先查詢這個hashmap是否有softreference, softreference裡的圖片是否為空,
如果為空就加載圖片到softreference並加入hashmap。
無需再代碼裡顯式的處理圖片的回收與釋放,gc會自動處理資源的釋放。
這種方式處理起來簡單實用,能一定程度上避免前一種方法反復加載釋放的低效率。但還不夠優化。
示例代碼:
private Map> imageMap
= new HashMap>();
public Bitmap loadBitmap(final String imageUrl,final ImageCallBack imageCallBack) {
SoftReference 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));
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 ;
}
// 從網上下載圖片
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);
}
Android 3.1版本起,官方還提供了LruCache來進行cache處理,當存儲Image的大小大於LruCache
設定的值,那麼近期使用次數最少的圖片就會被回收掉,系統會自動釋放內存!
使用示例:
步驟:
1)要先設置緩存圖片的內存大小,我這裡設置為手機內存的1/8,
手機內存的獲取方式:int MAXMEMONRY = (int) (Runtime.getRuntime() .maxMemory() / 1024);
2)LruCache裡面的鍵值對分別是URL和對應的圖片
3)重寫了一個叫做sizeOf的方法,返回的是圖片數量。
private LruCache mMemoryCache;
private LruCacheUtils() {
if (mMemoryCache == null)
mMemoryCache = new LruCache(
MAXMEMONRY / 8) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// 重寫此方法來衡量每張圖片的大小,默認返回圖片數量。
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)下面的方法分別是清空緩存、添加圖片到緩存、從緩存中取得圖片、從緩存中移除。
移除和清除緩存是必須要做的事,因為圖片緩存處理不當就會報內存溢出,所以一定要引起注意。
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();
}
}
}
上述內容摘自——圖片緩存之內存緩存技術LruCache,軟引用
本節給大家講解了OOM問題的發生緣由,也總結了一下網上給出的一些避免因Bitmap而引起OOM
的一些方案,因為公司做的APP都是地圖類的,很少涉及到圖片,所以筆者並沒有遇到過OOM的問題,
所以對此並不怎麼熟悉~後續在進階課程的內存管理,我們再慢慢糾結這個OOM的問題,好的,
本節就到這裡,謝謝~
android 系統集成了一個輕量級的數據庫,SQLite只是一個嵌入式的數據庫引擎;android提供SQLiteDatabase代表一個數據庫,一旦應用程序獲得了SQ
一、前言最近在學習HTML5相關的知識,發現前端技術的功能越來越強大了,很多功能如果我們通過原生代碼的形式進行實現的話相對於H5會花費數倍的時間,在最求快速迭代的時候是不
今天在維護公司的一個APP的時候,有如下場景。彈出一個AlertDialog的時候,在系統語言是中文的時候,如下所示:彈出一個AlertDialog的時候,在系統語言是E
在Android系統中,一個Activity對應一個應用程序窗口,任何一個Activity的啟動都是由AMS服務和應用程序進程相互配合來完成的。AMS服務統一調度系統中所