記憶體溢出之Bitmap
可以說出現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)); // 此處加入ArrayListif (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(); }