前言
在 Android 中编写一个全局可调用的播放音频的单例工具类,可以借助 object 关键字实现单例,并封装 MediaPlayer 的常用操作(如播放、暂停、停止等)。
工具类
整体
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 116 117 118 119 120 121
| import android.content.Context import android.media.MediaPlayer import android.net.Uri import androidx.annotation.RawRes import java.io.IOException
object ZAudioPlayer { private var mediaPlayer: MediaPlayer? = null
fun playRaw(context: Context, @RawRes resId: Int) { reset() try { mediaPlayer = MediaPlayer.create(context.applicationContext, resId) mediaPlayer?.setOnCompletionListener { reset() } mediaPlayer?.start() } catch (e: Exception) { e.printStackTrace() reset() } }
fun playAsset(context: Context, assetFileName: String) { reset() try { val assetFd = context.assets.openFd(assetFileName) mediaPlayer = MediaPlayer().apply { setDataSource(assetFd.fileDescriptor, assetFd.startOffset, assetFd.length) prepare() setOnCompletionListener { assetFd.close() reset() } start() } } catch (e: IOException) { e.printStackTrace() reset() } }
fun playUri(context: Context, uri: Uri) { reset() try { mediaPlayer = MediaPlayer().apply { setDataSource(context, uri) prepareAsync() setOnPreparedListener { start() } setOnCompletionListener { reset() } } } catch (e: Exception) { e.printStackTrace() reset() } }
fun playUrl(context: Context, url: String) { stop() val uri = Uri.parse(url) try { mediaPlayer = MediaPlayer().apply { setDataSource(context, uri) prepareAsync() setOnPreparedListener { start() } setOnCompletionListener { reset() } } } catch (e: Exception) { e.printStackTrace() reset() } }
fun pause() { mediaPlayer?.takeIf { it.isPlaying }?.pause() }
fun stop() { reset() }
private fun reset() { mediaPlayer?.apply { if (isPlaying) stop() release() } mediaPlayer = null }
fun isPlaying(): Boolean = mediaPlayer?.isPlaying == true }
|
设置播放速度
在 Android 的 MediaPlayer 中,原生并不直接支持变速播放(改变播放速度),直到 API 23(Android 6.0) 才引入了对播放速度的控制支持。
要控制音频播放速度,请使用 PlaybackParams 类,并通过 MediaPlayer.setPlaybackParams() 方法设置。
但请注意:
- 最低 API 要求:API 23(Android 6.0)及以上
- 某些设备或编解码器可能不完全支持所有速度值(尤其是非 1.0 的值)
- 通常只支持 0.5x 到 2.0x 之间的速度
- 必须在
start() 之前调用 setPlaybackParams(),否则可能无效。
代码示例
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
|
fun playUrl(context: Context, url: String, speed: Float = 1.0f) { stop() val uri = Uri.parse(url) try { mediaPlayer = MediaPlayer().apply { setDataSource(context, uri) prepareAsync() setOnPreparedListener { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val params = PlaybackParams().setSpeed(speed) setPlaybackParams(params) } start() } setOnCompletionListener { reset() } } } catch (e: Exception) { e.printStackTrace() reset() } }
|
使用示例
播放
播放网络音频
1 2
| val uri = Uri.parse("https://example.com/sound.mp3") ZAudioPlayer.playUri(applicationContext, uri)
|
或者
1 2
| val url = "https://example.com/sound.mp3" ZAudioPlayer.playUrl(applicationContext, url)
|
播放 raw 资源
1
| ZAudioPlayer.playRaw(applicationContext, R.raw.click_sound)
|
播放 assets 文件
1
| ZAudioPlayer.playAsset(applicationContext, "notification.mp3")
|
暂停 / 停止
1 2
| ZAudioPlayer.pause() ZAudioPlayer.stop()
|
Compose中使用
viewModel中定义
1 2 3
| fun playAudio(applicationContext: android.content.Context, url: String) { ZAudioPlayer.playUrl(applicationContext, url) }
|
Compose中调用
获取上下文
1 2
| val context = LocalContext.current val appContext = context.applicationContext
|
调用
1
| vm.playAudio(appContext, url)
|
页面销毁时停止播放
1 2 3 4 5
| DisposableEffect(Unit) { onDispose { ZAudioPlayer.stop() } }
|
全局appContext
定义
1 2 3
| val LocalAppContext = compositionLocalOf<Context> { error("AppContext not provided") }
|
提供
1 2 3 4 5
| val context = LocalContext.current val appContext = context.applicationContext CompositionLocalProvider(LocalAppContext provides appContext) {
}
|
使用
1
| val appContext = LocalAppContext.current
|
注意事项
上下文建议使用 applicationContext,防止内存泄漏。
如果需要同时播放多个音频,此单例不适用(因为只维护一个 MediaPlayer 实例)。
对于短音效(如按钮点击声),建议使用 SoundPool;对于背景音乐或长音频,MediaPlayer 更合适。
网络音频需添加网络权限:
1
| <uses-permission android:name="android.permission.INTERNET" />
|