Android 內存淺析【泄漏、溢出】【二】

內存溢出之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)); // 此處加入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();   
	} 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章