可以說出現OutOfMemory問題的絕大多數人,都是因爲Bitmap的問題。因爲Bitmap佔用的內存實在是太多了,它是一個“超級大胖子”,特別是分辨率大的圖片,如果要顯示多張那問題就更顯著了。
如何解決Bitmap帶給我們的內存問題?
第一、及時銷燬。
雖然,系統能夠確認Bitmap分配的內存最終會被銷燬,但是由於它佔用的內存過多,所以很可能會超過java堆的限制。因此,在用完Bitmap時,要及時的recycle掉。recycle並不能確定立即就會將Bitmap釋放掉,但是會給虛擬機一個暗示:“該圖片可以釋放了”。所以Bitmap對象在不使用時,我們應該先調用recycle()釋放內存,然後才它設置爲null. 雖然recycle()從源碼上看,調用它應該能立即釋放Bitmap的主要內存,但是測試結果顯示它並沒能立即釋放內存。
if(!bitmap.isRecycled()){
bitmap.recycle();
bitmap = null;
}
第二、設置一定的採樣率。手機就那麼屁大的屏幕,所以有時候我們要顯示的區域很小,沒有必要將整個圖片都加載出來,而只需要記載一個縮小過的圖片,這時候可以設置一定的採樣率,那麼就可以大大減小佔用的內存。如下面的代碼:
public ImageView iv ;
public void example() {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;// 圖片寬高都爲原來的二分之一,即圖片爲原來的四分之一。數值越大圖片越模糊.你懂的
//Uri u = Uri.parse( "content://media/internal/audio/media/81" );
// Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(Uri.parse(uriString)), null, options);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher, options);
iv.setImageBitmap(bitmap);
}
第三、巧妙的運用軟引用(SoftRefrence)有些時候,我們使用Bitmap後沒有保留對它的引用,因此就無法調用Recycle函數。這時候巧妙的運用軟引用,可以使Bitmap在內存快不足時得到有效的釋放。
內存溢出之Adapter
構造 Adapter時,沒有使用緩存的 convertView
以構造ListView的 BaseAdapter 爲例,在BaseAdapter 中提共了方public View getView(int position, View convertView, ViewGroup pare來向ListView提供每一個item所需要的view對象。初始時ListBaseAdapter 中根據當前的屏幕布局實例化一定數量的view對象,同些view對象緩存起來。當向上滾動ListView時,原先位於最上面的象會被回收,然後被用來構造新出現的最下面的list item。這個構造過程方法完成的,getView()的第二個形參 View convertView就是被緩存起view對象(初始化時緩存中沒有view對象則convertView是null)。
由此可以看出,如果我們不去使用convertView,而是每次都在getView()中重新實例化一個View對象的話,即浪費時間,也造成內存垃圾,給垃圾回收增加壓力,如果垃圾回收來不及的話,虛擬機將不得不給該應用進程分配更多的內存,造成不必要的內存開支。構造 Adapter時,沒有使用緩存的 convertView
以構造ListView的 BaseAdapter 爲例,在BaseAdapter 中提共了方法:
public View getView(int position, View convertView, ViewGroup parent) 來向ListView提供每一個item所需要的view對象。初始時ListView會從BaseAdapter 中根據當前的屏幕布局實例化一定數量的view對象,同時ListView會將這些view對象緩存起來。當向上滾動ListView時,原先位於最上面的list item的view對象會被回收,然後被用來構造新出現的最下面的list item。這個構造過程就是由getView()方法完成的,getView()的第二個形參 View convertView就是被緩存起來的list item的view對象(初始化時緩存中沒有view對象則convertView是null)。
所以綜合以上的理論,寫了一個小例子,表達一下:
/**
*
******************************************
* @author 老牛比
* 文件名稱 : ExampleAdapter.java
* 創建時間 : 2012-10-1 下午11:11:28
* 文件描述 : 優化小例子
******************************************
*/
public class ExampleAdapter extends BaseAdapter {
private ArrayList<SoftReference<Bitmap>> mBitmaps = new ArrayList<SoftReference<Bitmap>>();
private ArrayList<Object> mValues;
private LayoutInflater mInflater;
public ExampleAdapter(ArrayList<SoftReference<Bitmap>> mBitmaps,
ArrayList<Object> mValues, Context mContext,
LayoutInflater mInflater) {
super();
this.mValues = mValues;
this.mInflater = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public int getCount() {
return mValues.size();
}
public Object getItem(int position) {
return mValues.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
Bitmap bitmap = BitmapFactory.decodeFile(mValues.get(position).XXX);
mBitmaps.add(new SoftReference<Bitmap>(bitmap)); // 此處加入ArrayList
if (convertView == null) {
convertView = mInflater.inflate(R.layout.example, false);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.text.setText("xxx");
holder.icon.setImageBitmap(bitmap);
return convertView;
}
/**
*
******************************************
* @author 老牛比
* 文件名稱 : ViewHolder.java
* 創建時間 : 2012-10-1 下午10:35:25
* 文件描述 : 使用 ViewHolder 模式, 效率再提高 50%
******************************************
*/
class ViewHolder {
TextView text;
ImageView icon;
}
}
內存溢出之Cursor
Cursor是Android查詢數據後得到的一個管理數據集合的類,正常情況下,操作小數據查詢時不會有內存問題,而且虛擬機能夠保證Cusor最終會被釋放掉。 然而如果Cursor的數據量特別大,應該保證Cursor佔用的內存被及時的釋放掉,而不是等待GC來處理。並且Android 文檔中提倡開發者手動的關閉Cursor。
所以我們最好這樣使用Cursor:
public void example() {
Cursor mCursor = null;
try {
mCursor = mContext.getContentResolver().query(uri, null, null,
null, null);
if (mCursor != null) {
mCursor.moveToFirst();
// TODO:do something
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mCursor != null) {
mCursor.close();
}
}
}
但在CursorAdapter中應用的情況下,必須注意,CursorAdapter在Acivity結束時並沒有自動的將Cursor關閉掉,因此,你需要在onDestroy函數中,手動關閉:
@Override
protected void onDestroy() {
if (mAdapter != null && mAdapter.getCurosr() != null) {
mAdapter.getCursor().close();
}
super.onDestroy();
}