1. 程式人生 > >打造基於MediaSessionCompat的音樂播放(一)

打造基於MediaSessionCompat的音樂播放(一)

前言:

依稀記得很久之前寫過音樂播放的Demo,當時用的方式還是:通過廣播實現Activity和Service之間的通訊,通過間接控制MediaPlayer來實現音樂的播放。

最近寫了一個本地音樂播放器,總體架構是模仿 谷歌官方的android-UniversalMusicPlayer,這裡先不多做介紹。這篇的重點是簡單地介紹一下其中使用的MediaSessionCompat框架的使用。

MediaSessionCompat框架:

MediaSessionCompat位於android/support/v4/media/session包下,主要是用於替代Android L 之後推出的MessionSession。
我們通過一張別人的圖來了解一下(這張圖說的是MediaSession,而不是MediaSessionCompat,但大致原理是一樣的):
這裡寫圖片描述


通過Activity和Service(MediaBrowserServiceCompat)這兩個元件來實現音樂播放的功能。

MediaBrowserCompat(客戶端):

在Activity中,宣告MediaBrowserCompat(客戶端),通過MediaBrowserCompat來和MediaBrowserServiceCompat(服務端)連線,

private MediaBrowserCompat mMediaBrowser;

mMediaBrowser = new MediaBrowserCompat(this,
                new ComponentName(this
, MusicService.class), mConnectionCallback, null);

MediaBrowserCompat的連線:

同時通過MediaBrowserCompat.ConnectionCallback(上面的程式碼片中的mConnectionCallback)介面來實現和服務端連線的回撥,在回撥中,我們可以通過返回的MediaSessionCompat.Token獲取到MediaControllerCompat(控制器),通過MediaControllerCompat,我們可以實現在Activity中控制音樂。

 private final MediaBrowserCompat.ConnectionCallback mConnectionCallback =
            new
MediaBrowserCompat.ConnectionCallback() { @Override public void onConnected() { //說明已經連線上了 try { connectToSession(mMediaBrowser.getSessionToken()); } catch (RemoteException e) { e.printStackTrace(); } } };

MediaBrowserCompat訂閱:

MediaBrowserCompat連線上MediaBrowserServiceCompat之後,我們可以通過MediaBrowserCompat向MediaBrowserServiceCompat發起訂閱請求,例如需要獲取某一列表的資料,或者某一首歌的資料,這時就需要
做如下操作:
需要先解除訂閱,再發起訂閱(這好像是官方的一個Bug,必須這麼做)

MediaBrowserCompat.unsubscribe(mMediaId);
MediaBrowserCompat.subscribe(mMediaId,mSubscriptionCallback);
 /*
     * 瀏覽器訂閱的介面,資料的回撥
     * */
    private final MediaBrowserCompat.SubscriptionCallback mSubscriptionCallback = new MediaBrowserCompat.SubscriptionCallback() {
        @Override
        public void onChildrenLoaded(@NonNull String parentId, @NonNull List<MediaBrowserCompat.MediaItem> children) {
            super.onChildrenLoaded(parentId, children);
            //children 即為Service傳送回來的媒體資料集合
            //在onChildrenLoaded可以執行重新整理列表UI的操作

        }
    };

mSubscriptionCallback是瀏覽器訂閱的介面,通過傳不同的MediaId,我們可以在這裡拿到不同的回撥資料,再根據拿到的資料做我們想做的操作。

MediaControllerCompat(控制器):

那麼MediaControllerCompat是如何控制音樂的呢?
上面不是提到了客戶端連線成功後的操作嗎,我們通過MediaSessionCompat.Token,初始化控制器

 private void connectToSession(MediaSessionCompat.Token token) throws RemoteException {
        mediaController = new MediaControllerCompat(this, token);
        MediaControllerCompat.setMediaController(this, mediaController);
        onMediaBrowserConnected();
        onMediaControllerConnected(mediaController.getSessionToken());
    }

通過MediaControllerCompat,我們可以拿到MediaControllerCompat.TransportControls物件,
該物件可以控制播放音樂的一些常規操作:

TransportControls.skipToPrevious();
TransportControls.skipToNext();
TransportControls.pause();
TransportControls.play();

除此之外,我們還可以通過MediaControllerCompat來實現MediaControllerCompat.Callback介面,

MediaControllerCompat.registerCallback(mMediaControllerCallback);

private final MediaControllerCompat.Callback mMediaControllerCallback =
            new MediaControllerCompat.Callback() {

                @Override
                public void onPlaybackStateChanged(@NonNull PlaybackStateCompat state) {
                    //這裡根據播放狀態的改變,本地ui做相應的改變,例如播放模式,播放、暫停,進度條等
                    updatePlaybackState(state);
                }

                @Override
                public void onMetadataChanged(MediaMetadataCompat metadata) {
                    //歌曲的資訊,例如播放時長,歌曲名稱等
                    updateDuration(metadata);
                }
            };

該介面的作用是獲取音樂播放的狀態改變,從而控制我們UI介面的更新,例如進度條,播放資訊

MediaBrowserServiceCompat(服務端):

而在Service中,我們可以讓自己定義的service繼承MediaBrowserServiceCompat,

public class MusicService extends MediaBrowserServiceCompat

MediaSessionCompat:

之前在客戶端連線成功後,有這麼一行程式碼:

connectToSession(mMediaBrowser.getSessionToken());

mMediaBrowser.getSessionToken()拿到的就是MediaSessionCompat。

那麼什麼是MediaSessionCompat呢

在MediaSession框架中,有受控端(一個)和控制端(可以有多個)。接下來為了保證受控端和控制端不串號(想象一個遙控器可以遙控同一型號的多臺電視),就有了SessionToken的概念,相當於我們在連線藍芽裝置時的配對碼,這樣就保證了不串號

當應用程式想要釋出媒體播放資訊或處理媒體金鑰時,應該建立MediaSession。MediaSessionCompat允許與媒體控制器、音量鍵、媒體按鈕和傳輸控制進行互動。一般來說,一個應用程式只需要一個會話來進行所有的播放(儘管可以建立多個會話來提供更好的媒體控制)

在onCreate方法中,初始化MediaSessionCompat,

private MediaSessionCompat mSession;

 mSession = new MediaSessionCompat(this, "MusicService");
 setSessionToken(mSession.getSessionToken());
 mSession.setCallback(mPlaybackManager.getMediaSessionCallback());
 mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
                MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);

onGetRoot()控制對服務的訪問:

之前提到過,在客戶端宣告MediaBrowserCompat的時候,向服務端發起了初次連線請求。此時,服務端會在onGetRoot方法中收到請求,此時返回一個rootId就好了,如果方法返回null,則拒絕連線。

 @Nullable
    @Override
    public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, @Nullable Bundle rootHints) {
        return new BrowserRoot(MEDIA_ID_ROOT, null);
    }

onLoadChildren()與客戶端通訊:

接著在onLoadChildren方法中,服務端會接收來自客戶端的不同請求,此時需要通過客戶端傳送過來的parentMediaId,服務端根據parentMediaId來返回不同的結果給客戶端

@Override
    public void onLoadChildren(@NonNull String parentMediaId, @NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {

        if (MEDIA_ID_EMPTY_ROOT.equals(parentMediaId)) {
            result.sendResult(new ArrayList<MediaBrowserCompat.MediaItem>());
        } else {
            result.sendResult(mMusicProvider.getChildren(parentMediaId));
        }
    }

這裡mMusicProvider的作用是提供音樂,先不多做介紹。

至此,構建一個基於MediaSessionCompat的音樂播放器的第一步就完成啦。