打造基于MediaSessionCompat的音乐播放(二)

前言
在上一篇文章打造基于MediaSessionCompat的音乐播放(一)中,已经简单地介绍了MediaSessionCompat框架以及它的具体使用。
今天这篇,主要介绍如何提供数据给播放器。
在这里我还是以谷歌官方的android-UniversalMusicPlayer为例,并结合我的实际(提供的是本地数据),如果你想看网络数据如何处理,可以参考上面谷歌的Demo。

下面进入正题

数据的连接

前面提到过,在Activity中通过MediaBrowserCompat和实现了MediaBrowserServiceCompat的service连接,再通过

MediaBrowserCompat.unsubscribe(mMediaId);
MediaBrowserCompat.subscribe(mMediaId,mSubscriptionCallback);

订阅数据,想要获取相应的音乐数据,就是通过客户端发送MediaId来获取,而数据则是通过MediaBrowserCompat.SubscriptionCallback来获取的。

在服务端的onLoadChildren方法中,我们拿到这个客户端发送过来的MediaId,对这个MediaId做相应的处理之后,根据MediaId的不同,返回不同的数据即可。

 @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));
        }
    }

上一篇提到过这个,result.sendResult将结果返回给客户端。接下来我们详细地看一下这个mMusicProvider,它的作用是提供本地数据。它根据从客户端接收到的parentMediaId(即上面提到的客户端发出的MediaId)类型,返回数据。

本地数据分类与检索

在MusicProvider中,我将数据主要分成四大类:

private List<MediaMetadataCompat> mLocalMusicList;//本地所有的音乐

private final ConcurrentMap<String, MutableMediaMetadata> mMusicListById;//key是musicId

private ConcurrentMap<String, List<MediaMetadataCompat>> mMusicListByAlbum;//本地专辑集合

private ConcurrentMap<String,List<MediaMetadataCompat>> mMusicListByArtist;//本地歌手集合

MusicProvider需要先在Service中初始化,先对本地音乐数据进行检索,并将其转化为我们需要的数据,这里举个mMusicListById的例子

 mLocalMusicList = mSongSource.getLocalList();

mSongSource是一个实现了自定义方法的接口,它的作用是提供本地数据
在它的getLocalList方法中,我们获取本地数据

@Override
    public ArrayList<MediaMetadataCompat> getLocalList() {
        getLocalSongList();
        ArrayList<MediaMetadataCompat> tracks = new ArrayList<>();
        for (int i = 0; i < mLocalSong.size(); i++) {
            tracks.add(buildFromLocal(mLocalSong.get(i)));
        }
        return tracks;
    }

数据的转换
将数据从媒体数据转换成我们需要的数据(MediaMetadataCompat)

同时通过buildFromLocal方法,将这些本地数据转化为我们能用的数据

private MediaMetadataCompat buildFromLocal(Song song){
        String title = song.getTitle();
        String album = song.getAlbum();
        String artist = song.getArtist();
        int duration = song.getDuration();
        String source = song.getPath();
        String strId = "";
        if(title != null && artist != null){
            strId = title + artist;
        }
        String id = String.valueOf(strId.hashCode());
        String albumArt = song.getAlbumObj().getAlbumArt();
        return new MediaMetadataCompat.Builder()
                .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID,id)
                .putString(SongSource.CUSTOM_METADATA_TRACK_SOURCE, source)
                .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_URI,source)
                .putString(MediaMetadataCompat.METADATA_KEY_ALBUM,album)
                .putString(MediaMetadataCompat.METADATA_KEY_ARTIST,artist)
                .putString(MediaMetadataCompat.METADATA_KEY_ART_URI,albumArt)
                .putLong(MediaMetadataCompat.METADATA_KEY_DURATION,duration)
                .putString(MediaMetadataCompat.METADATA_KEY_TITLE, title)
                .build();
    }

通过上面的操作,我们的mLocalMusicList 就拿到了所有的本地音乐数据,再通过这个总数据,获取相应的另外三种数据。


 Iterator<MediaMetadataCompat> tracks = mSongSource.iterator();
 while (tracks.hasNext()) {
     MediaMetadataCompat item = tracks.next();
     String musicId = item.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID);
     mMusicListById.put(musicId, new MutableMediaMetadata(musicId, item));
 }

数据的分类返回

到目前为止,我们只是拥有了这些数据,接下来我们看怎么讲这些数据通过MediaId,获取相应的数据返回给客户端。
上面有提到过在service中返回数据的方法中有这行代码:

result.sendResult(mMusicProvider.getChildren(parentMediaId));

