Android Jetpack Compose应用Media3功能时出现SecurityException崩溃求助
问题分析与解决方案:Media3
SecurityException: Session rejected the connection request 核心排查与修复步骤
1. 检查Media Session安全策略配置
SecurityException最常见原因是Session的权限校验拒绝了连接请求,先确认MusicPlaybackService中MediaSession的安全策略:
- 调试阶段可临时放开所有连接权限,验证是否因权限限制导致:
val session = MediaSession.Builder(context, player) .setSessionSecurityPolicy(SessionSecurityPolicy.permitAll()) .build() - 若需自定义权限控制,需在
onGetSession方法中校验客户端信息(包名、签名),确保允许当前应用的Controller连接:override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? { // 仅允许本应用包名连接 if (controllerInfo.packageName == packageName) { return session } return null }
2. 对齐Controller连接时机与Session生命周期
避免在Session未完全初始化时发起连接:
- 通过
ServiceConnection监听Service启动状态,在Service连接成功后再初始化Controller:val serviceIntent = Intent(context, MusicPlaybackService::class.java) context.bindService(serviceIntent, object : ServiceConnection { override fun onServiceConnected(name: ComponentName?, service: IBinder?) { val sessionToken = MediaSessionToken.fromBinder(service) MediaController.Builder(context, sessionToken).buildAsync() } override fun onServiceDisconnected(name: ComponentName?) {} }, Context.BIND_AUTO_CREATE) - 使用MediaController的
setListener监听连接错误,避免崩溃:MediaController.Builder(context, sessionToken) .setListener(object : MediaController.Listener { override fun onSessionConnectError(error: SessionCommandException) { // 处理连接失败,如重试或提示用户 } }) .buildAsync()
3. 验证SessionToken的正确性
确保Controller获取的SessionToken与Service中创建的Session一致:
- 在Service中通过
session.sessionToken获取有效Token,避免传递过期或无效的Token; - 若跨进程连接,需确认客户端包名与Service声明的权限范围匹配,避免因包名不匹配被拒绝。
4. 排查多进程与权限隔离问题
如果应用使用多进程:
- 检查
AndroidManifest.xml中Service的android:process属性,确保Controller所在进程拥有访问MediaSession的权限; - 给Service进程添加必要权限(如
android.permission.MEDIA_CONTENT_CONTROL),避免权限缺失导致连接被拒。
5. 开启详细日志定位根因
开启Media3调试日志,获取更具体的拒绝原因:
在AndroidManifest.xml中添加:
<meta-data android:name="com.google.android.exoplayer2.DEBUG" android:value="true" />
查看日志中伴随SecurityException的详细提示,通常会明确指出是权限不匹配、Token无效还是其他校验失败原因。
代码示例参考
修复后的MusicPlaybackService
class MusicPlaybackService : MediaSessionService() { private lateinit var player: ExoPlayer private lateinit var session: MediaSession override fun onCreate() { super.onCreate() player = ExoPlayer.Builder(this).build() session = MediaSession.Builder(this, player) .setSessionSecurityPolicy(SessionSecurityPolicy.permitAll()) .setSessionActivity(PendingIntent.getActivity(this, 0, Intent(this, MainActivity::class.java), PendingIntent.FLAG_IMMUTABLE)) .build() } override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? { // 自定义校验逻辑,仅允许本应用连接 return if (controllerInfo.packageName == packageName) session else null } override fun onDestroy() { player.release() session.release() super.onDestroy() } }
修复后的MusicPlaybackController
class MusicPlaybackController(context: Context) : PlaybackController { private var mediaController: MediaController? = null init { val serviceIntent = Intent(context, MusicPlaybackService::class.java) context.bindService(serviceIntent, object : ServiceConnection { override fun onServiceConnected(name: ComponentName?, service: IBinder?) { service?.let { binder -> val sessionToken = MediaSessionToken.fromBinder(binder) MediaController.Builder(context, sessionToken) .setListener(object : MediaController.Listener { override fun onSessionConnected(controller: MediaController, hints: MediaSession.ConnectionHints) { mediaController = controller // 初始化播放控制逻辑 } override fun onSessionConnectError(error: SessionCommandException) { Log.e("PlaybackController", "连接失败: ${error.message}") } }) .buildAsync() } } override fun onServiceDisconnected(name: ComponentName?) {} }, Context.BIND_AUTO_CREATE) } // 实现PlaybackController接口方法... }
内容的提问来源于stack exchange,提问作者Aarav Bansal
相关产品推荐
相关产品推荐

