关于音频焦点的运用
引子
說 Audio Focus 前先說個(gè)很簡(jiǎn)單需求:來電時(shí)暫停正在播放的音樂,電話結(jié)束時(shí)恢復(fù)播放。
看到這個(gè)需求,第一反應(yīng)肯定是:監(jiān)聽用戶來電狀態(tài),作相應(yīng)操作。這里不多做介紹,這樣做有個(gè)不好的地方就是需要隱私權(quán)限!這樣做一點(diǎn)也不優(yōu)雅。
后來搜索時(shí)看到一篇分析文章:Android來電時(shí)停止音樂播放的流程(順便說一嘴,這篇轉(zhuǎn)載居然不注明出處!!)。文章里的分析很明確的指出,系統(tǒng)在框架層就很好的幫我們處理了這個(gè)需求,問題是如何將音樂交給系統(tǒng)框架來處理呢?
音頻焦點(diǎn)
問題的解決方法就是:請(qǐng)求系統(tǒng)的音頻焦點(diǎn)(Request the Audio Focus)。
如果英文還行,強(qiáng)烈建議請(qǐng)看官方的原文:Managing Audio Playback,里面介紹的很清楚。以下為簡(jiǎn)單概述。
官方文檔指出Android?在處理音頻播放是分了多個(gè)“音頻流”的,如音樂流、音效流、電話聲音流等,使控制音量時(shí)可以互不干涉。多數(shù)情況下我們播放音樂都是使用?STREAM_MUSIC?音頻流。
另外,系統(tǒng)中可能會(huì)有多個(gè)應(yīng)用程序會(huì)播放音頻,所以需要考慮他們之間該如何協(xié)調(diào),為了避免同時(shí)播放音樂,Android 系統(tǒng)使用音頻焦點(diǎn)來進(jìn)行統(tǒng)一管理,即只有獲得了音頻焦點(diǎn)的應(yīng)用程序才可以播放音樂。
那么,播放音頻應(yīng)該這樣來做:
如此便可以完美的解決引子里的需求。
一個(gè)簡(jiǎn)單的示例
MusicService.java| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | public class MusicService extends Service { private AudioManager mAm; private boolean isPlaymusic; private String url; private MediaPlayer mediaPlayer; @Override public void onCreate() { super.onCreate(); mAm = (AudioManager) getSystemService(AUDIO_SERVICE); } @Override public void onStart(Intent intent, int startId) { if (intent != null) { Bundle bundle = intent.getExtras(); if (bundle != null) { isPlaymusic = bundle.getBoolean("isPlay", true); url = bundle.getString("url"); if (isPlaymusic) play(); else stop(); } } } OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() { public void onAudioFocusChange(int focusChange) { if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) { // Pause playback pause(); } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) { // Resume playback resume(); } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) { // mAm.unregisterMediaButtonEventReceiver(RemoteControlReceiver); mAm.abandonAudioFocus(afChangeListener); // Stop playback stop(); } } }; private boolean requestFocus() { // Request audio focus for playback int result = mAm.requestAudioFocus(afChangeListener, // Use the music stream. AudioManager.STREAM_MUSIC, // Request permanent focus. AudioManager.AUDIOFOCUS_GAIN); return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED; } private void resume() { if (mediaPlayer != null) { mediaPlayer.start(); } } private void pause() { if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.pause(); } } OnCompletionListener completionListener = new OnCompletionListener() { @Override public void onCompletion(MediaPlayer player) { if(!player.isLooping()){ mAm.abandonAudioFocus(afChangeListener); } } }; private void play() { if (requestFocus()) { if (mediaPlayer == null) { try { mediaPlayer = new MediaPlayer(); mediaPlayer.setDataSource(url); mediaPlayer.prepare(); mediaPlayer.setOnCompletionListener(completionListener); } catch (IOException e) { e.printStackTrace(); } } if (!mediaPlayer.isPlaying()) { mediaPlayer.start(); } } } @Override public void onDestroy() { super.onDestroy(); if (mediaPlayer != null) mediaPlayer.release(); } private void stop() { if (mediaPlayer != null) { mediaPlayer.stop(); } } @Override public IBinder onBind(Intent arg0) { // TODO Auto-generated method stub return null; } } |
經(jīng)模擬器測(cè)試,當(dāng)來電時(shí)音頻焦點(diǎn)會(huì)給到鈴聲流,并打出日志:
I/AudioService(1235): AudioFocus requestAudioFocus() from AudioFocus_For_Phone_Ring_And_Calls此時(shí)MusicService中的afChangeListener會(huì)得到AUDIOFOCUS_LOSS_TRANSIENT,于是會(huì)暫停播放音頻。當(dāng)通話結(jié)束或者掛掉電話,afChangeListener會(huì)得到AUDIOFOCUS_GAIN,于是恢復(fù)播放音頻。
注意:
總結(jié)
- 上一篇: 关于Renderscript的理解
- 下一篇: 关于优酷开发sdkdemo的布局