打造基于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的音乐播放器的第一步就完成啦。

猜你喜欢

转载自blog.csdn.net/ckwccc/article/details/80363674