一、音效的分類
音效按照作用的不同,可以將音效分為即時音效和背景音樂。兩種音效在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類中的相關方法進行音量控制。