androidkotlinexoplayerforeground-serviceandroid-media3

MediaSessionService destroyed after remove app from recent apps


I'm starting the service in onCreate in the Activity using startForegroundService for Android Oreo and above and using startService for lower, in service I used startForeground in Service by calling helper methods for notifications, when the app is in foreground the player working perfectly and shows a notification, but once I removed the app from recent apps the service destroyed. The service should continue working as foreground service but I don't know why it keeps stoping

I tried some solutions like startService instead of startForegroundService for Oreo and above, I tried others solutions last one was restarting the service once it was destroyed but it's working but it's not ideal. I'm testing the app in Anroid 13


Here is my 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

Here is my 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()
            }
        }
    }
}

Here is my 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)
            }
    }


}

Solution

  • I solved the problem, simply I needed MediaController to connect it with MediaSessionService and using MediaController you don't need to startForegroundService from Activity and you don't need to startForeground within service all you need just MediaController,Notification for foreground is automatically provided by MediaSessionService as the documentation said and if you want you can make your custom one but in case you make your own notification I'm not sure if u need to start service as foreground from Activity.

    here is the sample in Activity in onStart

        override fun onStart() {
            super.onStart()
            val sessionToken = SessionToken(this, ComponentName(this,
                PlayerSessionService::class.java))
    
            val controllerFuture = MediaController.Builder(this,sessionToken).buildAsync()
            controllerFuture.addListener({
                if (controllerFuture.isDone){
                    controller = controllerFuture.get()
                }
            }, MoreExecutors.directExecutor()
            )
    
        }
    

    Just this

    Note: You can't inject MediaController using di like koin or dagger hilt I faced problem when I tried to inject it using koin which freeze my app the reason is MediaController takes seconds to be initialized