Android 音樂(音效)播放方式總結


一、音效的分類

音效按照作用的不同,可以將音效分為即時音效和背景音樂。兩種音效在Android中的實現技術是不同的。
主要的實現方式為:SoundPool、MediaPlayer。
區別在於,MediaPlayer會在播放音頻的時候,會占用大量的系統資源,並且播放的時候,還需要緩沖,有較大的時延。但是SoundPool的機制是將聲音資源加載到內存中,然后在需要播放的地方進行播放,幾乎沒有時延,但是也正是因為這樣的機制,限制了加載的文件的大小,不然會出現加載失敗或者內存占用過大的情況,原則上SoundPool播放的音效的長度不應該超過7S。

所有如果需要實時播放短時間音效的話,可以使用SoundPool,如果需要播放背景音樂的話,建議使用MediaPlayer。

二、即時音效的播放—SoundPool

相關API文檔地址:https://developer.android.com/reference/android/media/SoundPool.html

具體實現代碼為:

public class SampleActivity extends Activity {
	SoundPool sp; // 聲明SoundPool的引用
	HashMap<Integer, Integer> hm; // 聲明一個HashMap來存放聲音文件
	int currStreamId;// 當前正播放的streamId

	@Override
	// 重寫onCreate方法
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main); // 設置layout
		initSoundPool(); // 初始化聲音池的方法
		Button b1 = (Button) this.findViewById(R.id.Button01); // 獲取播放按鈕
		b1.setOnClickListener // 為播放按鈕添加監聽器
		(new OnClickListener() {
			@Override
			public void onClick(View v) {
				playSound(1, 0); // 播放1號聲音資源,且播放一次
				// 提示播放即時音效
				Toast.makeText(getBaseContext(), "播放即時音效", Toast.LENGTH_SHORT)
						.show();
			}
		});
		Button b2 = (Button) this.findViewById(R.id.Button02); // 獲取停止按鈕
		b2.setOnClickListener // 為停止按鈕添加監聽器
		(new OnClickListener() {
			@Override
			public void onClick(View v) {
				sp.stop(currStreamId); // 停止正在播放的某個聲音
				// 提示停止播放
				Toast.makeText(getBaseContext(), "停止播放即時音效", Toast.LENGTH_SHORT)
						.show();
			}
		});
	}

	// 初始化聲音池的方法
	public void initSoundPool() {
		sp = new SoundPool(4, AudioManager.STREAM_MUSIC, 0); // 創建SoundPool對象
		hm = new HashMap<Integer, Integer>(); // 創建HashMap對象
		hm.put(1, sp.load(this, R.raw.musictest, 1)); // 加載聲音文件musictest並且設置為1號聲音放入hm中
	}

	// 播放聲音的方法
	public void playSound(int sound, int loop) { // 獲取AudioManager引用
		AudioManager am = (AudioManager) this
				.getSystemService(Context.AUDIO_SERVICE);
		// 獲取當前音量
		float streamVolumeCurrent = am
				.getStreamVolume(AudioManager.STREAM_MUSIC);
		// 獲取系統最大音量
		float streamVolumeMax = am
				.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
		// 計算得到播放音量
		float volume = streamVolumeCurrent / streamVolumeMax;
		// 調用SoundPool的play方法來播放聲音文件
		currStreamId = sp.play(hm.get(sound), volume, volume, 1, loop, 1.0f);
	}
}

三、背景音效的播放—MediaPlayer

因為SoundPool只適合播放不大於7秒的音效文件,限制較大。背景音樂一般用MediaPlayer來播放。想要很好的用MediaPlayer進行音/視頻的播放,首先需要熟悉MediaPlayer的生命周期。這樣不僅僅有利於開發人員開發出更加合理的代碼,而且可以達到充分利用系統資源的目的。

MediaPlayer的生命周期

MediaPlayer的生命周期包括10種狀態,每種狀態下可以調用相應的方法來實現音/視頻文件的管理或播放。其各個狀態及狀態間的關系可以用一個簡單的流程圖來表示,如圖所示:

Idle 狀態

