androidx / media

Jetpack Media3 support libraries for media use cases, including ExoPlayer, an extensible media player for Android
https://developer.android.com/media/media3
Apache License 2.0
1.73k stars 416 forks source link

Duplicate MediaSession #451

Open TheBeastLT opened 1 year ago

TheBeastLT commented 1 year ago

After migrating to media3 from ExoPlayer and new MediaSession api, we've started getting some rare crash reports on duplicate media sessions:

Fatal Exception: java.lang.IllegalStateException: Session ID must be unique. ID=.MySession
       at androidx.media3.session.MediaSession.<init>(MediaSession.java:553)
       at androidx.media3.session.MediaSession$Builder.build(MediaSession.java:360)

Session is correctly cleaned up on fragment stop/destroy lifecycles, but I guess there can be conditions where you're not able to release it (due to some timeout or fragment crash) and the reference to it escapes your scope. So I think it would be convenient to have some static method to retrieve a MediaSession by it's id, or have some flag in the builder to force release previous session if it exists?

oceanjules commented 1 year ago

I think we actually have a map you are talking about here:

https://github.com/androidx/media/blob/2fc189d6a40f116bd54da69ab9a065219f6973e7/libraries/session/src/main/java/androidx/media3/session/MediaSession.java#L235

...which is how the uniqueness is checked in the first place, although it's private.

Could you provide a bit more information about how you initialise and release your session? Have you compared your implementation to what we have in: https://github.com/androidx/media/blob/2fc189d6a40f116bd54da69ab9a065219f6973e7/demos/session/src/main/java/androidx/media3/demo/session/PlaybackService.kt#L85-L89

marcbaechinger commented 1 year ago

Yeah, I also think we need a bit more information about how you create/release sessions.

Session is correctly cleaned up on fragment stop/destroy lifecycles,

Are you using a session without a service in your UI or are you connecting a MediaController/MediaBrowser to a service?

where you're not able to release it (due to some timeout or fragment crash) and the reference to it escapes your scope.

I think you'd find a way to handle such cases. If the app crashes, fine, then the process will go away and you don't have much of a chance other then fixing the root problem that crashed the app. But I'm not sure I understand what you mean with the fragment crashes. Because if the app is still alive you should be able to detect this and gracefully release the session.

However, please shed some light on how you initialize and release your session and add some more details what you intent to do. Specifically if you want to create and destroy your session in accordance to the life-cycle of a fragment or another UI components I'd be interested to hear what your use case is.

Perhaps, we can give you some guidance in how to do that best, or we find that there is a use case that we currently don't support well.

TheBeastLT commented 1 year ago

This happens on some rarer cases for which we see crash reports, but it's not very clear on the conditions how it happens. This is how we init and destroy the session:

...
    override fun onStart() {
        super.onStart()
        createMediaSession()
    }

    override fun onStop() {
        player?.stop()
        super.onStop()
        releaseMediaSession()
    }

    override fun onDestroy() {
        super.onDestroy()
        releasePlayer()
    }
...
    private fun createMediaSession() {
        mediaSession = MediaSession.Builder(requireContext(), player)
            .setId(MEDIA_SESSION_TAG)
            .build()
    }

    private fun releasePlayer() {
        releaseMediaSession()
        playerTransportGlue?.host = null
        playerTransportGlue = null
        playerAdapter = null
        player?.release()
        player = null
    }

    private fun releaseMediaSession() {
        mediaSession?.release()
        mediaSession = null
    }
achampagne-ensemble commented 3 months ago

I am also running into this issue, how I handle creating and releasing the mediaSession is similar to @TheBeastLT's code. I can repro if I close the video player activity and quickly try to re-open the activity. My guess is that there is some kind of race condition where the activity is destroyed before the mediaSession is released, and re-starting the activity stops the mediaSession from being released before it's re-created. It would be nice if there was a static method that allows developers to find all active mediaSessions, so we could prevent re-creating one that is not yet released. Any updates on this @marcbaechinger ?