在穿戴式设备上播放音频

本指南介绍了如何在 Wear OS 应用中使用熟悉的 Android API 播放音频。

检测音频设备

Wear OS 应用首先必须检测穿戴式设备是否具有合适的音频输出方式。穿戴式设备通常具有以下至少一种音频输出方式:

  • AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:在具有内置扬声器的设备上。
  • AudioDeviceInfo.TYPE_BLUETOOTH_A2DP:如果蓝牙耳机已配对并连接。
  • AudioDeviceInfo.TYPE_BLE_BROADCAST:如果蓝牙低功耗 (BLE) 广播群组设备已配对并连接。
  • AudioDeviceInfo.TYPE_BLE_HEADSET:如果 BLE 耳机已配对并连接。
  • AudioDeviceInfo.TYPE_BLE_SPEAKER:如果 BLE 音箱已配对并连接。

以下示例将 getDevices() 方法与 FEATURE_AUDIO_OUTPUT 值搭配使用,以检查音频输出类型是否可用。

private val audioManager: AudioManager by lazy {
    getSystemService(AUDIO_SERVICE) as AudioManager
}

fun audioOutputAvailable(type: Int): Boolean {
    if (!packageManager.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
        return false
    }
    return audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS).any { it.type == type }
}

然后,您可以使用此方法检查音频输出类型是否可用。

val hasSpeaker = audioOutputAvailable(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER)
val hasBluetoothHeadset = audioOutputAvailable(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP)
val hasBLEBroadcast = audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_BROADCAST)
val hasBLEHeadset = audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_HEADSET)
val hasBLESpeaker = audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_SPEAKER)

为了提供最佳用户体验,请仅在有蓝牙耳机或音箱连接到手表时播放媒体。

选择用于输出音频的首选设备

根据应用的使用情形以及音频对应用核心体验的重要性,选择用户与应用音频输出之间的互动方式。

允许用户选择媒体输出设备

从 Wear OS 5 开始,系统提供了一个界面,让用户可以选择哪个设备播放媒体,并显示有关当前正在播放的媒体内容的信息。

如果您的应用在您想在搭载 Wear OS 5 或更高版本的设备上提供音频播放时检测到未连接蓝牙耳机,请提出将用户直接转到媒体输出切换器。在不支持媒体输出切换器的设备上,调用 ACTION_BLUETOOTH_SETTINGS intent 操作,将用户引导至系统设置中的蓝牙页面。

launchOutputSelection() 方法(GitHub 上 Horologist 库的一部分)演示了如何让用户选择媒体输出设备。

蓝牙耳机

与内置扬声器(只要设备配备便始终可用)不同,蓝牙耳机可在应用运行时配对或取消配对。如果您的应用需要使用耳机才能继续操作,请注册一个回调,以检测用户何时使用 registerAudioDeviceCallback 连接和断开蓝牙耳机:

val audioDeviceCallback =
    object : AudioDeviceCallback() {
        override fun onAudioDevicesAdded(addedDevices: Array<out AudioDeviceInfo>?) {
            super.onAudioDevicesAdded(addedDevices)
            if (audioOutputAvailable(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP) ||
                audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_BROADCAST) ||
                audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_HEADSET) ||
                audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_SPEAKER)
            ) {
                // A Bluetooth or BLE device is connected and available for playback.
            }
        }
        override fun onAudioDevicesRemoved(removedDevices: Array<out AudioDeviceInfo>?) {
            super.onAudioDevicesRemoved(removedDevices)
            if (!(audioOutputAvailable(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP)) &&
                !(audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_BROADCAST)) &&
                !(audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_HEADSET)) &&
                !(audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_SPEAKER))
            ) {
                // No Bluetooth or BLE devices are connected anymore.
            }
        }
    }

audioManager.registerAudioDeviceCallback(audioDeviceCallback, /*handler=*/ null)

如果您的应用在您想提供音频输出时检测到未连接蓝牙耳机,请不要显示错误消息,而是要提议将用户直接转到蓝牙设置,以便他们更轻松地建立连接。您可以使用 ACTION_BLUETOOTH_SETTINGS 发送 intent 来实现此目的:

fun Context.launchBluetoothSettings(closeOnConnect: Boolean = true) {
    val intent = with(Intent(Settings.ACTION_BLUETOOTH_SETTINGS)) {
        addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
        putExtra("EXTRA_CONNECTION_ONLY", true)
        if (closeOnConnect) {
            putExtra("EXTRA_CLOSE_ON_CONNECT", true)
        }
        putExtra("android.bluetooth.devicepicker.extra.FILTER_TYPE", FILTER_TYPE_AUDIO)
    }
    startActivity(intent)
}

internal const val FILTER_TYPE_AUDIO = 1

内置扬声器

大多数 Wear OS 设备都配有内置扬声器。如果您的应用提供使用声音的非媒体使用情形,请考虑使用音箱来增加互动途径。例如,配有扬声器的 Wear OS 设备可能会触发时钟闹钟或计时器闹钟,并辅以音频通知;健身应用可能会使用扬声器提供锻炼指示。

如需了解详情,请参阅 WearSpeakerSample

播放音频

检测到并选择合适的音频输出方式后,在 Wear OS 上播放音频的过程和在移动设备或其他设备上播放音频相同。如需了解详情,请参阅 MediaPlayer 概览。如果想更轻松地使用高级功能(例如在线播放和下载媒体内容),不妨使用 ExoPlayer。遵循音频应用的最佳实践,例如管理音频焦点

防止通过内置音箱意外播放媒体

媒体应用可遵循此指南,以防止应用在无意中通过内置手表音箱播放媒体。具体指南因应用使用的播放器而异。

ExoPlayer

如果您的应用使用 ExoPlayer:

  1. 在构建 ExoPlayer 实例时,调用 setSuppressPlaybackOnUnsuitableOutput(true) 方法

val exoPlayer = ExoPlayer.Builder(context)
    .setAudioAttributes(AudioAttributes.DEFAULT, true)
    .setSuppressPlaybackOnUnsuitableOutput(true)
    .build()

  1. 通过将 WearUnsuitableOutputPlaybackSuppressionResolverListener listener 注册为 ExoPlayer 实例的监听器,以响应播放禁止事件:

exoPlayer.addListener(WearUnsuitableOutputPlaybackSuppressionResolverListener(context))

Horologist Media 工具包

Horologist Media 工具包已含有相应逻辑,可防止通过内置手表音箱意外播放媒体。

其他媒体播放器