Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android中播放聲音

Android中播放聲音

編輯:關於Android編程

在Android系統中,有兩種播放聲音的方式,一種是通過MediaPlayer,另外一種是通過SoundPool。前者主要用於播放長時間的音樂,而後者用於播放小段小段的音效,像按鍵音這種,其優點是資源占用了小,同時能夠載入多個聲音片段,再根據需要選擇播放。下面分別介紹這兩種方式:

1、MediaPlayer

MediaPlayer有兩種創建方式,方式一:

MediaPlayer mp = new MediaPlayer()
通過這種方式創建MediaPlayer對象,必須在創建對象之後使用下面的語句:
mp.setDataSource("filePath");
mp.prepare();
然後就是調用start()方法。注意使用這種方法設置播放的音樂的時候需要使用的是路徑,這樣對於一般的應用使用起來就不是很方便,不能直接打包到apk中。
方式二:
mp = MediaPlayer.create(this, R.raw.music);
通過這種方式創建MediaPlayer對象,就不需要setDataSource()和prepare()了,直接start()就好了。

當需要停止播放音樂的時候,使用下面的方式:

if(mp.isPlaying()){
	mp.stop();
}
mp.reset();
需要注意的是,在stop之前一定要確認mp正在播放,因為如果已經停止播放了,再次stop會引發錯誤的,原因就是MediaPlayer有一個嚴格的生命周期,在錯誤的時期調用錯誤的方法,是一定會報錯的。

2、SoundPool

SoundPool在播放音效的時候特別的好用,為什麼呢?因為它可以一次load較多的聲音片段。下面首先介紹其基本的使用方法:

//創建
SoundPool sp = new SoundPool(10, StreamType.MUSIC, 5);
//載入
soundPoolMap = new HashMap();
soundPoolMap.put(0, soundPool.load(context, R.raw.sound1, 1));
//播放
soundPool.play(soundPoolMap.get(0), 1, 1, 0, 0, 1);
各個函數中參數的具體意義不是重點,這裡就不做介紹了,各位自己去查一下。我想要說的是,如果各位在主線程中直接這麼做,很可能會報錯(Sample X Not Ready),這是為什麼呢?

原來,SoundPool.load()這個函數是立即返回的,也就是說,不管載入好了沒有,這個函數都會返回,但是實際上非常有可能聲音尚未載入結束,那應該怎麼做呢?實際上,系統在load完成之後,會發送廣播,這時候才能播放。又因為這是一個費時的操作,所以最好新建一個線程來實現。下面是我的實現方案:

Handler handler;
static SoundPool soundPool;
static HashMap soundPoolMap;
String TAG = "wtianok";

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_main);

	if (savedInstanceState == null) {
		getSupportFragmentManager().beginTransaction()
				.add(R.id.container, new PlaceholderFragment()).commit();
	}

	soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 5);
	soundPoolMap = new HashMap();

	// 載入soundPool
	new LoadSoundPoolThread().run(this);

	handler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			if (msg.what == MessageType.SOUND_POOL_READY.ordinal()) {
				Log.d(TAG, "finish load message received");
				//do somethind
			}
		}
	}
}

private class LoadSoundPoolThread extends Thread {
	public void run(Context context) {
		Log.d(TAG, "start to load soundpool");
		soundPool.setOnLoadCompleteListener(new MyOnLoadCompleteListener());
		soundPool.load(context, R.raw.sound1, 1);
		soundPool.load(context, R.raw.sound2, 1);
		soundPool.load(context, R.raw.sound3, 1);
		soundPool.load(context, R.raw.sound4, 1);
		soundPool.load(context, R.raw.sound5, 1);
		soundPool.load(context, R.raw.sound6, 1);
		soundPool.load(context, R.raw.sound7, 1);
	}

	private class MyOnLoadCompleteListener implements
			OnLoadCompleteListener {
		int count = 1;

		@Override
		public void onLoadComplete(SoundPool soundPool, int sampleId,
				int status) { //注意這裡的形參
			Log.d(TAG, "load " + count + " sound");
			soundPoolMap.put(count, sampleId);
			if (count == 7) {
				Log.d(TAG, "finish load soundpool");
				MainActivity.soundPool = soundPool;
				sendMsg(MessageType.SOUND_POOL_READY);
				count = 1;
			}
			count++;
		}
	}
}

private void sendMsg(MessageType micTestStartRecord) {
	Message message = Message.obtain();
	message.what = micTestStartRecord.ordinal();
	handler.sendMessage(message);
}

需要注意的是,在完成載入之後,我使用了下面這一句:
MainActivity.soundPool = soundPool;
如果不加這一句,我在主線程中play的時候,依然會報Sample X Not Ready錯誤,而如果在收到廣播之後調用play是沒有問題的。仔細看,發現在接收廣播的onLoadComplete()函數中,實際上調用的是函數的局部參數soundPool,而不是全局的soundPool,但是從代碼來看,load的時候調用的確實全局的soundPool,那為什麼用全局的soundPool就不能播放呢?我到現在還是沒有想通,但是項目還是要繼續,只好加了上面那一句,將全局的soundPool重新賦值,事實證明,這樣做可以。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved