攀枝花网站网站建设,网站设计目标,新建网站推广给企业,网站怎么做留言区两个或两个以上的 Android 应用可同时向同一输出流播放音频。系统会将所有音频流混合在一起。虽然这是一项出色的技术#xff0c;但却会给用户带来很大的困扰。为了避免所有音乐应用同时播放#xff0c;Android 引入了“音频焦点”的概念。一次只能有一个应用获得音频焦点。当…两个或两个以上的 Android 应用可同时向同一输出流播放音频。系统会将所有音频流混合在一起。虽然这是一项出色的技术但却会给用户带来很大的困扰。为了避免所有音乐应用同时播放Android 引入了“音频焦点”的概念。一次只能有一个应用获得音频焦点。当您的应用需要输出音频时它需要请求获得音频焦点获得焦点后就可以播放声音了。不过在您获得音频焦点后您可能无法将其一直持有到播放完成。其他应用可以请求焦点从而占有您持有的音频焦点。如果发生这种情况您的应用应暂停播放或降低音量以便于用户听到新的音频源。音频焦点采用合作模式。我们建议应用遵守音频焦点准则但系统不会强制执行这些准则。如果应用想要在失去音频焦点后继续大声播放系统无法阻止它。这是一种不好的体验用户很可能会卸载具有这种不良行为的应用。行为恰当的音频应用应根据以下一般准则来管理音频焦点在即将开始播放之前调用 requestAudioFocus()并验证调用是否返回 onPlay() 回调中调用 requestAudioFocus()。在其他应用获得音频焦点时停止或暂停播放或降低音量。播放停止后放弃音频焦点。运行的 Android 版本不同音频焦点的处理方式也会不同对于以 Android 5.0(API 级别 21)及更高版本为目标平台的应用音频应用应使用面向 Android 8.0(API 级别 26)或更高版本的应用应使用 AudioFocusRequest 包含有关应用的音频上下文和功能的信息。系统使用这些信息来自动管理音频焦点的得到和失去。Android 8.0 及更高版本中的音频焦点从 Android 8.0(API 级别 26)开始当您调用 AudioFocusRequest 参数。要释放音频焦点请调用 AudioFocusRequest 作为参数。在请求和放弃焦点时应使用相同的 AudioFocusRequest 实例。要创建FocusGain 字段为必需字段所有其他字段均为可选字段。方法备注每个请求中都必须包含此字段。此字段的值与 Android 8.0 之前的 requestAudioFocus() 调用中所使用的 durationHint 值相同AUDIOFOCUS_GAIN、AUDIOFOCUS_GAIN_TRANSIENT、AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 或 AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE。首先使用如果未指定则 AudioAttributes 默认为 AudioAttributes.USAGE_MEDIA。当其他应用使用 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 请求焦点时持有焦点的应用通常不会收到 自行降低音量。如果您需要暂停播放而不是降低音量请调用 setWillPauseWhenDucked(true)然后创建并设置 OnAudioFocusChangeListener具体如自动降低音量中所述。当焦点被其他应用锁定时对音频焦点的请求可能会失败。此方法可实现延迟获取焦点即在焦点可用时异步获取焦点。请注意要使“延迟获取焦点”起作用您还必须在音频请求中指定只有当您在请求中还指定了 willPauseWhenDucked(true) 或 setAcceptsDelayedFocusGain(true) 时才需要 OnAudioFocusChangeListener。有两个方法可以设置监听器一个带处理程序参数一个不带。处理程序是运行监听器的线程。如果您未指定处理程序则会使用与主以下示例展示了如何使用 AudioFocusRequest.Builder 构建 AudioFocusRequest 来请求和放弃音频焦点KotlinaudioManager getSystemService(Context.AUDIO_SERVICE) as AudioManagerfocusRequest AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).run {setAudioAttributes(AudioAttributes.Builder().run {setUsage(AudioAttributes.USAGE_GAME)setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)build()})setAcceptsDelayedFocusGain(true)setOnAudioFocusChangeListener(afChangeListener, handler)build()}mediaPlayer MediaPlayer()val focusLock Any()var playbackDelayed falsevar playbackNowAuthorized false// ...val res audioManager.requestAudioFocus(focusRequest)synchronized(focusLock) {playbackNowAuthorized when (res) {AudioManager.AUDIOFOCUS_REQUEST_FAILED - falseAudioManager.AUDIOFOCUS_REQUEST_GRANTED - {playbackNow()true}AudioManager.AUDIOFOCUS_REQUEST_DELAYED - {playbackDelayed truefalse}else - false}}// ...override fun onAudioFocusChange(focusChange: Int) {when (focusChange) {AudioManager.AUDIOFOCUS_GAIN -if (playbackDelayed || resumeOnFocusGain) {synchronized(focusLock) {playbackDelayed falseresumeOnFocusGain false}playbackNow()}AudioManager.AUDIOFOCUS_LOSS - {synchronized(focusLock) {resumeOnFocusGain falseplaybackDelayed false}pausePlayback()}AudioManager.AUDIOFOCUS_LOSS_TRANSIENT - {synchronized(focusLock) {resumeOnFocusGain trueplaybackDelayed false}pausePlayback()}AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK - {// ... pausing or ducking depends on your app}}}JavaaudioManager (AudioManager) Context.getSystemService(Context.AUDIO_SERVICE);playbackAttributes new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_GAME).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build();focusRequest new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).setAudioAttributes(playbackAttributes).setAcceptsDelayedFocusGain(true).setOnAudioFocusChangeListener(afChangeListener, handler).build();mediaPlayer new MediaPlayer();final Object focusLock new Object();boolean playbackDelayed false;boolean playbackNowAuthorized false;// ...int res audioManager.requestAudioFocus(focusRequest);synchronized(focusLock) {if (res AudioManager.AUDIOFOCUS_REQUEST_FAILED) {playbackNowAuthorized false;} else if (res AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {playbackNowAuthorized true;playbackNow();} else if (res AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {playbackDelayed true;playbackNowAuthorized false;}}// ...Overridepublic void onAudioFocusChange(int focusChange) {switch (focusChange) {case AudioManager.AUDIOFOCUS_GAIN:if (playbackDelayed || resumeOnFocusGain) {synchronized(focusLock) {playbackDelayed false;resumeOnFocusGain false;}playbackNow();}break;case AudioManager.AUDIOFOCUS_LOSS:synchronized(focusLock) {resumeOnFocusGain false;playbackDelayed false;}pausePlayback();break;case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:synchronized(focusLock) {resumeOnFocusGain true;playbackDelayed false;}pausePlayback();break;case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:// ... pausing or ducking depends on your appbreak;}}}自动降低音量在 Android 8.0(API 级别 26)中当其他应用使用 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 请求焦点时系统可以在不调用应用的 onAudioFocusChange() 回调的情况下降低和恢复音量。虽然自动降低音量的行为对于音乐和视频播放应用来说是可接受的但在播放语音内容时(例如在听书应用中)就没什么用处了。在这种情况下应用应该暂停播放。如果您希望应用在被要求降低音量时暂停播放请创建包含 onAudioFocusChange() 回调方法的 OnAudioFocusChangeListener该回调方法可以实现所需的暂停/恢复行为。调用延迟获取焦点在有些情况下系统不能批准对音频焦点的请求因为焦点被其他应用“锁定”了例如在通话过程中。在这种情况下requestAudioFocus() 会返回 AUDIOFOCUS_REQUEST_FAILED。在这种情况下您的应用将不会播放音频因为它未获得焦点。方法 AUDIOFOCUS_REQUEST_DELAYED。当锁定音频焦点的情况不再存在时(例如当通话结束时)系统会批准待处理的焦点请求并调用 onAudioFocusChange() 来通知您的应用。为了处理“延迟获取焦点”您必须创建包含 onAudioFocusChange() 回调方法的 OnAudioFocusChangeListener该回调方法会通过调用Android 8.0 之前的音频焦点当您调用如果您计划在可预见的将来播放音频(例如在播放音乐时)并且希望前一个持有音频焦点的应用停止播放则应该请求永久性的音频焦点 (AUDIOFOCUS_GAIN)。如果您只希望在短时间内播放音频并且希望前一个持有音频焦点的应用暂停播放则应该请求暂时性的焦点 (AUDIOFOCUS_GAIN_TRANSIENT)。请求附带“降低音量”(AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK) 的暂时性焦点表示您只希望在短时间内播放音频并允许前一个持有焦点的应用在降低其音频输出的情况下继续播放。这两个音频输出会混合到音频流中。降低音量特别适合于间歇性使用音频流的应用例如有声的行车路线。requestAudioFocus() 方法同样需要 onAudioFocusChange() 回调您的应用会在其他应用获取或放弃音频焦点时收到该回调。以下代码段会请求对 STREAM_MUSIC 流的永久性音频焦点并注册 OnAudioFocusChangeListener 来处理音频焦点的后续更改。(有关更改监听器的说明请参阅响应音频焦点更改。)KotlinaudioManager getSystemService(Context.AUDIO_SERVICE) as AudioManagerlateinit var afChangeListener AudioManager.OnAudioFocusChangeListener...// Request audio focus for playbackval result: Int audioManager.requestAudioFocus(afChangeListener,// Use the music stream.AudioManager.STREAM_MUSIC,// Request permanent focus.AudioManager.AUDIOFOCUS_GAIN)if (result AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {// Start playback}JavaAudioManager audioManager (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);AudioManager.OnAudioFocusChangeListener afChangeListener;...// Request audio focus for playbackint result audioManager.requestAudioFocus(afChangeListener,// Use the music stream.AudioManager.STREAM_MUSIC,// Request permanent focus.AudioManager.AUDIOFOCUS_GAIN);if (result AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {// Start playback}KotlinaudioManager.abandonAudioFocus(afChangeListener)Java// Abandon audio focus when playback completeaudioManager.abandonAudioFocus(afChangeListener);这会通知系统您不再需要焦点并注销关联的 OnAudioFocusChangeListener。如果您请求的是暂时性焦点则会通知已暂停或降低音量的应用它可以继续播放或恢复其音量。响应音频焦点更改当应用获得音频焦点后它必须能够在其他应用为自己请求音频焦点时释放该焦点。出现这种情况时您的应用会收到对 AudioFocusChangeListener 中的 requestAudioFocus() 时指定的。传递给 onAudioFocusChange() 的 focusChange 参数表示所发生的更改类型。它对应于获取焦点的应用所使用的持续时间提示。您的应用应该做出适当的响应。暂时性失去焦点如果焦点更改是暂时性的(AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK 或 AUDIOFOCUS_LOSS_TRANSIENT)您的应用应该降低音量(如果您不依赖于自动降低音量)或暂停播放否则保持相同的状态。在暂时性失去音频焦点时您应该继续监控音频焦点的变化并准备好在重新获得焦点后恢复正常播放。当抢占焦点的应用放弃焦点时您会收到一个回调 (AUDIOFOCUS_GAIN)。此时您可以将音量恢复到正常水平或重新开始播放。永久性失去焦点如果是永久性失去音频焦点 (AUDIOFOCUS_LOSS)则其他应用会播放音频。您的应用应立即暂停播放因为它不会收到 AUDIOFOCUS_GAIN 回调。要重新开始播放用户必须执行明确的操作例如在通知或应用界面中按播放传输控件。以下代码段展示了如何实现 OnAudioFocusChangeListener 及其 onAudioFocusChange() 回调。请注意这里使用 Handler 延迟了对永久性失去音频焦点的停止回调。Kotlinprivate val handler Handler()private val afChangeListener AudioManager.OnAudioFocusChangeListener { focusChange -when (focusChange) {AudioManager.AUDIOFOCUS_LOSS - {// Permanent loss of audio focus// Pause playback immediatelymediaController.transportControls.pause()// Wait 30 seconds before stopping playbackhandler.postDelayed(delayedStopRunnable, TimeUnit.SECONDS.toMillis(30))}AudioManager.AUDIOFOCUS_LOSS_TRANSIENT - {// Pause playback}AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK - {// Lower the volume, keep playing}AudioManager.AUDIOFOCUS_GAIN - {// Your app has been granted audio focus again// Raise volume to normal, restart playback if necessary}}}Javaprivate Handler handler new Handler();AudioManager.OnAudioFocusChangeListener afChangeListener new AudioManager.OnAudioFocusChangeListener() {public void onAudioFocusChange(int focusChange) {if (focusChange AudioManager.AUDIOFOCUS_LOSS) {// Permanent loss of audio focus// Pause playback immediatelymediaController.getTransportControls().pause();// Wait 30 seconds before stopping playbackhandler.postDelayed(delayedStopRunnable,TimeUnit.SECONDS.toMillis(30));}else if (focusChange AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {// Pause playback} else if (focusChange AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {// Lower the volume, keep playing} else if (focusChange AudioManager.AUDIOFOCUS_GAIN) {// Your app has been granted audio focus again// Raise volume to normal, restart playback if necessary}}};处理程序使用如下所示的 RunnableKotlinprivate var delayedStopRunnable Runnable {mediaController.transportControls.stop()}Javaprivate Runnable delayedStopRunnable new Runnable() {Overridepublic void run() {getMediaController().getTransportControls().stop();}};为了确保在用户重新开始播放时不会触发延迟停止请调用 mHandler.removeCallbacks(mDelayedStopRunnable) 来响应任何状态变化。例如在回调的 onPlay()、onSkipToNext() 等中调用 removeCallbacks()。此外在清理服务使用的资源时您也应该在服务的 onDestroy() 回调中调用此方法。