iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 13
0
Mobile Development

Android 音樂播放器自己來系列 第 13

播放器架構實作 (6) - Notification 實作

  • 分享至 

  • xImage
  •  

點擊歌曲播放後,在 status bar 上就會顯示 notification,上面有歌曲的相關資訊,並且可以控制。除了這些功能外,還有一個功能是讓 App 就算在背景播放時,還是能維持在前景,為什麼要有這個功能呢?因為如果 App 在背景,當系統資源不足時,可能就會將這個 App 給砍掉,這時候如果在播歌,這樣歌就會停下來了,會有不好的使用者體驗,因此在播歌時,會啟動 ForegroundService,並且透過 notification 來告知使用者。

ContextCompat.startForegroundService(applicationContext,
                 Intent(applicationContext, this@MusicService.javaClass))
startForeground(notificationId, notification)

在使用 startForegroundService 後,必須馬上呼叫 startForeground,如果沒有馬上呼叫( 5秒內),就會有 exception,StackOverflow 上還蠻多開發者在討論這個問題的,在 StackOverflow 內比較多在討論的寫法是在 Service 的 onCreate 內才會呼叫 startForeground。但可以看到這邊是接續寫的,因為這邊的順序點擊播歌後,Player 播放,啟動 notificaion,notification 的 callback 收到後,再來啟動 Service。

今天就是要來實作這個部分啦。先來看官方的介紹:Using MediaStyle notifications with a foreground service,設定了 notification 要顯示的內容(歌曲名稱、專輯圖、播放行為)。 ExoPlayer 提供了一些方便的設定方式,提供了一些預設的行為。

首先在 AndroidManifest 先加入 FOREGROUND_SERVICE permission,這個權限宣告是在 Android 9 加入的,因為會使用到 ForegroundService,因此要先宣告。

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

因為會使用到 PlayerNotificationManager 為 exoplayer-ui 的元件,import 相關元件。

implementation 'com.google.android.exoplayer:exoplayer-ui:2.11.8'

在 MusicNotificationManager 內透過 ExoPlayer 提供的 createWithNotificationChannel,來建立 NotificationManager,設定一些基本資訊 和 sessionToken。

val mediaController = MediaControllerCompat(context, sessionToken)

        notificationManager = PlayerNotificationManager.createWithNotificationChannel(
            context,
            NOW_PLAYING_CHANNEL_ID,
            R.string.notification_channel,
            R.string.notification_channel_description,
            NOW_PLAYING_NOTIFICATION_ID,
            DescriptionAdapter(mediaController),
            notificationListener
        ).apply {
            setMediaSessionToken(sessionToken)
            setSmallIcon(R.drawable.ic_default_cover_icon)
            // Don't display the rewind or fast-forward buttons.
            setRewindIncrementMs(0)
            setFastForwardIncrementMs(0)
        }

在 DescriptionAdapter 是傳入 MediaControllerCompat,取得播放歌曲的相關資訊(歌曲名稱、歌手名稱、專輯封面圖、點擊後會開啟的 activity),設定給 notification。

private inner class DescriptionAdapter(private val controller: MediaControllerCompat) :
        PlayerNotificationManager.MediaDescriptionAdapter {

        var currentIconUri: Uri? = null
        var currentBitmap: Bitmap? = null

        override fun createCurrentContentIntent(player: Player): PendingIntent? =
            controller.sessionActivity

        override fun getCurrentContentText(player: Player) =
            controller.metadata.description.subtitle.toString()

        override fun getCurrentContentTitle(player: Player): CharSequence =
            controller.metadata.description.title.toString()

        override fun getCurrentLargeIcon(
            player: Player,
            callback: PlayerNotificationManager.BitmapCallback
        ): Bitmap? {
        ...... 
        }

設定完了 notificaion,再來就是呼叫 notification 的時間點啦,透過播放器的 callback,在 Player.STATE_READY 去啟動 notification。等同於官方文件介紹的在 MediaSessionCompat.Callback.onPlay() 裡去啟動 notification。

override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
            when (playbackState) {
                Player.STATE_BUFFERING,
                Player.STATE_READY -> {
                    notificationManager.showNotificationForPlayer(currentPlayer)
                }
                else -> {
                    notificationManager.hideNotification()
                }
            }
        }

程式碼在這,分支名稱(day13_notification): Fancy/day13_notification


上一篇
播放器架構實作 (5) - ExoPlayer 播歌實作
下一篇
播放器架構實作 (7) - MusicBrowser & MediaController 實作(播音樂啦!)
系列文
Android 音樂播放器自己來30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言