Training—Managing Audio Playback

阅读:https://developer.android.com/training/managing-audio/index.html

 

系统将音频流分为了很多种:stream for playing music, alarms, notifications, the incoming call ringer, system sounds, in-call volume, and DTMF tones.

默认情况下,音量调整按钮控制当前正在活动的音频流。

setVolumeControlStream(AudioManager.STREAM_MUSIC);

使用上面的方法,就能让物理按键对当前声明的音频流的声音大小进行控制。

用户的一些暂停播放等操作,系统就会发出包含 ACTION_MEDIA_BUTTON 的intent,如果需要对其进行操作,那么就需要:

<receiver android:name=".RemoteControlReceiver">
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_BUTTON" />
    </intent-filter>
</receiver>

The receiver implementation itself needs to extract which key was pressed to cause the broadcast. The Intent includes this under the EXTRA_KEY_EVENT key, while the KeyEvent class includes a list KEYCODE_MEDIA_* static constants that represents each of the possible media buttons, such as KEYCODE_MEDIA_PLAY_PAUSE and KEYCODE_MEDIA_NEXT.

  在传来的intent里,包含着EXTRA_KEY_EVENT,可以从这里获取KeyEvent,也就是用户点击的时间,例如 KEYCODE_MEDIA_PLAY_PAUSE and KEYCODE_MEDIA_NEXT.

public class RemoteControlReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
            KeyEvent event = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
            if (KeyEvent.KEYCODE_MEDIA_PLAY == event.getKeyCode()) {
                // Handle key press.
            }
        }
    }
}

Because multiple applications might want to listen for media button presses, you must also programmatically control when your app should receive media button press events.

The following code can be used within your app to register and de-register your media button event receiver using the AudioManager. When registered, your broadcast receiver is the exclusive receiver of all media button broadcasts.

AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
...

// Start listening for button presses
am.registerMediaButtonEventReceiver(RemoteControlReceiver);
...

// Stop listening for button presses
am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);

通过上面的方法,就能自己控制所有的按钮响应了。

 

 在安卓系统中,为了避免多个APP同时播放音乐,因此有了“audio focus”的概念,只有获得audio focus的APP才应该播放音乐

 You must specify which stream you're using and whether you expect to require transient or permanent audio focus. Request transient focus when you expect to play audio for only a short time (for example when playing navigation instructions). Request permanent audio focus when you plan to play audio for the foreseeable future (for example, when playing music).

  有两种focus,transient or permanent audio focus。顾名思义,一个针对事件短暂的,例如短信通知声,另一种是长久性的,例如播放音乐。

AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
...

// Request audio focus for playback
int result = am.requestAudioFocus(afChangeListener,
                                 // Use the music stream.
                                 AudioManager.STREAM_MUSIC,
                                 // Request permanent focus.
                                 AudioManager.AUDIOFOCUS_GAIN);
   
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
    // Start playback.
}

上面代码展示了如何获得音乐的focus,如果要进行音乐播放,播放之前必须获得focus!

// Abandon audio focus when playback complete    
am.abandonAudioFocus(afChangeListener);

Once you've finished playback be sure to call abandonAudioFocus(). This notifies the system that you no longer require focus and unregisters the associated AudioManager.OnAudioFocusChangeListener. In the case of abandoning transient focus, this allows any interupted app to continue playback.

播放结束记得通知系统你播放完了,并且记得unregisters the associated AudioManager.OnAudioFocusChangeListener

能通过:

                             AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);

获得duck,duck似乎是一个,当其他APP获得transient audio focus的时候,音乐继续播放,不过很小声。

对于focus的loss我们同样需要进行处理,下面的代码展示了如何根据其他APP的FOCUS请求来进行相应的操作,一般来说,如果是因为其他APP的transient audio focus而失去焦点,那么应该会重获焦点,如果是permanent audio focus 那我们应该停止很多操作:

OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
    public void onAudioFocusChange(int focusChange) {
        if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT
            // Pause playback
        } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
            // Resume playback 
        } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
            am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
            am.abandonAudioFocus(afChangeListener);
            // Stop playback
        }
    }
};

还可以手动使用DUCK:

OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
    public void onAudioFocusChange(int focusChange) {
        if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
            // Lower the volume
        } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
            // Raise it back to normal
        }
    }
};

可通过AudioManage来对当前的输出设配进行检测:

if (isBluetoothA2dpOn()) {
    // Adjust output for Bluetooth.
} else if (isSpeakerphoneOn()) {
    // Adjust output for Speakerphone.
} else if (isWiredHeadsetOn()) {
    // Adjust output for headsets
} else { 
    // If audio plays and noone can hear it, is it still playing?
}

官方很贴心,它想到了一种情景:当输出设备切换时的情况,有一种是因为蓝牙失去连接,而这个时候如果你播放音乐,有可能是一种噪声,好在这种时候系统会发出广播:

private class NoisyAudioStreamReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
            // Pause the playback
        }
    }
}

private IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);

private void startPlayback() {
    registerReceiver(myNoisyAudioStreamReceiver(), intentFilter);
}

private void stopPlayback() {
    unregisterReceiver(myNoisyAudioStreamReceiver);
}

 

 

 

 

 

posted @ 2013-11-04 20:32  yutoulck  阅读(291)  评论(0编辑  收藏  举报