使用new方法創建一個MediaPlayer對象或者調用了其reset方法時,改MediaPlayer對象處於Idle 狀態。
但是通過兩種方式進入的idle狀態還是有些區別的,主要體現為:如果這個狀態下調用了getDuration等方法,若通過reset進入idle狀態的話會出發onErrorListener.onError,並且MediaPlayer會進入Error狀態。如果是新創建的MediaPlayer對象,則不會觸發onError,也不會進入Error狀態

End 狀態

通過release方法可以進入End狀態,只要MediaPlayer對象不再被使用了,就應當盡快將其通過release方法釋放掉,以釋放其占用的軟、硬件資源,這其中有些資源是互斥的(相當於臨界資源)。如果MediaPlayer進入了End狀態,則不會再進入其他的任何狀態了。

Initialized 狀態

這個狀態比較簡單,MediaPlayer調 用 setDataSource方法就進入了 Initialized狀態,表示此時要播放的文件已經設置好了。

Prepared 狀態

初始化完成之后還需要通過調用prepare或 prepareAsync方法進行准備,這兩個方法一個是同 步的,一個是異步的。只有進入了 Prepared狀態,才表明MediaPlayer到目前為止都工作正常,可以進行音樂文件的播放。

Preparing 狀態

這個狀態比較容易理解,主要是與prepareAsync異步准備方法配合,如果異步准備完成,會觸發OnPreparedListener.onPrepared,進而進入Prepared狀態。

Started 狀態

MediaPlayer准備完成后,通過調用start方法,將進入Started狀態。所謂Started狀態 ,也就是播放中狀態,開發中可以使用isPlaying方法測試MediaPlayer是否處於Started狀態。 如果播放完畢,而又設置了循環播放,則 MediaPlayer仍然會處於Started狀態。類似的,如果在該狀態下MediaPlayer調用了 seekTo或 者 start方法均可以讓MediaPlayer停 留 在 Started狀態。

Pause 狀態

Started狀態下調用pause方法可以暫停播放,從而進入到Pause狀態。MediaPlayer暫停后再次調用start方法則可以繼續進行播放,並轉到Started狀態。暫停狀態可以調用seekTo方法,這是不會改變狀態的。

Stop狀態

Started或Paused狀態下均可以調用stop方法停止播放並進入Stop狀態,而處於Stop狀態的MediaPlayer想要重新播放,需要通過調用prepareAsync或prepare方法返回到先前的Prepared狀態重新開始才可以。

Play backCompleted 狀態

文件正常播放完畢,而又沒有設置循環播放的話就進入該狀態,並會觸發OnCompletionListener 接口中的onCompletion方法。此時可以調用start方法重新從頭播放文件,也可以調用stop方法停 止播放,或者調用seekTo方法來重新定位播放位置。

Error狀態

由於某種原因 MediaPlayer出現了錯誤,則會觸發OnErrorListener.onError回調方法,此時MediaPlayer即進入Error狀態。及時捕捉並妥善處理這些錯誤是很重要的,這可以幫助應用程序及 時釋放相關的軟、硬件資源,也可以改善用戶體驗。如 果 MediaPlayer進入了 Error狀態,可以通過調用reset方法來恢復,使得 MediaPlayer重新返 回 到 Idle狀態。

總結

從上述對生命周期的總結介紹可以看出,某些情況發生的時候,MediaPlayer會回調特定監聽接口中的事件處理方法。如果在開發中希望使用回調,則需要先向MediaPlayer注冊實現了指定監聽接口的監聽器。

引申

從上述也可以看到,Ijkplayer和這個在方法名定義上,非常類似,目前看,生命周期這塊的控制基本上也是和MediaPlayer一樣的。或許這也就是為什么,現在播放器大家用Ijkplayer這么多的原因了。

四、音量控制—AudioManager

AudioManager類在Android系統中主要用來進行音/視頻播放時的音量控制,使用時的基本步驟如下所列。

  • 首先可以調用Activity對象的getSystemService(Context.AUDIO_SERVICE)方法獲取AudioManager對象。
  • 然后再調用AudioManager類中的相關方法進行音量控制。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM