You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android 13移除最近应用后MediaSessionService被销毁问题求助

问题描述

我在Activity的onCreate中启动服务:Android Oreo及以上版本使用startForegroundService,更低版本使用startService。在服务中,我通过通知工具类调用startForeground方法。应用处于前台时播放器运行正常且显示通知,但一旦将应用从最近应用列表移除,服务就会被销毁——该服务本应作为前台服务持续运行,不清楚停止的原因。

尝试过的解决方案:在Oreo及以上版本改用startService启动,还有其他方法;最后一种是服务销毁后重启,虽能工作但并不理想。
测试环境:Android 13


Activity代码

class MainActivity : ComponentActivity() {
    private var isServiceRunning: Boolean = false

    private fun startingService(){
        if (!isServiceRunning){
            var intent = Intent(this, PlaybackSessionService::class.java)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
                startForegroundService(intent)
            }else{
                startService(intent)
            }

            isServiceRunning = true
        }
    }

    @OptIn(UnstableApi::class)
    override fun onCreate(savedInstanceState: Bundle?) {
            startingService()
//the rest code compose Ui
}

PlaybackSessionService代码

class PlaybackSessionService: MediaSessionService() {
    val mediaSession: MediaSession by inject()
    val musiCoNotificationManager: MusiCoNotificationManager by inject()


    @OptIn(UnstableApi::class)
    override fun onCreate() {
        super.onCreate()

        Log.e("ks","onCreate service")

        musiCoNotificationManager.startNotificationService(
            mediaSession = mediaSession,
            mediaSessionService = this
        )
    }


    @OptIn(UnstableApi::class)
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.e("ks","onStartCommand service")

        musiCoNotificationManager.startNotificationService(
            mediaSession = mediaSession,
            mediaSessionService = this
        )

        return super.onStartCommand(intent, flags, startId)
    }

    override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? {
        return mediaSession
    }


    override fun onTaskRemoved(rootIntent: Intent?) {
        super.onTaskRemoved(rootIntent)
    }


    override fun onDestroy() {
        super.onDestroy()

        Log.e("ks","Service destroyed")
        mediaSession.apply {
            release()
            if (player.playbackState != Player.STATE_IDLE){
                player.seekTo(0)
                player.playWhenReady = false
                player.stop()
                player.release()
            }
        }
    }
}

NotificationManager代码

class MusiCoNotificationManager(
    private val context: Context,
    private val player: ExoPlayer,
): KoinComponent {


    @UnstableApi
    fun startNotificationService(
        mediaSessionService: MediaSessionService,
        mediaSession: MediaSession
    ){
        buildNotification(mediaSession)
        startForegroundNotificationService(mediaSessionService)
    }

    private fun startForegroundNotificationService(mediaSessionService: MediaSessionService){
        Log.e("ks","Enter startForeground fun in MusiCoNotificationManager class ")
        val notification = NotificationCompat.Builder(context, MusicoApp.NOTIFICATION_CHANNEL_ID)
            .setOngoing(true)
            .setOnlyAlertOnce(true)
            .build()
        mediaSessionService.startForeground(MusicoApp.NOTIFICATION_ID,notification)
    }

    @UnstableApi
    private fun buildNotification(mediaSession: MediaSession){
        PlayerNotificationManager.Builder(
            context,
            MusicoApp.NOTIFICATION_ID,
            MusicoApp.NOTIFICATION_CHANNEL_ID
        )
            .setMediaDescriptionAdapter(
                MusiCoNotificationAdapter(context = context, pendingIntent = mediaSession.sessionActivity)
            )
            .setSmallIconResourceId(R.drawable.music_note)
            .build()
            .also {
                it.setMediaSessionToken(mediaSession.platformToken)
                it.setUseFastForwardActionInCompactView(true)
                it.setUseNextActionInCompactView(true)
                it.setUseRewindActionInCompactView(true)
                it.setUsePreviousActionInCompactView(true)
                it.setPriority(NotificationCompat.PRIORITY_DEFAULT)
                it.setPlayer(player)
            }
    }
}

问题原因与解决办法

1. onStartCommand返回值错误

你的onStartCommand返回了父类默认值START_NOT_STICKY,该值意味着系统杀死服务后不会自动重启,且应用从最近列表移除时,服务会被直接终止。

修复:将返回值改为START_STICKY或START_REDELIVER_INTENT:

  • START_STICKY:系统重启服务后传入空Intent,适合无需恢复播放状态的场景
  • START_REDELIVER_INTENT:系统会重新传递最后一次的Intent,适合需要恢复播放进度的场景

修改后的代码:

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    Log.e("ks","onStartCommand service")
    musiCoNotificationManager.startNotificationService(
        mediaSession = mediaSession,
        mediaSessionService = this
    )
    return START_STICKY // 或START_REDELIVER_INTENT
}

2. 通知冲突导致前台服务失效

你同时使用PlayerNotificationManager和手动调用startForeground,两者共用同一个NOTIFICATION_ID,会导致通知被覆盖,系统可能判定前台服务的通知无效,进而杀死服务。

修复:让PlayerNotificationManager自动处理前台服务逻辑,移除手动调用startForeground的代码:
修改MusiCoNotificationManager的startNotificationService方法:

@UnstableApi
fun startNotificationService(
    mediaSessionService: MediaSessionService,
    mediaSession: MediaSession
){
    // PlayerNotificationManager内部会自动调用startForeground
    buildNotification(mediaSession)
}

3. 额外配置检查

  • Android 13通知权限:确保已在Manifest中声明权限,并在运行时申请:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
  • 服务声明配置:Android 12+需在Manifest中指定foregroundServiceType:
<service
    android:name=".PlaybackSessionService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="android.media.browse.MediaBrowserService" />
    </intent-filter>
</service>
  • onTaskRemoved处理:保持当前空实现,不要调用stopSelf(),避免应用从最近列表移除时主动终止服务。

内容的提问来源于stack exchange,提问作者Khalid Ahmed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 00:34:51