Android 音樂播放器架構導讀 Part 1

fantasy1022
9 min readMar 28, 2020

--

Ref: https://pixabay.com/photos/smartphone-notebook-social-media-1735044/

來到 KKBOX (KKStream)也有三年多的時間了,一直是負責音樂類型 App 的開發,但對音樂播放器架構沒有到很瞭解,之前的印象就是用系統的元件 MediaPlayer,操作相關的 API 就可以了,然後 Player 本身有個複雜的狀態圖,Player 要先 Prepare,然後才能 Play,等等的其他邏輯,但這只是 Player 本身,如果把範圍拉大,之前就像相機拉近鏡頭,只看一棵樹,現在把鏡頭變回ㄧ倍 XD,看整個森林。一個架構好的音樂播放器,除了本身功能正常之外,要怎麼支援和系統間的互動,還有使用者不是 UI 操作呢? Google Android 官方有詳細的架構介紹,本篇文章會根據文件來介紹:

在介紹前先來想一下和跟系統互動是怎麼一會事?想像在用一個音樂 App( KKBOX, Spotify, Youtube music),打開後,想要聽歌這時候會按下播放鍵,手機上的 Status bar 會出現 Notification,這時候按 back 或是 home 離開 App,或是關掉手機螢幕,音樂還會繼續播放,甚至還可以透過手錶或是耳機,來控制音樂播放是暫停和下一首,這些操作都不是透過 App 上的 UI 來完成的,可以支援這些行為和 UI 上的顯示都需要照遵循官方的說明來實作。

先從大範圍的架構來看,App 分成兩部分:UI 和 Player,UI 上可以控制 Player 和顯示 Player 的狀態,現在是在播哪首歌,是不是在播放中…等,Player 的實作可以用 Android 官方提供的 MediaPlayer, ExoPlayer 或是ijkplayer,也可以用較底層的 API AudioTrack 來自己實作。如果用比抽象的概念來稱呼就是 client / server,client 不一定是 UI 了,可能是藍芽耳機、Android wear、或是 Pixel 4 的揮手控制,想像成有一個遙控器可以來控制這個 Player 播放,因此將控制和播放邏輯拆開,而不要綁在一起,是一個正確觀念且有彈性的。

Ref: https://developer.android.com/guide/topics/media-apps/media-apps-overview

接著再看得仔細一點,官方提供了什麼元件供開發者使用呢?因為 UI 和 Player 都可以是任意的,因應自己的需求調整,但播放器是有共通的播放行為,官方就提供了 MediaControllerMediaSession 元件,裡面有一些預設的 callback(play, pause, stop) 可供使用。

MediaSession 就像是 Player 的代理人,要控制 Player,都必須透過 MediaSession 來控制,MediaSession 也會有目前播放的狀態(歌曲資訊、是否在播放中),MediaSession 可由多個 MediaController 來控制,舉例來說,透過 App 播放音樂後,可以透過手錶上對應的 App 來獲取現在的播放狀態。

那 MediaController 就像是 client 端的代理人了,要透過 MediaController 來下指令,同時也會收從 MediaSession 來的 Callback,比如一首歌播放完了,會跳下一首,這時候 MediaController 收到 MediaSession 來的通知,播放狀態改變了,這時候就可以做相對應的處理(UI 改變)。

既然是類似 client 和 server 的關係,那可以多對一或是多對多之類的嗎?可以從使用情境可以推知,可能會有多個 client(App, 手錶) 對應到一個播放器,因此多個 MediaController 可以綁定同一個 MediaSession,但一個 MediaController 一次就只能綁定一個 MediaSession。

Ref: https://developer.android.com/guide/topics/media-apps/media-apps-overview

