最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

kotlin - Android Media3 MediaSessionService does not produce a notification - Stack Overflow

programmeradmin3浏览0评论

As Android documentation says:

A MediaSessionService will automatically create a MediaStyle notification for you in the form of a MediaNotification.

So I implemented a minimal MediaSessionService which supports "playing" a song and retrieving its metadata. I can control it using a MediaController from my activity, but there is no notification to control it outside of the application. What am I missing?

The service:

@OptIn(UnstableApi::class)
class PlaybackService : MediaSessionService() {
    private val _logger = Logger.getLogger("PlaybackService")
    private val _job = SupervisorJob()
    private var _mediaSession: MediaSession? = null

    override fun onCreate() {
        super.onCreate()

        val scope = CoroutineScope(Dispatchers.IO + _job)
        _mediaSession = MediaSession.Builder(this, Player(mainLooper, scope)).build()
        setListener(object : Listener {
            override fun onForegroundServiceStartNotAllowedException() {
                _logger.warning("Foreground service start not allowed.")
            }
        })
    }

    override fun onDestroy() {
        _mediaSession?.player?.release();
        _mediaSession?.release();

        _job.cancel()

        super.onDestroy()
    }

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

    private class Player(looper: Looper, val scope: CoroutineScope) : SimpleBasePlayer(looper) {
        private val _logger = Logger.getLogger("PlaybackService")

        private val _availableCommands: Commands = Commands.Builder().addAll(
            COMMAND_PLAY_PAUSE,
            COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM,
            COMMAND_SEEK_TO_NEXT_MEDIA_ITEM,
            COMMAND_GET_CURRENT_MEDIA_ITEM,
            COMMAND_GET_METADATA
        ).build();

        private val _metadata = MediaMetadata.Builder()
            .setArtist("Artist")
            .setTitle("Title")
            .build()

        private val _audioAttributes = AudioAttributes.Builder()
            .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
            .setUsage(C.USAGE_MEDIA)
            .build()

        override fun getState(): State {
            return State.Builder()
                .setAvailableCommands(_availableCommands)
                .setPlaylist(listOf(MediaItemData.Builder("")
                    .setMediaMetadata(_metadata)
                    .build()))
                .setAudioAttributes(_audioAttributes)
                .build()
        }

        override fun handleSetPlayWhenReady(playWhenReady: Boolean) = scope.future {
            onPlayPause(playWhenReady)
        }

        private suspend fun onPlayPause(play: Boolean) {
            delay(500)
            if (play)
                _logger.info("Play command invoked.")
            else
                _logger.info("Pause command invoked.")
        }
    }
}

The MediaController usage:

    override fun onStart() {
        super.onStart()

        val context = requireContext()

        if (ContextCompat.checkSelfPermission(context, "android.permission.POST_NOTIFICATIONS") != PERMISSION_GRANTED) {
            _logger.info("Requesting permission for 'android.permission.POST_NOTIFICATIONS'...")
            ActivityCompat.requestPermissions(requireActivity(), arrayOf("android.permission.POST_NOTIFICATIONS"), 0)
        } else
            _logger.info("Already has permission for 'android.permission.POST_NOTIFICATIONS'.")

        val sessionToken = SessionToken(context, ComponentName(context, PlaybackService::class.java))
        val future = MediaController.Builder(context, sessionToken).buildAsync();
        future.addListener({
            val mediaController = future.get()
            mediaController.play()
            val metadata = mediaController.mediaMetadata
        }, MoreExecutors.directExecutor())
    }

The Android manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android=";
    xmlns:tools=";>

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.LayoutPreview"
        tools:targetApi="31">

        <service
            android:name=".PlaybackService"
            android:foregroundServiceType="mediaPlayback"
            android:exported="true">
            <intent-filter>
                <action android:name="androidx.media3.session.MediaSessionService"/>
                <action android:name="android.intent.action.MEDIA_BUTTON" />
            </intent-filter>
        </service>

        <receiver android:name="androidx.media.session.MediaButtonReceiver" android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_BUTTON" />
            </intent-filter>
        </receiver>

        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

EDIT: my application now has the android.permission.POST_NOTIFICATIONS permission and onForegroundServiceStartNotAllowedException is not called, but it still does not show a notification.

As Android documentation says:

A MediaSessionService will automatically create a MediaStyle notification for you in the form of a MediaNotification.

So I implemented a minimal MediaSessionService which supports "playing" a song and retrieving its metadata. I can control it using a MediaController from my activity, but there is no notification to control it outside of the application. What am I missing?

The service:

