AudioManager , Audio Focus
AudioManager:用於 app 沒有撥放音樂時,還能很直覺的使用音量鍵去調整音樂的音量。
Audio Focus:用於app播放時,可以停止另一個app播放的音樂。
先介紹 Media Session 在做什麼事吧~
MainActivity會先 connect 到 Service ( MediaBrowserServiceCompat ),接著會用到 MediaBrowser 的 CallBack 。
Connect 成功之後,可以使用 MediaBrowser 的 subscribe(CallBack 機制) 去拿到音樂的資料。這裡還要做一件事情,使用 MediaBrowser 去拿 sessionToken 與 MediaControllerCompat 做連結。
接著 MainActivity 可以使用 MediaControllerCompat 發送 message 給 System ,然後 Service 會收到 System 的廣播,去執行對應的事務。
實作看看吧
首先,先設定Gradle:
dependencies {
....
implementation "com.android.support:support-media-compat:27.1.1"
....
}
在 MainActivity Create 一個Browser
mBrowser = MediaBrowserCompat(
this,
ComponentName(this, MusicService::class.java!!), //bind browser service
BrowserConnectionCallback, null)// set Connect callback
Browser 的 CallBack:
private val BrowserConnectionCallback = object : MediaBrowserCompat.ConnectionCallback() {
override fun onConnected() {
Log.e(TAG, "onConnected------")
if (mBrowser!!.isConnected) {
//mediaId is MediaBrowserService.onGetRoot return value
//if Service allow client connect,return value isn't null, valus is root ID
// refuse connect , value is null
val mediaId = mBrowser!!.root
//Browser subscribe Service to get data, it request two parameters, one is mediaId another is calback
//if mediaId subscribed by other , it need to cancel other subscriber.
//Although subscriber will be cover, it will not call onChildCall(Callback)
//google say
// This is temporary: A bug is being fixed that will make subscribe
// consistently call onChildrenLoaded initially, no matter if it is replacing an existing
// subscriber or not. Currently this only happens if the mediaID has no previous
// subscriber or if the media content changes on the service side, so we need to
// unsubscribe first. mBrowser!!.unsubscribe(mediaId) //subscribe will trigger onLoadChildren in MusicService
mBrowser!!.subscribe(mediaId, BrowserSubscriptionCallback)
try {
mController = MediaControllerCompat(applicationContext, mBrowser!!.sessionToken)
mController!!.registerCallback(ControllerCallback)
} catch (e: RemoteException) {
e.printStackTrace()
}
}
}
override fun onConnectionFailed() {
Log.e(TAG, " connect fail")
}
}
看到 mBrowser!!.isConnected,Connect 成功後,這裡先取消其他人的 subscribe(mBrowser!!.unsubscribe(mediaId)),這裡如果直接執行 subscribe 的方法,應該會覆蓋掉原本的 subscriber ,但是有一個 bug ,在拿音樂檔案的時候,subscribe 的 CallBack 不會被呼叫到,所以要先 unsubscribe 其他人。
mController = MediaControllerCompat(applicationContext, mBrowser!!.sessionToken)
mController!!.registerCallback(ControllerCallback)
接著從 connect 拿到的 sessionToken,去 create contoller,那 registerCallback 在做什麼事呢?
它是負責監聽目前音樂(UI)的狀態,例如:使用者按下撥放,這時會呼叫 CallBack 讓 UI 去改變 Play Button 的狀態(例如:變成暫停的 icon)。
或者目前歌曲的名稱(Metadata)改變了,也會被呼叫到。
private val ControllerCallback = object : MediaControllerCompat.Callback() {
/***
* music change state change callback
* @param state
*/
override fun onPlaybackStateChanged(state: PlaybackStateCompat) {
when (state.state) {
PlaybackStateCompat.STATE_NONE// none state
-> {
textTitle!!.text = ""
btnPlay!!.text = "start"
}
PlaybackStateCompat.STATE_PAUSED -> btnPlay!!.text = "start"
PlaybackStateCompat.STATE_PLAYING -> btnPlay!!.text = "pause"
}
}
/**
* change music callback
* @param metadata
*/
override fun onMetadataChanged(metadata: MediaMetadataCompat) {
textTitle!!.text = metadata.description.title
}
}