如何从MediaSessionService示例获取Player变量并在Compose中使用?
在Compose中简单获取Media3 MediaController并实现网络媒体播放与通知
一、修复PlaybackService的核心问题
你当前的Service代码存在两个关键错误:
- 未初始化
mediaSession,导致外部无法连接会话 - Service内部错误使用
MediaController,正确逻辑是Service持有Player实例,通过MediaSession对外暴露控制能力
修改后的PlaybackService代码:
import android.content.Intent import androidx.media3.common.MediaItem import androidx.media3.common.Player import androidx.media3.exoplayer.ExoPlayer import androidx.media3.session.MediaSession import androidx.media3.session.MediaSessionService import androidx.media3.session.MediaStyleNotificationHelper class PlaybackService : MediaSessionService() { private lateinit var player: ExoPlayer private var mediaSession: MediaSession? = null override fun onCreate() { super.onCreate() // 初始化ExoPlayer(适配网络媒体播放) player = ExoPlayer.Builder(this).build() // 关联Player与MediaSession,对外暴露控制入口 mediaSession = MediaSession.Builder(this, player).build() // 启动前台服务并显示媒体通知(Android 8.0+必须步骤) startForegroundWithNotification() } private fun startForegroundWithNotification() { val notification = MediaStyleNotificationHelper.createNotification( this, mediaSession!!.sessionToken, "媒体播放", "正在播放网络媒体", null // 可传入点击通知跳转的Activity类,如MainActivity::class.java ) startForeground(1, notification) } override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? = mediaSession override fun onTaskRemoved(rootIntent: Intent?) { if (!player.playWhenReady || player.mediaItemCount == 0) { stopSelf() } } override fun onDestroy() { player.stop() player.release() mediaSession?.release() super.onDestroy() } }
二、在Compose中获取MediaController
无需复杂MVVM架构,以下两种简单方式实现:
方式1:在Activity中初始化并传递给Composable
import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.media3.session.MediaController import androidx.media3.session.SessionToken import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.MoreExecutors class MainActivity : ComponentActivity() { private lateinit var controllerFuture: ListenableFuture<MediaController> private var mediaController: MediaController? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 启动PlaybackService startService(Intent(this, PlaybackService::class.java)) // 异步构建MediaController val sessionToken = SessionToken(this, PlaybackService::class.java) controllerFuture = MediaController.Builder(this, sessionToken).buildAsync() controllerFuture.addListener({ mediaController = controllerFuture.get() // 控制器就绪后更新Composable setContent { mediaController?.let { HomeScreen(it) } ?: Text("加载中...") } }, MoreExecutors.directExecutor()) // 初始显示加载状态 setContent { Text("加载中...") } } override fun onDestroy() { mediaController?.release() super.onDestroy() } }
方式2:直接在Composable中管理连接状态
import androidx.compose.foundation.layout.Column import androidx.compose.material.Button import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import androidx.media3.common.MediaItem import androidx.media3.session.MediaController import androidx.media3.session.SessionToken import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.MoreExecutors @Composable fun HomeScreen() { val context = LocalContext.current val mediaController = remember { mutableStateOf<MediaController?>(null) } // 异步连接MediaSession LaunchedEffect(Unit) { if (mediaController.value == null) { context.startService(Intent(context, PlaybackService::class.java)) val sessionToken = SessionToken(context, PlaybackService::class.java) val future: ListenableFuture<MediaController> = MediaController.Builder(context, sessionToken).buildAsync() future.addListener({ mediaController.value = future.get() }, MoreExecutors.directExecutor()) } } Column { when (mediaController.value) { null -> Text("加载中...") else -> { Button(onClick = { // 播放网络媒体 val mediaItem = MediaItem.fromUri("https://example.com/your-media-file.mp3") mediaController.value?.apply { setMediaItem(mediaItem) prepare() play() } }) { Text("播放") } Button(onClick = { mediaController.value?.stop() }) { Text("停止") } } } } }
三、关键注意事项
- 权限配置:确保Manifest中已添加
INTERNET和FOREGROUND_SERVICE权限,且Service的foregroundServiceType设为mediaPlayback - 通知显示:必须调用
startForeground将服务设为前台,否则Android 8.0+系统不会显示媒体通知 - 资源释放:Activity销毁时释放MediaController,Service销毁时释放Player和MediaSession,避免内存泄漏
内容的提问来源于stack exchange,提问作者Amr Essam
相关产品推荐
相关产品推荐

