如何将我的前台服务通知 (MediaStyle) 保持在顶部,如 Google 播放音乐?

How to keep my foreground service notification (MediaStyle) on top like Google Play Music?

所以,我有一个 PlayerService,它基本上是一个音频播放器。它位于前台,并使用 MediaStyle 和自定义操作(通知中的按钮),如上一个、play/pause、下一个和关闭。

问题是,当我启动 Google Play Music 等其他音频应用程序时,它的通知总是在我的上方!如果我用我的其他 2 个音频应用程序启动,当我单击它们通知中的播放图标时,该通知会转到顶部并停留在那里。而我的总是倒数第二。我的应用程序是否正在播放并不重要。

尝试了 API 的所有版本。但是假设我主要将它与 pre-Oreo 一起使用,所以我们可以忘记 NotificationChannel 的东西。

也试过用MediaSession,设置PRIORITY_MAX,还是不行。顺便说一句,AudioFocus 和所有其他东西都运行良好。通知优先级是唯一的问题。

getNotification() 方法:

return NotificationCompat.Builder( this, CHANNEL_ID )
            .setTicker(ticker)
            .setContentTitle(title)
            .setContentText(text)
            .setSmallIcon(icon)
            .setLargeIcon(icon)
            .setContentIntent(notificationPendingIntent)
            .addAction(prevAction)
            .addAction(playAction)
            .addAction(nextAction)
            .addAction(stopAction)
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setStyle(androidx.media.app.NotificationCompat.MediaStyle()
                .setShowActionsInCompactView(0, 1, 2)
            )
            .setColor(color)
            .setWhen(System.currentTimeMillis())
            .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
            .build()

更新为:

notificationManager.notify( 1, getNotification() )

动作示例:

NotificationCompat.Action(
        R.drawable.play,
        "Play",
        PendingIntent.getBroadcast(
            this,
            REQUEST_CODE,
            playIntent,
            PendingIntent.FLAG_UPDATE_CURRENT
        )
    ) 

所以,问题出在 MediaSession API 的某个地方。我试图在一些教程和 Google 文档的帮助下让它工作,但失败了。其中一些太难或太矫枉过正(例如 Github 上的 UniversalMusicPlayer)。我不需要用 Android Auto 等实现所有这些东西。

终于,我找到了 Exoplayer MediaSession 扩展。这就是我的做法。

build.gradle:

    implementation 'com.google.android.exoplayer:extension-mediasession:2.10.1'

清单:

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

PlayerService.kt:

    ...
    private val player by lazy { ExoPlayerFactory.newSimpleInstance( this ) }
    private val mediaSession by lazy { MediaSessionCompat(this, TAG) }
    private val connector by lazy { MediaSessionConnector(mediaSession) }
    ...
    override fun onCreate() {
        super.onCreate()

        mediaSession.setFlags( MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS or MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS )
        connector.setPlayer(player)
    }
    ...
    private fun getNotification(): Notification {
        return NotificationCompat.Builder( this, CHANNEL_ID )
            ...
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setStyle(androidx.media.app.NotificationCompat.MediaStyle()
                .setMediaSession(mediaSession.sessionToken)
                .setShowActionsInCompactView(0, 1, 2)
            )
            .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
            .build()
    }

这里最重要的部分是连接器。其他代码就像任何其他前台或音频服务一样。但是 MediaSessionConnector 在这里是一个东西。只需几行,但可以为您处理所有事情。您不必编写其他一些 Media*** 代码!除了 MediaBrowser 的东西,但它不是我的情况。

只是因为你没有注册MediaSession

 mMediaSession = new MediaSessionCompat(mContext, TAG, mComponentName, null);

别忘了

mMediaSession.setPlaybackState(new PlaybackStateCompat.Builder()
            .setState(PlaybackStateCompat.STATE_PLAYING, 0, 1.0f)
            .build());