@OptIn(UnstableApi::class)
class PlaybackService : MediaSessionService() {
    private val _logger = Logger.getLogger("PlaybackService")
    private val _job = SupervisorJob()
    private var _mediaSession: MediaSession? = null

    override fun onCreate() {
        super.onCreate()

        val scope = CoroutineScope(Dispatchers.IO + _job)
        _mediaSession = MediaSession.Builder(this, Player(mainLooper, scope)).build()
        setListener(object : Listener {
            override fun onForegroundServiceStartNotAllowedException() {
                _logger.warning("Foreground service start not allowed.")
            }
        })
    }

    override fun onDestroy() {
        _mediaSession?.player?.release();
        _mediaSession?.release();

        _job.cancel()

        super.onDestroy()
    }

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

    private class Player(looper: Looper, val scope: CoroutineScope) : SimpleBasePlayer(looper) {
        private val _logger = Logger.getLogger("PlaybackService")

        private val _availableCommands: Commands = Commands.Builder().addAll(
            COMMAND_PLAY_PAUSE,
            COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM,
            COMMAND_SEEK_TO_NEXT_MEDIA_ITEM,
            COMMAND_GET_CURRENT_MEDIA_ITEM,
            COMMAND_GET_METADATA
        ).build();

        private val _metadata = MediaMetadata.Builder()
            .setArtist("Artist")
            .setTitle("Title")
            .build()

        private val _audioAttributes = AudioAttributes.Builder()
            .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
            .setUsage(C.USAGE_MEDIA)
            .build()

        override fun getState(): State {
            return State.Builder()
                .setAvailableCommands(_availableCommands)
                .setPlaylist(listOf(MediaItemData.Builder("")
                    .setMediaMetadata(_metadata)
                    .build()))
                .setAudioAttributes(_audioAttributes)
                .build()
        }

        override fun handleSetPlayWhenReady(playWhenReady: Boolean) = scope.future {
            onPlayPause(playWhenReady)
        }

        private suspend fun onPlayPause(play: Boolean) {
            delay(500)
            if (play)
                _logger.info("Play command invoked.")
            else
                _logger.info("Pause command invoked.")
        }
    }
}

The MediaController usage:

    override fun onStart() {
        super.onStart()

        val context = requireContext()

        if (ContextCompat.checkSelfPermission(context, "android.permission.POST_NOTIFICATIONS") != PERMISSION_GRANTED) {
            _logger.info("Requesting permission for 'android.permission.POST_NOTIFICATIONS'...")
            ActivityCompat.requestPermissions(requireActivity(), arrayOf("android.permission.POST_NOTIFICATIONS"), 0)
        } else
            _logger.info("Already has permission for 'android.permission.POST_NOTIFICATIONS'.")

        val sessionToken = SessionToken(context, ComponentName(context, PlaybackService::class.java))
        val future = MediaController.Builder(context, sessionToken).buildAsync();
        future.addListener({
            val mediaController = future.get()
            mediaController.play()
            val metadata = mediaController.mediaMetadata
        }, MoreExecutors.directExecutor())
    }

The Android manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android/apk/res/android"
    xmlns:tools="http://schemas.android/tools">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.LayoutPreview"
        tools:targetApi="31">

        <service
            android:name=".PlaybackService"
            android:foregroundServiceType="mediaPlayback"
            android:exported="true">
            <intent-filter>
                <action android:name="androidx.media3.session.MediaSessionService"/>
                <action android:name="android.intent.action.MEDIA_BUTTON" />
            </intent-filter>
        </service>

        <receiver android:name="androidx.media.session.MediaButtonReceiver" android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_BUTTON" />
            </intent-filter>
        </receiver>

        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

EDIT: my application now has the android.permission.POST_NOTIFICATIONS permission and onForegroundServiceStartNotAllowedException is not called, but it still does not show a notification.

Share Improve this question edited Mar 22 at 7:27 Dark Daskin asked Mar 15 at 3:10 Dark DaskinDark Daskin 1,4841 gold badge15 silver badges23 bronze badges 2
  • Also, show us the manifest file where you declared the service and the permissions. – sdex Commented Mar 17 at 11:32
  • @sdex added the manifest. – Dark Daskin Commented Mar 18 at 19:53
Add a comment  | 

1 Answer 1

Reset to default 0

To show the foreground service notification the app must declare and request the runtime permission:

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

Apart from that, set the media session listener when the media session is created:

setListener(MediaSessionServiceListener())

Implement your listener:

private inner class MediaSessionServiceListener : Listener 

and override this method to handle the exception:

override fun onForegroundServiceStartNotAllowedException()
发布评论

评论列表(0)

  1. 暂无评论