我们来看看mMusicProvider.getChildren(parentMediaId)是怎么实现的

  /*
    * 返回本地音乐数据
    * */
    public List<MediaBrowserCompat.MediaItem> getChildren(String mediaId){
        List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>();

        //所有的音乐
        if(MediaIdHelper.MEDIA_ID_NORMAL.equals(mediaId)){
            for (MediaMetadataCompat mediaMetadataCompat: mLocalMusicList) {
                mediaItems.add(createMediaItem(mediaMetadataCompat));
            }
        }else if(MediaIdHelper.MEDIA_ID_ALBUM.equals(mediaId)){//专辑
            for (String albumTitle :
                    getAlbumList()) {
                mediaItems.add(createAlbumMediaItem(albumTitle));
            }
        }else if(mediaId.startsWith(MediaIdHelper.MEDIA_ID_ALBUM_DETAIL)){//专辑详情
            String[] split = mediaId.split("&&");
            List<MediaMetadataCompat> musicsByAlbum = getMusicsByAlbum(split[1]);
            for (int i = 0; i < musicsByAlbum.size(); i++) {
                mediaItems.add(createMediaItem(musicsByAlbum.get(i)));
            }
        }else if(MediaIdHelper.MEDIA_ID_ARTIST.equals(mediaId)){//歌手列表
            for (String artistName :
                    getArtistList()) {
                mediaItems.add(createArtistMediaItem(artistName));
            }
        }else if(mediaId.startsWith(MediaIdHelper.MEDIA_ID_ARTIST_DETAIL)){//歌手列表详情
            String[] split = mediaId.split("&&");
            List<MediaMetadataCompat> musicByArtist = getMusicByArtist(split[1]);
            if(musicByArtist != null){
                for (int i = 0; i < musicByArtist.size(); i++) {
                    mediaItems.add(createMediaItem(musicByArtist.get(i)));
                }
            }

        }
        return mediaItems;
    }
public class MediaIdHelper {
    public static final String MEDIA_ID_EMPTY_ROOT = "__EMPTY_ROOT__";
    public static final String MEDIA_ID_ROOT = "__ROOT__";//初次连接
    public static final String MEDIA_ID_NORMAL = "__LOCAL_NORMAL__";//所有的本地音乐
    public static final String MEDIA_ID_ALBUM = "__LOCAL_ALBUM__";//音乐专辑
    public static final String MEDIA_ID_ALBUM_DETAIL = "__LOCAL_ALBUM_DETAIL__";//具体某个专辑的歌曲
    public static final String MEDIA_ID_ARTIST = "__LOCAL_ARTIST__";
    public static final String MEDIA_ID_ARTIST_DETAIL = "__LOCAL_ARTIST_DETAIL__";

}

结合自定义的MediaIdHelper 来看,我们客户端发送的mediaId,举个例子,
我们在客户端发送的MediaId是这样,MEDIA_ID_ARTIST意思是获取本地的所有歌手的数据

mMediaId = MEDIA_ID_ARTIST;
mediaBrowserProvider.getMediaBrowser().unsubscribe(mMediaId);
 mediaBrowserProvider.getMediaBrowser().subscribe(mMediaId,mSubscriptionCallback);

服务端接收到后,在MusicProvider的getChildren方法中,对这个MediaId进行判断,执行到

else if(MediaIdHelper.MEDIA_ID_ARTIST.equals(mediaId)){//歌手列表
            for (String artistName :
                    getArtistList()) {
                mediaItems.add(createArtistMediaItem(artistName));
            }

进入该分支,通过对所有歌手的遍历循环,构建符合规则的数据

 /*
    * 构建 歌手列表 数据
    * */
    private MediaBrowserCompat.MediaItem createArtistMediaItem(String artistName){
        List<MediaMetadataCompat> songList = mMusicListByArtist.get(artistName);//获得这个歌手的所有歌曲
        MediaMetadataCompat data = songList.get(0);

        MediaDescriptionCompat descriptionCompat = new MediaDescriptionCompat.Builder()
                .setMediaId(artistName)//歌手名称
                .setTitle(String.valueOf(songList.size()))
                .build();

        return new MediaBrowserCompat.MediaItem(descriptionCompat,
                MediaBrowserCompat.MediaItem.FLAG_BROWSABLE);
    }

循环完毕后,将mediaItems数据列表返回给客户端,客户端拿到数据之后就可以展示了。

所有的数据都可以通过上述方式获得。

数据类型了解

最后我们来看一下,上面出现过的可能显得有点陌生的数据类:
1.MediaMetadataCompat:
它出现在将初始的本地音乐数据转化成MediaMetadataCompat,
源码的解释是:

Contains metadata about an item, such as the title, artist, etc.

包含关于item的元数据,如标题、艺术家等信息。具体看MediaMetadataCompat类,它的数据可以包含TITLE、ARTIST、DURATION、ALBUM、AUTHOR、WRITER等等,可以说,一首歌的所有需要显示的信息,它都可以设置。

2.MediaBrowserCompat.MediaItem

A class with information on a single media item for use in browsing/searching media.
MediaItems are application dependent so we cannot guarantee that they contain the right values.

在浏览/搜索媒体中使用的具有单个媒体项上的信息的类。
mediaitem依赖于应用程序,因此我们不能保证它们包含正确的值。
这个数据类是用于客户端和服务端之间传输使用的,我们在客户端拿到(服务端发送)的数据就是它的集合。
这个数据里面的内容也是需要我们自己本地构建,在我的项目中,我就是通过之前拿到的MediaMetadataCompat,将其转化为MediaBrowserCompat.MediaItem数据

3.MutableMediaMetadata
自定义的数据类,它的结构如下:

public MediaMetadataCompat metadata;
public final String trackId;

它的作用是封装了MediaMetadataCompat,通过键值对的方式检索MediaMetadataCompat数据。

写在最后:
关于数据这一块就差不多写完了,下面一篇应该会写一下对于耳机插拔、音乐控制等控制器的实现

猜你喜欢

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