大部分使用者,在使用音樂 App 時,可能再找到要聽的音樂後,按下播放,接著可能就是切換到別的 App 使用,或是關掉螢幕純粹聽音樂。這時候音樂 App 就沒有再前景了,那會發生什麼事呢?如果自己簡單測試寫來玩玩看,好像也不會發生什麼事,聲音還是會繼續播啊,但隨著切換 App 多一點,或是時間放久一點,音樂可能就停了,因為 Android 系統對 App 的生命週期會依據 Process 的 priority 來做管控,前景的 priority 是最高的,最慢被系統回收。那要怎麼讓 App 在這個使用情境維持在前景呢?就是使用 Service 啦。

Android 在 5.0 時提供了 MediaBrowserService,可以專門用來播歌和瀏覽音樂資訊的 Service,就不用再用一般的 Service 了,還需要用 Bind 來 Service 來做一些操作,會變得有點繁瑣。要實作的時候會使用 MediaBrowserServiceCompat,提供了很好的向前支援,App 最低需求版本低於 Android 5.0 也可以使用。

用 MediaBrowserService 除了是遵照官方的架構,那還有什麼好處呢?可以讓音樂播放器的 App 可以被掃描到,這樣 Android auto 或是 Android wear 也可以來控制音樂播放器 App, 擴展音樂播放器的使用情境。在 AndroidManifest 註冊,因此系統知道這個音樂 App 是有使用 MediaBrowserService 的。

在 UI 端就是使用 MediaBrowser 來連接 MediaBrowserService,官方提供了一個內建溝通架構,就不用再使用 Bind Service

MusicSerice 是繼承 MediaBrowserService 的 Sub class
Ref: https://developer.android.com/guide/topics/media-apps/media-apps-overview

在電腦上播音樂,可能用 KKBOX 播了音樂,這時候再去看 Youtube 看影片,兩個聲音就會疊在一起,同時一起播出來,必須手動去停止一邊的播放。但在手機上就不是這個情況,後面的音樂軟體在播時,前面的音樂就會暫停播放,這在音樂播放 App 上是個良好的設計,想像如果有電話或是 Line 鈴響時,但音樂還是繼續播,這時候還需需要手動就暫停,就稍嫌麻煩了。這個設計還會應用在耳機拔出,耳機拔出後,音樂能自動暫停,也是個體貼的設計,因為通常用耳機的情境,可能就是個需要安靜的環境,不小心把耳機扯掉了,這時候音樂放出來就會有點尷尬 XD,但如果是 Airpod 拿下單隻耳機就會暫停播放,這就是蘋果貼心的設計了,App 端不知道耳機有沒有被拿下來 XD

接下來介紹這個 App 和 MediaBrowserService 相關,有使用MediaBrowserService 的音樂 App,就可以被這個 Test App 撈出來,可以當作是一個 Client 來控制音樂 APP。

Android 官方提供的音樂播放器範例:

可以看到影片的 App 列表,都是有註冊 MediaBrowserService 的,那我們用 UAMP 來做測試(Google 自己官方的 App 看來都有擋連線,連不上 XD),不用打開 UAMP 播放器,就可以透過 Play From Search Test 來播歌,播放後可以看到相關的歌曲資訊(MetaData),還有整張的歌單,在 Queue 裡面的情況,提供了各種細部的資料,在開發時,也能方便 Debug。

因為可以看到的資訊很多,Release 版的 App 應該要擋未知 Client 端的連線,以防重要資料洩漏。

連不上 XD

看完官方文件介紹後,想說來看看實際寫起來會怎麼樣,就去看 UAMP 是怎麼寫的,覺得真是博大精神,整體上來說就是實作了官方音樂播放器架構,但整個 Android 架構上,使用了 MVVM,各種觀察物件,好幾層的觀察 XD,Part 2 就決定介紹來介紹 UAMP 好了。

官方文件我想對於開發者來說,是一個很重要的知識來源,尤其這又是架構的部分,有時候要支援某個功能,就是因為架構的關係,不是簡單改改就可以支援了。從這個架構學到了不少,也能體會為什麼要這樣設計了!

--

--

fantasy1022
fantasy1022

No responses yet