第2章 ExoPlayer依赖集成与基本初始化
2.1 为什么选择ExoPlayer
ExoPlayer是Google官方维护的Android媒体播放器库,相比MediaPlayer,它提供了更细粒度的控制、更好的自定义能力和对DASH/HLS/SS等自适应流协议的原生支持。在Jetpack Compose项目中集成ExoPlayer是构建高质量播放界面的第一步。
2.2 Gradle依赖配置
在模块级 build.gradle.kts 或 build.gradle 中添加ExoPlayer核心依赖。推荐使用 2.19.1 或以上版本,保持与Compose的兼容性。
基础依赖(Kotlin DSL)
// app/build.gradle.kts
dependencies {
// ExoPlayer 核心库
implementation("com.google.android.exoplayer:exoplayer-core:2.19.1")
// UI 组件库(用于默认控制视图,非必需但常用)
implementation("com.google.android.exoplayer:exoplayer-ui:2.19.1")
// 可选:支持 DASH 自适应流
implementation("com.google.android.exoplayer:exoplayer-dash:2.19.1")
// 可选:支持 HLS 流
implementation("com.google.android.exoplayer:exoplayer-hls:2.19.1")
// 可选:支持 SmoothStreaming
implementation("com.google.android.exoplayer:exoplayer-smoothstreaming:2.19.1")
}
版本统一管理(推荐)
// 在项目根 build.gradle.kts 或 libs.versions.toml 中定义版本变量
// gradle/libs.versions.toml
[versions]
exoplayer = "2.19.1"
[libraries]
exoplayer-core = { group = "com.google.android.exoplayer", name = "exoplayer-core", version.ref = "exoplayer" }
exoplayer-ui = { group = "com.google.android.exoplayer", name = "exoplayer-ui", version.ref = "exoplayer" }
关键检查点:
- 同步Gradle后确认依赖已下载:查看
External Libraries中是否出现exoplayer-core。 - 若目标设备API低于21(Android 5.0),需额外使用
exoplayer-extension-okhttp或exoplayer-extension-mediasession。 - ExoPlayer没有
androidx.media3之前的版本,如果是Media3项目请使用androidx.media3:media3-exoplayer。
2.3 Player对象创建
ExoPlayer实例通过 ExoPlayer.Builder 创建。Builder允许配置渲染器、加载控制、带宽估计器等。在Compose环境下,建议将Player创建与 remember 和 DisposableEffect 结合,确保资源释放。
最基本的Player创建
val context = LocalContext.current
val player = remember {
ExoPlayer.Builder(context)
.setRenderersFactory(DefaultRenderersFactory(context))
.setLoadControl(DefaultLoadControl())
.build()
}
带自定义配置的Builder
val player = remember {
ExoPlayer.Builder(context)
.setRenderersFactory(
DefaultRenderersFactory(context)
.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON)
)
.setLoadControl(
DefaultLoadControl.Builder()
.setBufferDurationsMs(
DefaultLoadControl.DEFAULT_MIN_BUFFER_MS,
DefaultLoadControl.DEFAULT_MAX_BUFFER_MS,
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS,
DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS
)
.createDefaultLoadControl()
)
.setBandwidthMeter(DefaultBandwidthMeter.Builder(context).build())
.build()
}
| Builder方法 | 默认值 | 说明 |
|---|---|---|
setRenderersFactory |
DefaultRenderersFactory | 控制音频、视频、文本渲染器 |
setLoadControl |
DefaultLoadControl | 定义缓冲策略和加载阈值 |
setBandwidthMeter |
DefaultBandwidthMeter | 用于自适应流的带宽估计 |
setTrackSelector |
DefaultTrackSelector | 选择音视频轨道 |
setAnalyticsCollector |
AnalyticsCollector | 收集播放统计信息 |
2.4 生命周期绑定
ExoPlayer是一个重量级对象,必须跟随Composable生命周期创建和释放。典型模式:在 remember 中创建,在 DisposableEffect 的 onDispose 中释放。
基础生命周期绑定(Compose)
@Composable
fun VideoPlayerScreen(videoUri: Uri) {
val context = LocalContext.current
val player = remember {
ExoPlayer.Builder(context).build().also {
// 设置媒体源
val mediaItem = MediaItem.fromUri(videoUri)
it.setMediaItem(mediaItem)
it.prepare()
it.playWhenReady = true
}
}
// 绑定生命周期
DisposableEffect(AndroidView(factory = {}) ) {
onDispose {
player.apply {
stop()
release()
}
}
}
// 使用 AndroidView 嵌入 PlayerView
AndroidView(
factory = { ctx ->
PlayerView(ctx).apply {
this.player = player
}
},
modifier = Modifier.fillMaxSize()
)
}
常见陷阱:
- 重复创建:不要在Composable的每次重组中创建新Player,必须使用
remember保持实例稳定。 - 忘记释放:未释放Player会导致内存泄漏、媒体服务连接泄漏,严重时使系统媒体服务卡死。
- 多个屏幕/旋转:若Activity重建(如旋转),需在
savedInstanceState中恢复播放位置,否则用户会看到从头播放。
Activity/Fragment生命周期绑定(传统视图)
// 如果项目中仍有非Compose页面,可以使用 LifecycleObserver
class PlayerLifecycleObserver(private val player: ExoPlayer) : DefaultLifecycleObserver {
override fun onStart(owner: LifecycleOwner) {
// 准备播放
player.prepare()
}
override fun onStop(owner: LifecycleOwner) {
player.stop()
}
override fun onDestroy(owner: LifecycleOwner) {
player.release()
}
}
2.5 基本属性设置
初始化完成后可调整播放参数:
player.apply {
// 音量 0.0f ~ 1.0f
volume = 0.8f
// 播放速度
setPlaybackSpeed(1.0f)
// 是否循环播放
repeatMode = Player.REPEAT_MODE_ALL
// 音频焦点管理
setHandleAudioFocus(true)
// 保持屏幕常亮
setWakeMode(C.WAKE_MODE_NETWORK)
}
2.6 初始化流程总览
2.7 常见错误与调试
| 错误现象 | 可能原因 | 解决方式 |
|---|---|---|
| 播放时黑屏但音频正常 | SurfaceView未正确关联到Player | 检查PlayerView是否正确设置了player实例 |
IllegalStateException: player is not initialized |
在release后调用Player方法 | 使用 if (player.isPlaying) 等条件判断后操作 |
| 播放完成时崩溃 | 缺乏Player.Listener处理 | 添加 Player.Listener 监听播放结束事件 |
Gradle同步失败:Failed to resolve: com.google.android.exoplayer... |
未添加google()仓库或版本号错误 | 确保 settings.gradle.kts 包含 google(),版本来自maven central |
2.8 性能与稳定性优化
- 预加载与缓冲策略:根据网络类型调整
minBufferMs和maxBufferMs,移动网络下建议降低缓冲阈值。 - 释放时机:使用
DisposableEffect确保Player在Composable离开组合后被正确释放;若使用AndroidView的onRelease回调也可。 - 避免重复创建:当MediaItem变化时,调用
player.setMediaItem(newMediaItem)并player.prepare(),不要recreate整个Player。 - 音频焦点:开启
setHandleAudioFocus(true),避免与其他App冲突;自定义AudioFocusManager可优化暂停/继续行为。 - Surface生命周期:如果直接使用
SurfaceView,需在Surface可用时再调用player.setVideoSurface(surface),推荐PlayerView自动处理。
2.9 小结
本章完成了ExoPlayer的Gradle依赖配置、Player对象创建、生命周期绑定以及基本属性设置。这些是后续所有播放界面功能的基础,确保每个环节正确后,才能顺利进行自定义UI、手势控制、后台播放等高级特性开发。