Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to stop the service and remove the notification when connected with MediaController? #73

Closed
Waldmann375 opened this issue May 7, 2022 · 6 comments

Comments

@Waldmann375
Copy link

Waldmann375 commented May 7, 2022

I mean, release the MediaSession, stop the MediaSessionService and remove the notification.

The problem is that just calling stopService(intent) and releasing the MediaController from the activity does not work. I suspect it's because the service keeps being used by the MediaSession.

I know that I should release the mediasession, but the mediasession belongs to the sevice, which I can't control from Activity.
I can't even get into the service's onDestroy callback to release the mediasession.

So my current workaround is to release the MediaController in activity's onDestroy(), so the process stops by a fatal crash:

E/AndroidRuntime: FATAL EXCEPTION: main
    java.lang.NullPointerException: Attempt to invoke interface method 'void androidx.media3.session.IMediaSession.flushCommandQueue(androidx.media3.session.IMediaController)' on a null object reference
        at androidx.media3.session.MediaControllerImplBase$FlushCommandQueueHandler.handleMessage(MediaControllerImplBase.java:3041)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:255)
        at android.app.ActivityThread.main(ActivityThread.java:8212)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:632)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1049)

It does the job, but I believe there should be a better way for such a simple task.

@marcbaechinger marcbaechinger self-assigned this May 7, 2022
@marcbaechinger marcbaechinger changed the title How to stop the player and remove the notification? How to stop the service and remove the notification when connected with MediaController? May 7, 2022
@marcbaechinger
Copy link
Contributor

marcbaechinger commented May 7, 2022

As long as you are bound to the service with a connected controller in your activity, you can't easily stop the service.

If you for some reason want to stop the service from the activity, I think it should work if you first release the controller and then stop the service in MediaController.Listener.onDisconnected(MediaController) (of the last controller disconnecting). You can set a listener when you build the controller with the Builder.

Because the service can only be stopped when no controller is bound to it, I think you can always stop the service on the service side when you call

mediaSession.player.release();
mediaSession.release();
stopSelf();

This disconnects (unbinds) all controllers and then stops the service.

If I understand your requirement correctly, overriding onTaskRemoved of your service, releasing the player there and calling the two lines above would probably just do what you want. Looking into the JavaDoc of onTaskRemoved() is worth it, as it can free you from registering the media controller listener and do book-keeping (with multiple controllers) in the activity as explained above.

The stack trace above is a bug that I think is actually unrelated. I filled #74 for this, so we can keep this as a question for future readers. Thanks for reporting!

@Waldmann375
Copy link
Author

Waldmann375 commented May 8, 2022

Thank you for your answer, marcbaechinger

I did what you said regarding onTaskRemoved , and I've got following behavior: If the player was playing when I remove the app task from the recents, the notification vanish along with activity, but(!) the playback doesn't stop. The app has no single sign of being run, but the song was still playing. I fixed it by adding the line mediaSession.player.stop() before mediaSession.release() and then it went ok.

But this solution only covers the case when a user swipes the app from the recents. Is there a way to somehow send a termination signal from activity to a service (by a button click for example)? One usually does it by IBinder, but the MediaSessionService class already has it's own IBinder implemented, so if I override onBind(), it will breake everything else.

All I am trying to achieve is to ensure that if user wants to remove the notification and stop the player, he does it easily.

@marcbaechinger
Copy link
Contributor

You need to release the player in any case. It's documented in the class level JavaDoc of MediaSession that an app still needs to call mediaSession.player.release when the session is released. However, this should be documented on the release() method as well I think, so I marked this issue as a documentation candidate also.

But it still sounds odd that it keeps playing. I would assume you do release the player in onDestroy, because your app definitely needs to care about releasing the player as normally in any scenario. So I would expect you have this in onDestroy and onDestroy should be called when you call stopSelf if it works as expected. So I would investigate whether onDestroy is actually called accordingly after you did what you describe above. If it is called the you should release the player there, if it's not called you need to investigate why.

@vcimka
Copy link

vcimka commented May 11, 2022

have same problem with MediaSessionService

i'm trying to remove the app from recent when music playing and I get the following results:

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

        stopForeground(true)
        stopSelf()
    }
  1. music keep playing after app removing but notification was gone
override fun onTaskRemoved(rootIntent: Intent) {
        super.onTaskRemoved(rootIntent)

        player.stop()
        player.clearMediaItems()

        stopForeground(true)
        stopSelf()
    }
  1. music was stopped but notification was recreated in system tray

@marcbaechinger
Copy link
Contributor

What if you use the lines from my comment above?

mediaSession.player.release();
mediaSession.release();
stopSelf();

icbaker pushed a commit that referenced this issue May 24, 2022
Specifically mention that releasing the session does not release the player and the app needs to take care to release the player.

Issue: #73
PiperOrigin-RevId: 448454338
@marcbaechinger
Copy link
Contributor

The stack trace from the initial post was caused by #74 and is fixed.

The question of this issues looks like answered to me with the post above, so I close this issue for now. Please re-open or open a new issue if you think something on the library side is wrong.

For future readers and further reading this DAC article about bound and started services may be of interest. MediaLibraryService/MediaSessionService both are bound services (when a MediaController binds to the service) and started services as well (when the service is started in the foreground in case of the activity being put into the background). A bound service can't be stopped. If an app wants to stop the service, the session needs to be released to disconnect all bound controllers and then the service be stopped with for instance stopSelf() that causes onDestroy() to be called where the app releases the remaining resources (like the player).

@androidx androidx locked and limited conversation to collaborators Mar 4, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants