參考資料
媒體應用架構概覽 | Android 開發(fā)者 | Android Developers
MediaSession | Android Developers
MediaSession框架全解析_qzns木雨的博客-CSDN博客_mediasession
1. 多媒體應用架構
1.1 傳統(tǒng)應用架構
播放音頻或視頻的多媒體應用通常由兩部分組成:
- 播放器:接收傳入的數(shù)據(jù)多媒體辉阶,并輸出音頻或視頻〈穸螅可以是MediaPlayer谆甜、ExoPlayer或其他Player。
- 界面:用于顯示集绰、控制播放器狀態(tài)界面规辱。
眾所周知,如果需要在應用的后臺繼續(xù)播放音頻栽燕,我們就需要把Player放置在Service中罕袋,那么界面與播放器之間通信就非常值得研究了。很長一段時間里碍岔,都是由Service提供一個Binder來實現(xiàn)與播放器之間的通信浴讯。但是往往下拉的狀態(tài)欄和桌面的Widget都需要與Service之間進行通信,這時候Service就不得不通過實現(xiàn)一系列AIDL接口/廣播/ContentProvider完成與其它應用之間的通信付秕,而這些通信手段既增加了應用開發(fā)者之間的溝通成本兰珍,也增加了應用之間的耦合度。
為了解決上面的問題询吴,Android官方從Android5.0開始提供了MediaSession框架掠河。
1.2 MediaSession 框架
MediaSession框架規(guī)范了音視頻應用中界面與播放器之間的通信接口,實現(xiàn)界面與播放器之間的完全解耦猛计∵肽。框架定義了兩個重要的類媒體會話和媒體控制器,它們?yōu)闃嫿ǘ嗝襟w播放器應用提供了一個完善的結構奉瘤。
媒體會話和媒體控制器通過以下方式相互通信:使用與標準播放器操作(播放勾拉、暫停煮甥、停止等)相對應的預定義回調(diào),以及用于定義應用獨有的特殊行為的可擴展自定義調(diào)用藕赞。
2. MediaSession 介紹
MediaSession框架屬于典型的C/S架構成肘,有四個常用的成員類,是整個MediaSession框架流程控制的核心斧蜕。
2.1 客戶端媒體瀏覽器 - MediaBrowser
媒體瀏覽器双霍,用來連接MediaBrowserService
和訂閱數(shù)據(jù),通過它的回調(diào)接口我們可以獲取與Service的連接狀態(tài)以及獲取在Service中的音樂庫數(shù)據(jù)批销。在客戶端(也就是上文我們提到的界面洒闸,或者說是控制端)中創(chuàng)建。
媒體瀏覽器不是線程安全的均芽。所有調(diào)用都應在構造MediaBrowser
的線程上進行丘逸。
@RequiresApi(Build.VERSION_CODES.M)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val component = ComponentName(this, MediaService::class.java)
mMediaBrowser = MediaBrowser(this, component, connectionCallback, null);
mMediaBrowser.connect()
}
2.1.1 MediaBrowser.ConnectionCallback
用于接收與MediaBrowserService連接事件的回調(diào),在創(chuàng)建MediaBrowser
時傳入掀宋。
@RequiresApi(Build.VERSION_CODES.M)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val component = ComponentName(this, MediaService::class.java)
mMediaBrowser = MediaBrowser(this, component, connectionCallback, null);
mMediaBrowser.connect()
}
private val connectionCallback = object : MediaBrowser.ConnectionCallback() {
override fun onConnected() {
super.onConnected()
}
override fun onConnectionFailed() {
super.onConnectionFailed()
}
override fun onConnectionSuspended() {
super.onConnectionSuspended()
}
}
2.1.2 MediaBrowser.ItemCallback
用于返回MediaBrowser.getItem()的結果深纲。
private val connectionCallback = object : MediaBrowser.ConnectionCallback() {
override fun onConnected() {
super.onConnected()
// ...
if(mMediaBrowser.isConnected) {
val mediaId = mMediaBrowser.root
mMediaBrowser.getItem(mediaId, itemCallback)
}
}
}
@RequiresApi(Build.VERSION_CODES.M)
private val itemCallback = object : MediaBrowser.ItemCallback(){
override fun onItemLoaded(item: MediaBrowser.MediaItem?) {
super.onItemLoaded(item)
}
override fun onError(mediaId: String) {
super.onError(mediaId)
}
}
2.1.3 MediaBrowser.MediaItem
包含有關單個媒體項的信息,用于瀏覽/搜索媒體布朦。MediaItem
依賴于服務端提供囤萤,因此框架本身無法保證它包含的值都是正確的。
2.1.4 MediaBrowser.SubscriptionCallback
用于訂與MediaBrowserService中MediaBrowser.MediaItem列表變化的回調(diào)是趴。
private val connectionCallback = object : MediaBrowser.ConnectionCallback() {
override fun onConnected() {
super.onConnected()
// ...
if(mMediaBrowser.isConnected) {
val mediaId = mMediaBrowser.root
// 需要先取消訂閱
mMediaBrowser.unsubscribe(mediaId)
// 服務端會調(diào)用onLoadChildren
mMediaBrowser.subscribe(mediaId, subscribeCallback)
}
}
}
private val subscribeCallback = object : MediaBrowser.SubscriptionCallback(){
override fun onChildrenLoaded(
parentId: String,
children: MutableList<MediaBrowser.MediaItem>
) {
super.onChildrenLoaded(parentId, children)
}
override fun onChildrenLoaded(
parentId: String,
children: MutableList<MediaBrowser.MediaItem>,
options: Bundle
) {
super.onChildrenLoaded(parentId, children, options)
}
override fun onError(parentId: String) {
super.onError(parentId)
}
override fun onError(parentId: String, options: Bundle) {
super.onError(parentId, options)
}
}
2.2 客戶端媒體控制器 - MediaController
媒體控制器,用來向服務端發(fā)送控制指令澄惊,例如:播放唆途、暫停等等,在客戶端中創(chuàng)建掸驱。媒體控制器是線程安全的肛搬。MediaController還有一個關聯(lián)的權限android.permission.MEDIA_CONTENT_CONTROL(不是必須加的權限)必須是系統(tǒng)級應用才可以獲取,幸運的是車載應用一般都是系統(tǒng)級應用毕贼。
MediaController
必須在MediaBrowser連接成功后才可以創(chuàng)建温赔。
private val connectionCallback = object : MediaBrowser.ConnectionCallback() {
override fun onConnected() {
super.onConnected()
// ...
if(mMediaBrowser.isConnected) {
val sessionToken = mMediaBrowser.sessionToken
mMediaController = MediaController(applicationContext,sessionToken)
}
}
}
2.2.1 MediaController.Callback
用于從MediaSession接收回調(diào)。使用方式如下:
private val connectionCallback = object : MediaBrowser.ConnectionCallback() {
override fun onConnected() {
super.onConnected()
// ...
if(mMediaBrowser.isConnected) {
val sessionToken = mMediaBrowser.sessionToken
mMediaController = MediaController(applicationContext,sessionToken)
mMediaController.registerCallback(controllerCallback)
}
}
}
private val controllerCallback = object : MediaController.Callback() {
override fun onAudioInfoChanged(info: MediaController.PlaybackInfo?) {
super.onAudioInfoChanged(info)
}
override fun onExtrasChanged(extras: Bundle?) {
super.onExtrasChanged(extras)
}
// ...
}
2.2.2 MediaController.PlaybackInfo
保存有關當前播放以及如何處理此會話的音頻的信息鬼癣。使用方式如下:
// 獲取當前回話播放的音頻信息
val playbackInfo = mMediaController.playbackInfo
2.2.3 MediaController.TransportControls
用于控制會話中媒體播放的接口陶贼。這允許客戶端向Session發(fā)送媒體控制命令。使用方式如下:
private val connectionCallback = object : MediaBrowser.ConnectionCallback() {
override fun onConnected() {
super.onConnected()
// ...
if(mMediaBrowser.isConnected) {
val sessionToken = mMediaBrowser.sessionToken
mMediaController = MediaController(applicationContext,sessionToken)
// 播放媒體
mMediaController.transportControls.play()
// 暫停媒體
mMediaController.transportControls.pause()
}
}
}
2.3 服務端媒體瀏覽服務 - MediaBrowserService
媒體瀏覽器服務待秃,繼承自Service
拜秧,MediaBrowserService
屬于服務端,也是承載播放器(如MediaPlayer章郁、ExoPlayer等)和MediaSession
的容器枉氮。
實現(xiàn)MediaBrowserService
時會要求復寫onGetRoot
和onLoadChildren
兩個方法。
onGetRoot
通過的返回值決定是否允許客戶端的MediaBrowser
連接到MediaBrowserService
。
當客戶端調(diào)用MediaBrowser.subscribe
時會觸發(fā)onLoadChildren
方法聊替。
const val FOLDERS_ID = "__FOLDERS__"
const val ARTISTS_ID = "__ARTISTS__"
const val ALBUMS_ID = "__ALBUMS__"
const val GENRES_ID = "__GENRES__"
const val ROOT_ID = "__ROOT__"
class MediaService : MediaBrowserService() {
override fun onGetRoot(
clientPackageName: String,
clientUid: Int,
rootHints: Bundle?
): BrowserRoot? {
// 由MediaBrowser.connect觸發(fā)楼肪,可以通過返回null拒絕客戶端的連接。
return BrowserRoot(ROOT_ID, null)
}
override fun onLoadChildren(
parentId: String,
result: Result<MutableList<MediaBrowser.MediaItem>>
) {
// 由MediaBrowser.subscribe觸發(fā)
when (parentId) {
ROOT_ID -> {
// 查詢本地媒體庫
// ...
// 將此消息與當前線程分離惹悄,并允許稍后進行sendResult調(diào)用
result.detach()
// 設定到 result 中
result.sendResult()
}
FOLDERS_ID -> {
}
ALBUMS_ID -> {
}
ARTISTS_ID -> {
}
GENRES_ID -> {
}
else -> {
}
}
}
}
然后還需要在manifest中注冊這個Service淹辞。
<service
android:name=".MediaService"
android:label="@string/service_name">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
2.3.1 MediaBrowserService.BrowserRoot
包含瀏覽器服務首次連接時需要返回給客戶端的信息。
MediaBrowserService.BrowserRoot API 列表
方法名 | 備注 |
---|---|
Bundle getExtras() | 獲取有關瀏覽器服務的附加信息俘侠。 |
String getRootId() | 獲取用于瀏覽的根 ID象缀。 |
2.3.2 MediaBrowserService.Result<T>
包含瀏覽器服務返回給客戶端的結果集。通過調(diào)用sendResult()
將結果返回給調(diào)用方爷速,但是在此之前需要調(diào)用detach()
央星。
MediaBrowserService.Result API 列表
方法名 | 備注 |
---|---|
void detach() | 將此消息與當前線程分離,并允許稍后進行調(diào)用sendResult(T) |
void sendResult(T result) | 將結果發(fā)送回調(diào)用方惫东。 |
2.4 服務端媒體會話 - MediaSession
媒體會話莉给,即受控端。通過設定MediaSession.Callback
回調(diào)來接收媒體控制器MediaController
發(fā)送的指令廉沮。
創(chuàng)建MediaSession
后還需要調(diào)用setSessionToken()
方法設置用于和**控制器配對的令牌颓遏。使用方式如下:
const val FOLDERS_ID = "__FOLDERS__"
const val ARTISTS_ID = "__ARTISTS__"
const val ALBUMS_ID = "__ALBUMS__"
const val GENRES_ID = "__GENRES__"
const val ROOT_ID = "__ROOT__"
class MediaService : MediaBrowserService() {
private lateinit var mediaSession: MediaSession;
override fun onCreate() {
super.onCreate()
mediaSession = MediaSession(this, "TAG")
mediaSession.setCallback(callback)
sessionToken = mediaSession.sessionToken
}
// 與MediaController.transportControls中的大部分方法都是一一對應的
// 在該方法中實現(xiàn)對 播放器 的控制,
private val callback = object : MediaSession.Callback() {
override fun onPlay() {
super.onPlay()
// 處理 播放器 的播放邏輯滞时。
// 車載應用的話叁幢,別忘了處理音頻焦點
}
override fun onPause() {
super.onPause()
}
}
override fun onGetRoot(
clientPackageName: String,
clientUid: Int,
rootHints: Bundle?
): BrowserRoot? {
Log.e("TAG", "onGetRoot: $rootHints")
return BrowserRoot(ROOT_ID, null)
}
override fun onLoadChildren(
parentId: String,
result: Result<MutableList<MediaBrowser.MediaItem>>
) {
Log.e("TAG", "onLoadChildren: $parentId")
result.detach()
when (parentId) {
ROOT_ID -> {
result.sendResult(null)
}
FOLDERS_ID -> {
}
ALBUMS_ID -> {
}
ARTISTS_ID -> {
}
GENRES_ID -> {
}
else -> {
}
}
}
override fun onLoadItem(itemId: String?, result: Result<MediaBrowser.MediaItem>?) {
super.onLoadItem(itemId, result)
Log.e("TAG", "onLoadItem: $itemId")
}
}
2.4.1 MediaSession.Callback
接收來自控制器和系統(tǒng)的媒體按鈕、傳輸控件和命令坪稽。與MediaController.transportControls中的大部分方法都是一一對應的曼玩。使用方式如下:
override fun onCreate() {
super.onCreate()
mediaSession = MediaSession(this, "TAG")
mediaSession.setCallback(callback)
sessionToken = mediaSession.sessionToken
}
// 與MediaController.transportControls中的方法是一一對應的。
// 在該方法中實現(xiàn)對 播放器 的控制窒百,
private val callback = object : MediaSession.Callback() {
override fun onPlay() {
super.onPlay()
// 處理 播放器 的播放邏輯黍判。
// 車載應用的話,別忘了處理音頻焦點
// ...
if (!mediaSession.isActive) {
mediaSession.isActive = true
}
// 更新播放狀態(tài).
val state = PlaybackState.Builder()
.setState(
PlaybackState.STATE_PLAYING,1,1f
)
.build()
// 此時MediaController.Callback.onPlaybackStateChanged會回調(diào)
mediaSession.setPlaybackState(state)
}
override fun onPause() {
super.onPause()
}
override fun onStop() {
super.onStop()
}
}
2.4.2 MediaSession.QueueItem
作為播放隊列一部分的單個項目篙梢。它包含隊列中項目及其 ID 的說明顷帖。
MediaSession.QueueItem API 列表
方法名 | 備注 |
---|---|
MediaDescription getDescription() | 返回介質(zhì)的說明。包含媒體的基礎信息如:標題渤滞、封面等等贬墩。 |
long getQueueId() | 獲取此項目的隊列 ID。 |
2.4.3 MediaSession.Token
表示正在進行的會話蔼水。這可以通過會話所有者傳遞給客戶端震糖,以允許客戶端與服務端之間建立通信。
2.5 播放器狀態(tài) - PlaybackState
用于承載播放狀態(tài)的類趴腋。如當前播放位置和當前控制功能吊说。
在MediaSession.Callback
更改狀態(tài)后需要調(diào)用MediaSession.setPlaybackState
把狀態(tài)同步給客戶端论咏。使用方式如下:
private val callback = object : MediaSession.Callback() {
override fun onPlay() {
super.onPlay()
// ...
// 更新狀態(tài)
val state = PlaybackState.Builder()
.setState(
PlaybackState.STATE_PLAYING,1,1f
)
.build()
mediaSession.setPlaybackState(state)
}
}
2.5.1 PlaybackState.Builder
基于建造者模式來生成PlaybackState對象。使用方式如下:
PlaybackState state = new PlaybackState.Builder()
.setState(PlaybackState.STATE_PLAYING,
mMediaPlayer.getCurrentPosition(), PLAYBACK_SPEED)
.setActions(PLAYING_ACTIONS)
.addCustomAction(mShuffle)
.setActiveQueueItemId(mQueue.get(mCurrentQueueIdx).getQueueId())
.build();
2.5.2 PlaybackState.CustomAction
CustomActions
可用于通過將特定于應用程序的操作發(fā)送給MediaControllers
颁井,這樣就可以擴展標準傳輸控件的功能厅贪。使用方式如下:
CustomAction action = new CustomAction
.Builder("android.car.media.localmediaplayer.shuffle",
mContext.getString(R.string.shuffle),
R.drawable.shuffle)
.build();
PlaybackState state = new PlaybackState.Builder()
.setState(PlaybackState.STATE_PLAYING,
mMediaPlayer.getCurrentPosition(), PLAYBACK_SPEED)
.setActions(PLAYING_ACTIONS)
.addCustomAction(action)
.setActiveQueueItemId(mQueue.get(mCurrentQueueIdx).getQueueId())
.build();
PlaybackState.CustomAction API 說明
方法名 | 備注 |
---|---|
String getAction() | 返回CustomAction的action。 |
Bundle getExtras() | 返回附加項雅宾,這些附加項提供有關操作的其他特定于應用程序的信息养涮,如果沒有,則返回 null眉抬。 |
int getIcon() | 返回package中圖標的資源 ID贯吓。 |
CharSequence getName() | 返回此操作的顯示名稱。 |
2.6 元數(shù)據(jù)類 - MediaMetadata
包含有關項目的基礎數(shù)據(jù)蜀变,例如標題悄谐、藝術家等。一般需要服務端從本地數(shù)據(jù)庫或遠端查詢出原始數(shù)據(jù)在封裝成MediaMetadata再通過MediaSession.setMetadata(metadata)
返回到客戶端的MediaController.Callback.onMetadataChanged
中库北。
MediaMetadata API 說明
方法名 | 備注 |
---|---|
boolean containsKey(String key) | 如果給定的key包含在元數(shù)據(jù)中爬舰,則返回 true |
int describeContents() | 描述此可打包實例的封送處理表示中包含的特殊對象的種類。 |
Bitmap getBitmap(String key) | 返回給定的key的Bitmap;如果給定key不存在位圖寒瓦,則返回 null情屹。 |
int getBitmapDimensionLimit() | 獲取創(chuàng)建此元數(shù)據(jù)時位圖的寬度/高度限制(以像素為單位)。 |
MediaDescription getDescription() | 獲取此元數(shù)據(jù)的簡單說明以進行顯示杂腰。 |
long getLong(String key) | 返回與給定key關聯(lián)的值垃你,如果給定key不再存在,則返回 0L颈墅。 |
Rating getRating(String key) | 對于給定的key返回Rating;如果給定key不存在Rating蜡镶,則返回 null。 |
String getString(String key) | 以 String 格式返回與給定key關聯(lián)的文本值恤筛,如果給定key不存在所需類型的映射,或者null值顯式與該key關聯(lián)芹橡,則返回 null毒坛。 |
CharSequence getText(String key) | 返回與給定鍵關聯(lián)的值,如果給定鍵不存在所需類型的映射林说,或者與該鍵顯式關聯(lián) null 值煎殷,則返回 null。 |
Set<String> keySet() | 返回一個 Set腿箩,其中包含在此元數(shù)據(jù)中用作key的字符串豪直。 |
int size() | 返回此元數(shù)據(jù)中的字段數(shù)。 |
MediaMetadata 常用Key
方法名 | 備注 |
---|---|
METADATA_KEY_ALBUM | 媒體的唱片集標題珠移。 |
METADATA_KEY_ALBUM_ART | 媒體原始來源的相冊的插圖弓乙,Bitmap格式 |
METADATA_KEY_ALBUM_ARTIST | 媒體原始來源的專輯的藝術家末融。 |
METADATA_KEY_ALBUM_ART_URI | 媒體原始源的相冊的圖稿,Uri格式(推薦使用) |
METADATA_KEY_ART | 媒體封面暇韧,Bitmap格式 |
METADATA_KEY_ART_URI | 媒體的封面勾习,Uri格式。 |
METADATA_KEY_ARTIST | 媒體的藝術家懈玻。 |
METADATA_KEY_AUTHOR | 媒體的作者巧婶。 |
METADATA_KEY_BT_FOLDER_TYPE | 藍牙 AVRCP 1.5 的 6.10.2.2 節(jié)中指定的媒體的藍牙文件夾類型。 |
METADATA_KEY_COMPILATION | 媒體的編譯狀態(tài)涂乌。 |
METADATA_KEY_COMPOSER | 媒體的作曲家艺栈。 |
METADATA_KEY_DATE | 媒體的創(chuàng)建或發(fā)布日期。 |
METADATA_KEY_DISC_NUMBER | 介質(zhì)原始來源的光盤編號湾盒。 |
METADATA_KEY_DISPLAY_DESCRIPTION | 適合向用戶顯示的說明湿右。 |
METADATA_KEY_DISPLAY_ICON | 適合向用戶顯示的圖標或縮略圖。 |
METADATA_KEY_DISPLAY_ICON_URI | 適合向用戶顯示的圖標或縮略圖历涝, Uri格式诅需。 |
METADATA_KEY_DISPLAY_SUBTITLE | 適合向用戶顯示的副標題。 |
METADATA_KEY_DISPLAY_TITLE | 適合向用戶顯示的標題荧库。 |
METADATA_KEY_DURATION | 媒體的持續(xù)時間(以毫秒為單位)堰塌。 |
METADATA_KEY_GENRE | 媒體的流派。 |
METADATA_KEY_MEDIA_ID | 用于標識內(nèi)容的字符串Key分衫。 |
METADATA_KEY_MEDIA_URI | 媒體內(nèi)容场刑,Uri格式。 |
METADATA_KEY_NUM_TRACKS | 媒體原始源中的曲目數(shù)蚪战。 |
METADATA_KEY_RATING | 媒體的總體評分牵现。 |
METADATA_KEY_TITLE | 媒體的標題。 |
METADATA_KEY_TRACK_NUMBER | 媒體的磁道編號邀桑。 |
METADATA_KEY_USER_RATING | 用戶對媒體的分級瞎疼。 |
METADATA_KEY_WRITER | 媒體作家。 |
String METADATA_KEY_YEAR | 媒體創(chuàng)建或發(fā)布為長的年份壁畸。 |
3. MediaSession 簡單實踐
MediaSession 框架核心類通信過程如下圖所示贼急。
客戶端源碼如下所示:
class MainActivity : AppCompatActivity() {
private lateinit var mMediaBrowser: MediaBrowser
private lateinit var mMediaController: MediaController
@RequiresApi(Build.VERSION_CODES.M)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val component = ComponentName(this, MediaService::class.java)
mMediaBrowser = MediaBrowser(this, component, connectionCallback, null);
// 連接到MediaBrowserService,會觸發(fā)MediaBrowserService的onGetRoot方法捏萍。
mMediaBrowser.connect()
findViewById<Button>(R.id.btn_play).setOnClickListener {
mMediaController.transportControls.play()
}
}
private val connectionCallback = object : MediaBrowser.ConnectionCallback() {
override fun onConnected() {
super.onConnected()
if (mMediaBrowser.isConnected) {
val sessionToken = mMediaBrowser.sessionToken
mMediaController = MediaController(applicationContext, sessionToken)
mMediaController.registerCallback(controllerCallback)
// 獲取根mediaId
val rootMediaId = mMediaBrowser.root
// 獲取根mediaId的item列表,會觸發(fā)MediaBrowserService.onLoadItem方法
mMediaBrowser.getItem(rootMediaId,itemCallback)
mMediaBrowser.unsubscribe(rootMediaId)
// 訂閱服務端 media item的改變令杈,會觸發(fā)MediaBrowserService.onLoadChildren方法
mMediaBrowser.subscribe(rootMediaId, subscribeCallback)
}
}
}
private val controllerCallback = object : MediaController.Callback() {
override fun onPlaybackStateChanged(state: PlaybackState?) {
super.onPlaybackStateChanged(state)
Log.d("TAG", "onPlaybackStateChanged: $state")
when(state?.state){
PlaybackState.STATE_PLAYING ->{
// 處理UI
}
PlaybackState.STATE_PAUSED ->{
// 處理UI
}
// 還有其它狀態(tài)需要處理
}
}
// 音頻信息走敌,音量
override fun onAudioInfoChanged(info: MediaController.PlaybackInfo?) {
super.onAudioInfoChanged(info)
val currentVolume = info?.currentVolume
// 顯示在UI上
}
override fun onMetadataChanged(metadata: MediaMetadata?) {
super.onMetadataChanged(metadata)
val artUri = metadata?.getString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI)
// 顯示UI上
}
override fun onSessionEvent(event: String, extras: Bundle?) {
super.onSessionEvent(event, extras)
Log.d("TAG", "onSessionEvent: $event")
}
// ...
}
private val subscribeCallback = object : MediaBrowser.SubscriptionCallback() {
override fun onChildrenLoaded(
parentId: String,
children: MutableList<MediaBrowser.MediaItem>
) {
super.onChildrenLoaded(parentId, children)
}
override fun onChildrenLoaded(
parentId: String,
children: MutableList<MediaBrowser.MediaItem>,
options: Bundle
) {
super.onChildrenLoaded(parentId, children, options)
}
override fun onError(parentId: String) {
super.onError(parentId)
}
}
private val itemCallback = object : MediaBrowser.ItemCallback() {
override fun onItemLoaded(item: MediaBrowser.MediaItem?) {
super.onItemLoaded(item)
}
override fun onError(mediaId: String) {
super.onError(mediaId)
}
}
}
服務端源碼如下所示:
const val FOLDERS_ID = "__FOLDERS__"
const val ARTISTS_ID = "__ARTISTS__"
const val ALBUMS_ID = "__ALBUMS__"
const val GENRES_ID = "__GENRES__"
const val ROOT_ID = "__ROOT__"
class MediaService : MediaBrowserService() {
// 控制是否允許客戶端連接,并返回root media id給客戶端
override fun onGetRoot(
clientPackageName: String,
clientUid: Int,
rootHints: Bundle?
): BrowserRoot? {
Log.e("TAG", "onGetRoot: $rootHints")
return BrowserRoot(ROOT_ID, null)
}
// 處理客戶端的訂閱信息
override fun onLoadChildren(
parentId: String,
result: Result<MutableList<MediaBrowser.MediaItem>>
) {
Log.e("TAG", "onLoadChildren: $parentId")
result.detach()
when (parentId) {
ROOT_ID -> {
result.sendResult(null)
}
FOLDERS_ID -> {
}
ALBUMS_ID -> {
}
ARTISTS_ID -> {
}
GENRES_ID -> {
}
else -> {
}
}
}
override fun onLoadItem(itemId: String?, result: Result<MediaBrowser.MediaItem>?) {
super.onLoadItem(itemId, result)
Log.e("TAG", "onLoadItem: $itemId")
// 根據(jù)itemId逗噩,返回對用MediaItem
result?.detach()
result?.sendResult(null)
}
private lateinit var mediaSession: MediaSession;
override fun onCreate() {
super.onCreate()
mediaSession = MediaSession(this, "TAG")
mediaSession.setCallback(callback)
// 設置token
sessionToken = mediaSession.sessionToken
}
// 與MediaController.transportControls中的方法是一一對應的掉丽。
// 在該方法中實現(xiàn)對 播放器 的控制跌榔,
private val callback = object : MediaSession.Callback() {
override fun onPlay() {
super.onPlay()
// 處理 播放器 的播放邏輯。
// 車載應用的話机打,別忘了處理音頻焦點
Log.e("TAG", "onPlay:")
if (!mediaSession.isActive) {
mediaSession.isActive = true
}
// 更新狀態(tài)
val state = PlaybackState.Builder()
.setState(
PlaybackState.STATE_PLAYING, 1, 1f
)
.build()
mediaSession.setPlaybackState(state)
}
override fun onPause() {
super.onPause()
}
override fun onStop() {
super.onStop()
}
// 還有其它方法需要復寫
}
}
上述的代碼只是幫助理解MediaSession框架的通信過程矫户,本身的功能非常的簡陋。上一篇Android車載應用開發(fā)與分析(6)- 車載多媒體(一)- 音視頻基礎知識與MediaPlayer中介紹了音視頻的基礎知識和MediaPlayer的生命周期残邀,再通過本篇了解了MediaSession框架的基礎使用皆辽,下一篇我們就可以開始解析車載Android中的原生LocalMedia應用了。
4. MediaSession API 列表
4.1 MediaBrowser 相關組件 API 列表
4.1.1 MediaBrowser
方法名 | 備注 |
---|---|
void connect() | 連接到媒體瀏覽器服務芥挣。 |
void disconnect() | 斷開與媒體瀏覽器服務的連接驱闷。 |
Bundle getExtras() | 獲取介質(zhì)服務的任何附加信息。 |
void getItem(String mediaId, MediaBrowser.ItemCallback cb) | 從連接的服務中檢索特定的MediaItem |
String getRoot() | 獲取根ID空免。 |
ComponentName getServiceComponent() | 獲取媒體瀏覽器連接到的服務組件空另。 |
MediaSession.Token getSessionToken() | 獲取與媒體瀏覽器關聯(lián)的媒體會話Token。 |
boolean isConnected() | 返回瀏覽器是否連接到服務蹋砚。 |
void subscribe(String parentId,Bundle options, MediaBrowser.SubscriptionCallback callback) | 使用特定于服務的參數(shù)進行查詢扼菠,以獲取有關指定 ID 中包含的媒體項的信息,并訂閱以在更新更改時接收更新。 |
void subscribe(String parentId, MediaBrowser.SubscriptionCallback callback) | 詢有關包含在指定 ID 中的媒體項的信息,并訂閱以在更改時接收更新煤禽。 |
void unsubscribe(String parentId) | 取消訂閱指定媒體 ID 。 |
void unsubscribe(String parentId, MediaBrowser.SubscriptionCallback callback) | 通過回調(diào)取消訂閱對指定媒體 ID秧饮。 |
4.1.2 MediaBrowser.ConnectionCallback
方法 | 備注 |
---|---|
onConnected() | 與MediaBrowserService連接成功。在調(diào)用MediaBrowser.connect() 后才會有回調(diào)泽篮。 |
onConnectionFailed() | 與MediaBrowserService連接失敗盗尸。 |
onConnectionSuspended() | 與MediaBrowserService連接斷開。 |
4.1.3 MediaBrowser. ItemCallback
方法名 | 備注 |
---|---|
onError(String mediaId) | 檢索時出錯帽撑,或者連接的服務不支持時回調(diào)泼各。 |
onItemLoaded(MediaBrowser.MediaItem item) | 返回Item時調(diào)用。 |
4.1.4 MediaBrowser. MediaItem
方法名 | 備注 |
---|---|
int describeContents() | 描述此可打包實例的封送處理表示中包含的特殊對象的種類亏拉。 |
MediaDescription getDescription() | 獲取介質(zhì)的說明历恐。包含媒體的基礎信息如:標題、封面等等专筷。 |
int getFlags() | 獲取項的標志。FLAG_BROWSABLE :表示Item具有自己的子項蒸苇。FLAG_PLAYABLE :表示Item可播放 |
String getMediaId() | 返回此項的媒體 ID磷蛹。 |
boolean isBrowsable() | 返回此項目是否可瀏覽。 |
boolean isPlayable() | 返回此項是否可播放溪烤。 |
4.1.5 MediaBrowser.SubscriptionCallback
方法名 | 備注 |
---|---|
onChildrenLoaded(String parentId, List<MediaBrowser.MediaItem> children) | 在加載或更新子項列表時回調(diào)味咳。 |
onChildrenLoaded(String parentId, List<MediaBrowser.MediaItem> children,Bundle options) | 在加載或更新子項列表時回調(diào)庇勃。 |
onError(String parentId) | 當 ID 不存在或訂閱時出現(xiàn)其他錯誤時回調(diào)。 |
onError(String parentId, Bundle options) | 當 ID 不存在或訂閱時出現(xiàn)其他錯誤時回調(diào)槽驶。 |
4.2 MediaController 相關組件 API 列表
4.2.1 MediaController
方法名 | 備注 |
---|---|
void adjustVolume (int direction, int flags) | 調(diào)整此會話正在播放的輸出的音量责嚷。 |
boolean dispatchMediaButtonEvent (KeyEvent keyEvent) | 將指定的媒體按鈕事件發(fā)送到會話。 |
Bundle getExtras() | 獲取此會話的附加內(nèi)容掂铐。 |
long getFlags() | 獲取此會話的標志罕拂。 |
MediaMetadata getMetadata() | 獲取此會話的當前Metadata。 |
String getPackageName() | 獲取會話所有者的程序包名稱全陨。 |
MediaController.PlaybackInfo getPlaybackInfo() | 獲取此會話的當前播放信息爆班。 |
PlaybackState getPlaybackState() | 獲取此會話的當前播放狀態(tài)。 |
List<MediaSession.QueueItem> getQueue() | 獲取此會話的當前播放隊列(如果已設置)辱姨。 |
CharSequence getQueueTitle() | 獲取此會話的隊列標題柿菩。 |
int getRatingType() | 獲取會話支持的評級類型。 |
PendingIntent getSessionActivity() | 獲取啟動與此會話關聯(lián)的 UI 的意圖(如果存在)雨涛。 |
Bundle getSessionInfo() | 獲取創(chuàng)建會話時設置的其他會話信息枢舶。 |
MediaSession.Token getSessionToken() | 獲取連接到的會話的令牌。 |
String getTag() | 獲取會話的標記以進行調(diào)試替久。 |
MediaController.TransportControls getTransportControls() | 獲取TransportControls實例以將控制操作發(fā)送到關聯(lián)的會話凉泄。 |
void registerCallback (MediaController.Callback callback, Handler handler) | 注冊回調(diào)以從會話接收更新。 |
void registerCallback (MediaController.Callback callback) | 注冊回調(diào)以從會話接收更新侣肄。 |
void sendCommand (String command, Bundle args, ResultReceiver cb) | 向會話發(fā)送通用命令旧困。 |
void setVolumeTo (int value, int flags) | 設置此會話正在播放的輸出的音量。 |
void unregisterCallback (MediaController.Callback callback) | 注銷指定的回調(diào)稼锅。 |
4.2.2 MediaController.Callback
方法名 | 備注 |
---|---|
void onAudioInfoChanged (MediaController.PlaybackInfo info) | 當前音頻信息發(fā)生改變吼具。 |
void onExtrasChanged (Bundle extras) | 當前附加內(nèi)容發(fā)生改變。 |
void onMetadataChanged (MediaMetadata metadata) | 當前Metadata發(fā)生改變矩距。 |
void onPlaybackStateChanged(PlaybackState state) | 當前播放狀態(tài)發(fā)生改變拗盒。客戶端通過該回調(diào)來顯示界面上音視頻的播放狀態(tài)锥债。 |
void onQueueChanged (List<MediaSession.QueueItem> queue) | 當前隊列中項目發(fā)生改變陡蝇。 |
void onQueueTitleChanged (CharSequence title) | 當前隊列標題發(fā)生改變。 |
void onSessionDestroyed() | 會話銷毀哮肚。 |
void onSessionEvent (String event, Bundle extras) | MediaSession所有者發(fā)送的自定義事件登夫。 |
4.2.3 MediaController. PlaybackInfo
方法名 | 備注 |
---|---|
AudioAttributes getAudioAttributes() | 獲取此會話的音頻屬性。 |
int getCurrentVolume() | 獲取此會話的當前音量允趟。 |
int getMaxVolume() | 獲取可為此會話設置的最大音量恼策。 |
int getPlaybackType() | 獲取影響音量處理的播放類型。 |
int getVolumeControl() | 獲取可以使用的音量控件的類型潮剪。 |
String getVolumeControlId() | 獲取此會話的音量控制 ID涣楷。 |
4.2.4 MediaController. TransportControls
方法名 | 備注 |
---|---|
void fastForward() | 開始快進分唾。 |
void pause() | 請求播放器暫停播放并保持在當前位置。 |
void play() | 請求播放器在其當前位置開始播放狮斗。 |
void playFromMediaId (String mediaId, Bundle extras) | 請求播放器開始播放特定媒體 ID绽乔。 |
void playFromSearch (String query, Bundle extras) | 請求播放器開始播放特定的搜索查詢。 |
void playFromUri (Uri uri, Bundle extras) | 請求播放器開始播放特定Uri碳褒。 |
void prepare() | 請求播放器準備播放折砸。 |
void prepareFromMediaId (String mediaId, Bundle extras) | 請求播放器為特定媒體 ID 準備播放。 |
void prepareFromSearch (String query, Bundle extras) | 請求播放器為特定搜索查詢準備播放骤视。 |
void prepareFromUri (Uri uri, Bundle extras) | 請求播放器為特定Uri鞍爱。 |
void rewind() | 開始倒帶。 |
void seekTo(long pos) | 移動到媒體流中的新位置专酗。 |
void sendCustomAction (PlaybackState.CustomAction customAction, Bundle args) | 發(fā)送自定義操作以供MediaSession執(zhí)行睹逃。 |
void sendCustomAction (String action,Bundle args) | 將自定義操作中的 id 和 args 發(fā)送回去,以便MediaSession執(zhí)行祷肯。 |
void setPlaybackSpeed (float speed) | 設置播放速度沉填。 |
void setRating(Rating rating) | 對當前內(nèi)容進行評級。 |
void skipToNext() | 跳到下一項佑笋。 |
void skipToPrevious() | 跳到上一項翼闹。 |
void skipToQueueItem(long id) | 在播放隊列中播放具有特定 ID 的項目。 |
void stop() | 請求播放器停止播放;它可以以任何適當?shù)姆绞角宄錉顟B(tài)蒋纬。 |
4.3 MediaBrowserService 相關組件 API 列表
4.3.1 MediaBrowserService
方法名 | 備注 |
---|---|
final Bundle getBrowserRootHints() | 獲取從當前連接 MediaBrowser的發(fā)送的根提示猎荠。 |
final MediaSessionManager.RemoteUserInfo getCurrentBrowserInfo() | 獲取發(fā)送當前請求的瀏覽器信息。 |
MediaSession.Token getSessionToken() | 獲取會話令牌蜀备,如果尚未創(chuàng)建會話令牌或已銷毀會話令牌关摇,則獲取 null。 |
void notifyChildrenChanged(String parentId) | 通知所有連接的媒體瀏覽器指定父 ID 的子級已經(jīng)更改碾阁。 |
void notifyChildrenChanged(String parentId, Bundle options) | 通知所有連接的媒體瀏覽器指定父 ID 的子級已經(jīng)更改输虱。 |
abstract MediaBrowserService.BrowserRoot onGetRoot(String clientPackageName,int clientUid, Bundle rootHints) | 獲取供特定客戶端瀏覽的根信息。由MediaBrowser.connect 觸發(fā)脂凶,可以通過返回null拒絕客戶端的連接宪睹。 |
abstract void onLoadChildren(String parentId, Result<List<MediaBrowser.MediaItem>> result) | 獲取有關媒體項的子項的信息。由MediaBrowser.subscribe 觸發(fā)蚕钦。 |
void onLoadChildren(String parentId, Result<List<MediaBrowser.MediaItem>> result,Bundle options) | 獲取有關媒體項的子項的信息亭病。由MediaBrowser.subscribe 觸發(fā)。 |
void onLoadItem(String itemId, Result<MediaBrowser.MediaItem> result) | 獲取有關特定媒體項的信息嘶居。由MediaBrowser.getItem 觸發(fā)命贴。 |
void setSessionToken(MediaSession.Token token) | 設置媒體會話。 |
4.3.2 MediaBrowserService.BrowserRoot
方法名 | 備注 |
---|---|
Bundle getExtras() | 獲取有關瀏覽器服務的附加信息。 |
String getRootId() | 獲取用于瀏覽的根 ID胸蛛。 |
4.3.3 MediaBrowserService.Result
方法名 | 備注 |
---|---|
void detach() | 將此消息與當前線程分離,并允許稍后進行調(diào)用sendResult(T) |
void sendResult(T result) | 將結果發(fā)送回調(diào)用方樱报。 |
4.4 MediaSession 相關組件 API 列表
4.4.1 MediaSession
方法名 | 備注 |
---|---|
MediaController getController() | 獲取此會話的控制器葬项。 |
MediaSessionManager.RemoteUserInfo getCurrentControllerInfo() | 獲取發(fā)送當前請求的控制器信息。 |
MediaSession.Token getSessionToken() | 獲取此會話令牌對象迹蛤。 |
boolean isActive() | 獲取此會話的當前活動狀態(tài)民珍。 |
void release() | 當應用完成播放時,必須調(diào)用此項盗飒。 |
void sendSessionEvent (String event, Bundle extras) | 將專有事件發(fā)送給監(jiān)聽此會話的所有MediaController嚷量。會觸發(fā)MediaController.Callback.onSessionEvent。 |
void setActive(boolean active) | 設置此會話當前是否處于活動狀態(tài)并準備好接收命令逆趣。 |
void setCallback (MediaSession.Callback callback) | 設置回調(diào)以接收媒體會話的更新蝶溶。 |
void setCallback (MediaSession.Callback callback,Handler handler) | 設置回調(diào)以接收媒體會話的更新。 |
void setExtras(Bundle extras) | 設置一些可與MediaSession關聯(lián)的附加功能宣渗。 |
void setFlags(int flags) | 為會話設置標志抖所。 |
void setMediaButtonBroadcastReceiver(ComponentName broadcastReceiver) | 設置應接收媒體按鈕的清單聲明類的組件名稱。 |
void setMediaButtonReceiver(PendingIntent mbr) | 此方法在 API 級別 31 中已棄用痕囱。改用setMediaButtonBroadcastReceiver(android.content.ComponentName)田轧。 |
void setMetadata(MediaMetadata metadata) | 更新當前MediaMetadata。 |
void setPlaybackState(PlaybackState state) | 更新當前播放狀態(tài)鞍恢。 |
void setPlaybackToLocal(AudioAttributes attributes) | 設置此會話音頻的屬性傻粘。 |
void setPlaybackToRemote(VolumeProvider volumeProvider) | 將此會話配置為使用遠程音量處理。 |
void setQueue(List<MediaSession.QueueItem> queue) | 更新播放隊列中的項目列表帮掉。 |
void setQueueTitle(CharSequence title) | 設置播放隊列的標題弦悉。 |
void setRatingType(int type) | 設置此會話使用的評級樣式。 |
void setSessionActivity(PendingIntent pi) | 設置啟動此會話的Activity的Intent旭寿。 |
4.4.2 MediaSession.Callback
方法名 | 備注 |
---|---|
void onCommand(String command,Bundle args,ResultReceiver cb) | 當控制器已向此會話發(fā)送命令時調(diào)用警绩。 |
void onCustomAction(String action, Bundle extras) | 當要執(zhí)行MediaControllerPlaybackState.CustomAction時調(diào)用。 |
void onFastForward() | 處理快進請求盅称。 |
boolean onMediaButtonEvent(Intent mediaButtonIntent) | 當按下媒體按鈕并且此會話具有最高優(yōu)先級或控制器向會話發(fā)送媒體按鈕事件時調(diào)用肩祥。 |
void onPause() | 處理暫停播放的請求。 |
void onPlay() | 處理開始播放的請求缩膝。 |
void onPlayFromMediaId(String mediaId, Bundle extras) | 處理播放應用提供的特定mediaId的播放請求混狠。 |
void onPlayFromSearch(String query, Bundle extras) | 處理從搜索查詢開始播放的請求。 |
void onPlayFromUri(Uri uri, Bundle extras) | 處理播放由URI表示的特定媒體項的請求疾层。 |
void onPrepare() | 處理準備播放的請求将饺。 |
void onPrepareFromMediaId(String mediaId, Bundle extras) | 處理應用提供的特定mediaId的準備播放請求 |
void onPrepareFromSearch(String query, Bundle extras) | 處理準備從搜索查詢播放的請求。 |
void onPrepareFromUri(Uri uri, Bundle extras) | 處理由URI表示的特定媒體項的準備請求。 |
void onRewind() | 處理倒帶請求予弧。 |
void onSeekTo(long pos) | 處理跳轉到特定位置的請求刮吧。 |
void onSetPlaybackSpeed(float speed) | 處理修改播放速度的請求。 |
void onSetRating(Rating rating) | 處理設定評級的請求掖蛤。 |
void onSkipToNext() | 處理要跳到下一個媒體項的請求杀捻。 |
void onSkipToPrevious() | 處理要跳到上一個媒體項的請求。 |
void onSkipToQueueItem(long id) | 處理跳轉到播放隊列中具有給定 ID 的項目的請求蚓庭。 |
void onStop() | 處理停止播放的請求致讥。 |
4.4.3 MediaSession.QueueItem
方法名 | 備注 |
---|---|
MediaDescription getDescription() | 返回介質(zhì)的說明。包含媒體的基礎信息如:標題器赞、封面等等垢袱。 |
long getQueueId() | 獲取此項目的隊列 ID。 |
4.5 PlaybackState 相關組件 API 列表
4.5.1 PlaybackState
方法名 | 備注 |
---|---|
long getActions() | 獲取此會話上可用的當前操作港柜。 |
long getActiveQueueItemId() | 獲取隊列中當前活動項的 ID请契。 |
long getBufferedPosition() | 獲取當前緩沖位置(以毫秒為單位)。 |
List<PlaybackState.CustomAction> getCustomActions() | 獲取自定義操作的列表潘懊。 |
CharSequence getErrorMessage() | 獲取用戶可讀的錯誤消息姚糊。 |
Bundle getExtras() | 獲取在此播放狀態(tài)下設置的任何自定義附加內(nèi)容。 |
long getLastPositionUpdateTime() | 獲取上次更新位置的經(jīng)過的實時時間授舟。 |
float getPlaybackSpeed() | 獲取當前播放速度作為正常播放的倍數(shù)救恨。 |
long getPosition() | 獲取當前播放位置(以毫秒為單位)。 |
int getState() | 獲取當前播放狀態(tài)释树。 |
boolean isActive() | 返回是否將其視為活動播放狀態(tài)肠槽。 |
4.5.2 PlaybackState.Builder
方法名 | 備注 |
---|---|
PlaybackState.Builder addCustomAction(String action, String name, int icon) | 將自定義操作添加到播放狀態(tài)。 |
PlaybackState.Builder addCustomAction (PlaybackState.CustomAction customAction) | 將自定義操作添加到播放狀態(tài)奢啥。 |
PlaybackState.Builder setActions(long actions) | 設置此會話上可用的當前操作秸仙。 |
PlaybackState.Builder setActiveQueueItemId(long id) | 通過指定活動項目的 id 來設置播放隊列中的活動項目。 |
PlaybackState.Builder setBufferedPosition(long bufferedPosition) | 設置當前緩沖位置(以毫秒為單位)桩盲。 |
PlaybackState.Builder setErrorMessage(CharSequence error) | 設置用戶可讀的錯誤消息寂纪。 |
PlaybackState.Builder setExtras(Bundle extras) | 設置要包含在播放狀態(tài)中的任何自定義附加內(nèi)容。 |
PlaybackState.Builder setState(int state, long position, float playbackSpeed) | 設置當前播放狀態(tài)赌结。 |
PlaybackState.Builder setState(int state, long position, float playbackSpeed, long updateTime) | 設置當前播放狀態(tài)捞蛋。 |
PlaybackState build() | 生成并返回具有這些值的PlaybackState實例。 |
4.5.3 PlaybackState.CustomAction
方法名 | 備注 |
---|---|
String getAction() | 返回CustomAction的action柬姚。 |
Bundle getExtras() | 返回附加項拟杉,這些附加項提供有關操作的其他特定于應用程序的信息,如果沒有量承,則返回 null搬设。 |
int getIcon() | 返回package中圖標的資源 ID穴店。 |
CharSequence getName() | 返回此操作的顯示名稱。 |