android 音頻處理mediaplay介紹
處理音頻焦點(diǎn)
盡管某個(gè)時(shí)刻只有一個(gè)activity可以運(yùn)行,Android卻是一個(gè)多任務(wù)環(huán)境.這對使用音頻的應(yīng)用帶來了特殊的挑戰(zhàn),因?yàn)橹挥幸粋€(gè)音頻輸出而可能多個(gè)媒體都想用它.在Android2.2之前,沒有內(nèi)建的機(jī)制來處理這個(gè)問題,所以可能在某些情況下導(dǎo)致壞的用戶體驗(yàn).例如,當(dāng)一個(gè)用戶正在聽音樂而另一個(gè)應(yīng)用需要通知用戶一些重要的事情時(shí),用戶可能由于音樂聲音大而不能聽的通知.從Android2.2開始,平臺(tái)為應(yīng)用提供了一個(gè)協(xié)商它們?nèi)绾问褂迷O(shè)備音頻輸出的途徑,這個(gè)機(jī)制叫做音頻焦點(diǎn).
當(dāng)你的應(yīng)用需要輸出像樂音和通知之類的音頻時(shí),你應(yīng)該總是請求音頻焦點(diǎn).一旦應(yīng)用具有了焦點(diǎn),它就可以自由的使用音頻輸出.但它總是應(yīng)該監(jiān)聽焦點(diǎn)的變化.如果被通知丟失焦點(diǎn),它應(yīng)該立即殺死聲音或降低到靜音水平(有一個(gè)標(biāo)志表明應(yīng)選擇哪一個(gè))并且僅當(dāng)重新獲得焦點(diǎn)后才恢復(fù)大聲播放.
將來的音頻焦點(diǎn)是合作的.所以,應(yīng)用被希望(并被強(qiáng)列鼓勵(lì))遵守音頻焦點(diǎn)的方針,但是卻不是被系統(tǒng)強(qiáng)制的.如果一個(gè)應(yīng)用在丟失音頻焦點(diǎn)后依然想大聲播放音樂,系統(tǒng)不會(huì)去阻止它.然而用戶卻體驗(yàn)很壞并且很想把這鳥應(yīng)用卸載.
要請求音頻焦點(diǎn),你必須從AudioManager調(diào)用requestAudioFocus(),如下所示:
[java]
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
// 不能獲得音頻焦點(diǎn)
}
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
// 不能獲得音頻焦點(diǎn)
}
requestAudioFocus()的第一個(gè)參數(shù)是一個(gè)AudioManager.OnAudioFocusChangeListener,它的onAudioFocusChange()方法在音頻焦點(diǎn)發(fā)改變時(shí)被調(diào)用.因此,你也應(yīng)該在你的service和activity上實(shí)現(xiàn)此接口.例如:
[java]
class MyService extends Service
implements AudioManager.OnAudioFocusChangeListener {
// ....
public void onAudioFocusChange(int focusChange) {
// Do something based on focus change...
}
}
class MyService extends Service
implements AudioManager.OnAudioFocusChangeListener {
// ....
public void onAudioFocusChange(int focusChange) {
// Do something based on focus change...
}
}
參數(shù)focusChange告訴你音頻焦點(diǎn)如何發(fā)生了變化,它可以是以上幾種值(它們都是定義在AudioManager中的常量):
AUDIOFOCUS_GAIN:你已獲得了音頻焦點(diǎn).
AUDIOFOCUS_LOSS:你已經(jīng)丟失了音頻焦點(diǎn)比較長的時(shí)間了.你必須停止所有的音頻播放.因?yàn)轭A(yù)料到你可能很長時(shí)間也不能再獲音頻焦點(diǎn),所以這里是清理你的資源的好地方.比如,你必須釋放MediaPlayer.
AUDIOFOCUS_LOSS_TRANSIENT:你臨時(shí)性的丟掉了音頻焦點(diǎn),很快就會(huì)重新獲得.你必須停止所有的音頻播放,但是可以保留你的資源,因?yàn)槟憧赡芎芸炀湍苤匦芦@得焦點(diǎn).
AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:你臨時(shí)性的丟掉了音頻焦點(diǎn),但是你被允許繼續(xù)以低音量播放,而不是完全停止.
下面是一個(gè)例子:
[java]
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
// resume playback
if (mMediaPlayer == null) initMediaPlayer();
else if (!mMediaPlayer.isPlaying()) mMediaPlayer.start();
mMediaPlayer.setVolume(1.0f, 1.0f);
break;
case AudioManager.AUDIOFOCUS_LOSS:
// Lost focus for an unbounded amount of time: stop playback and release media player
if (mMediaPlayer.isPlaying()) mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
// Lost focus for a short time, but we have to stop
// playback. We don't release the media player because playback
// is likely to resume
if (mMediaPlayer.isPlaying()) mMediaPlayer.pause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
// Lost focus for a short time, but it's ok to keep playing
// at an attenuated level
if (mMediaPlayer.isPlaying()) mMediaPlayer.setVolume(0.1f, 0.1f);
break;
}
}
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
// resume playback
if (mMediaPlayer == null) initMediaPlayer();
else if (!mMediaPlayer.isPlaying()) mMediaPlayer.start();
mMediaPlayer.setVolume(1.0f, 1.0f);
break;
case AudioManager.AUDIOFOCUS_LOSS:
// Lost focus for an unbounded amount of time: stop playback and release media player
if (mMediaPlayer.isPlaying()) mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
// Lost focus for a short time, but we have to stop
// playback. We don't release the media player because playback
[!--empirenews.page--]// is likely to resume
if (mMediaPlayer.isPlaying()) mMediaPlayer.pause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
// Lost focus for a short time, but it's ok to keep playing
// at an attenuated level
if (mMediaPlayer.isPlaying()) mMediaPlayer.setVolume(0.1f, 0.1f);
break;
}
}
記住音頻焦點(diǎn)API僅在APIlevel 8 (Android2.2)及更高版本上可以,所以如果你想支持更早的Android版本,你必須在可能時(shí)采取兼容性的策略使用特性,如果不可能,you should adopt a backward compatibility strategy that allows you touse this feature if available, and fall back seamlessly if not.
你可以用反射的方式調(diào)用音頻焦點(diǎn)方法或自己在一個(gè)單獨(dú)的類(叫做AudioFocusHelper)中實(shí)現(xiàn)所有的音頻焦點(diǎn)功能來達(dá)到向前兼容.下面是一個(gè)這樣的類:
[java]
public class AudioFocusHelper implements AudioManager.OnAudioFocusChangeListener {
AudioManager mAudioManager;
// 這里是其它的字段,你可能要保存一個(gè)接口的引用,這個(gè)接口
// 被用于與你的service通訊以報(bào)告焦點(diǎn)的變化.
public AudioFocusHelper(Context ctx, ) {
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
// ...
}
public boolean requestFocus() {
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
mAudioManager.requestAudioFocus(mContext, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
}
public boolean abandonFocus() {
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
mAudioManager.abandonAudioFocus(this);
}
@Override
public void onAudioFocusChange(int focusChange) {
// 讓你的service知道焦點(diǎn)變化了
}
}
你可以僅在檢測到系統(tǒng)運(yùn)行的是API level 8 或更早的版本時(shí)才創(chuàng)建AudioFocusHelper類的實(shí)例.例如:
if (android.os.Build.VERSION.SDK_INT >= 8) {
mAudioFocusHelper = new AudioFocusHelper(getApplicationContext(), this);
} else {
mAudioFocusHelper = null;
}
public class AudioFocusHelper implements AudioManager.OnAudioFocusChangeListener {
AudioManager mAudioManager;
// 這里是其它的字段,你可能要保存一個(gè)接口的引用,這個(gè)接口
// 被用于與你的service通訊以報(bào)告焦點(diǎn)的變化.
public AudioFocusHelper(Context ctx, ) {
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
// ...
}
public boolean requestFocus() {
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
mAudioManager.requestAudioFocus(mContext, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
}
public boolean abandonFocus() {
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
mAudioManager.abandonAudioFocus(this);
}
@Override
public void onAudioFocusChange(int focusChange) {
// 讓你的service知道焦點(diǎn)變化了
}
}
你可以僅在檢測到系統(tǒng)運(yùn)行的是API level 8 或更早的版本時(shí)才創(chuàng)建AudioFocusHelper類的實(shí)例.例如:
if (android.os.Build.VERSION.SDK_INT >= 8) {
mAudioFocusHelper = new AudioFocusHelper(getApplicationContext(), this);
} else {
mAudioFocusHelper = null;
}
清理
前面提到過,一個(gè)MediaPlayer對象可以消耗掉大量的系統(tǒng)資源,所以你應(yīng)該僅在需要它時(shí)保持它并在用完時(shí)立即釋放.明確的調(diào)用清理方法而不是依靠系統(tǒng)的垃圾收集機(jī)制是很重要的,因?yàn)樵诒皇占癕ediaPlayer可能會(huì)存在很長時(shí)間,雖然此時(shí)它只是占用內(nèi)存而不影響其它的媒體相關(guān)的資源.所以,當(dāng)你使用一個(gè)service時(shí),你應(yīng)該總四重寫onDestroy()方法來保證釋放MediaPlayer:
[java]
public class MyService extends Service {
MediaPlayer mMediaPlayer;
// ...
@Override
public void onDestroy() {
if (mMediaPlayer != null) mMediaPlayer.release();
}
}
public class MyService extends Service {
MediaPlayer mMediaPlayer;
// ...
@Override
public void onDestroy() {
if (mMediaPlayer != null) mMediaPlayer.release();
}www.2cto.com
}
你也應(yīng)該尋找其它需要釋放你的MediaPlayer的時(shí)機(jī).例如,如果你預(yù)料到長時(shí)間不能播放媒體(比如丟掉音頻焦點(diǎn)以后),你應(yīng)該明確地釋放你的MediaPlayer,然后在后面重新創(chuàng)建它.反過來,如果你預(yù)測到只會(huì)短時(shí)間停止播放,你應(yīng)該保持你的MediaPlayer來避免過多的創(chuàng)建,而不是重新"準(zhǔn)備"它.