Universal Music Player 源码解析(一)--

作者: kolibreath | 来源:发表于2018-05-03 17:09 被阅读0次

    文章集合:
    Universal Music Player 源码解析(一)--MediaSession框架

    Univeral Music Player 源码解析 -- 让人头疼的playback

    Universal Music Player 源码解析(二)--MusicService 和 MediaController

    Universal Music Player 源码分析 (三)-- 其他类分析

    MediaSession 框架介绍

    大体印象:MediaBrowser 通过调用getSessionToken()得到一个token,通过这个token,我们的MediaController对象才可以被创建,而MediaSessionMediaController之间通过MediaSession.Token对象相联系

    以下代码出自BaseActivity

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

    connectToSession()是在mConnectionCallback中被调用,

    
        private final MediaBrowserCompat.ConnectionCallback mConnectionCallback =
            new MediaBrowserCompat.ConnectionCallback() {
                @Override
                public void onConnected() {
                    LogHelper.d(TAG, "onConnected");
                    try {
                        connectToSession(mMediaBrowser.getSessionToken());
                    } catch (RemoteException e) {
                        LogHelper.e(TAG, e, "could not connect media controller");
                        hidePlaybackControls();
                    }
                }
            };
    

    同时这个callback作为MediaBrowser的构造函数的参数被使用,后面会说到这一点,根据函数见名知意的特点,这个回调将会在mediaBrowser connect的时候被调用.

    继续分析,关于这行代码:

       MediaControllerCompat.setMediaController(this, mediaController);
    

    跟踪源码可以发现MediaController是严格和Activity对象绑定在一起的,所以才可以通过Activity来获取MediaController,这个在后面分析MediaSession框架层和UI的联系的时候会再次说明.

       MediaControllerCompat 
    controllerCompat = MediaControllerCompat.getMediaController(this);
    

    之前说过,MediaBrowser是通过构造函数创建的:

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

    简单来说,这个构造函数传入了一个MusicService,跟踪源码的话,这个MusicService将会在mediaBrowser的connect()中启动

      public void connect() {
            mImpl.connect();
        }
    

    mImpl是一个MediaBrowserImpl对象

    
     final Intent intent = new Intent(MediaBrowserServiceCompat.SERVICE_INTERFACE); intent.setComponent(mServiceComponent);
     mServiceConnection = new MediaServiceConnection();
          boolean bound = false;
               try {
             bound = mContext.bindService(intent, mServiceConnection,
          Context.BIND_AUTO_CREATE);
    

    MusicService将会通过bindService的方式启动,
    传进去的另外一个mConnectionCallbackonConnected()将会在回调之前获取到一个Token对象.

    再说一下真正关心的MediaSession
    以下代码出自MusicService
    MusicService继承了MediaBrowserServiceCompat

     //the string paras is tag for debugging purpose
            mSession = new MediaSessionCompat(this, "MusicService");
            setSessionToken(mSession.getSessionToken());
            mSession.setCallback(mPlaybackManager.getMediaSessionCallback());
            mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
                    MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
    

    先看这行代码:

     setSessionToken(mSession.getSessionToken());
    

    同样这个token是根据``MusicService`绑定的,之后可以根据MusicService的对象取出来

      MediaSessionCompat.Token freshToken 
    = mService.getSessionToken();
    

    同时,MediaSession还有一个很重要的功能就是管理playback

      mSession.setCallback(mPlaybackManager.getMediaSessionCallback());
    
    private class MediaSessionCallback extends MediaSessionCompat.Callback {
            @Override
            public void onPlay() {
                LogHelper.d(TAG, "play");
                if (mQueueManager.getCurrentMusic() == null) {
                    mQueueManager.setRandomQueue();
                }
                handlePlayRequest();
            }
    
            @Override
            public void onSkipToQueueItem(long queueId) {
                LogHelper.d(TAG, "OnSkipToQueueItem:" + queueId);
                mQueueManager.setCurrentQueueItem(queueId);
                mQueueManager.updateMetadata();
            }
    
            @Override
            public void onSeekTo(long position) {
                LogHelper.d(TAG, "onSeekTo:", position);
                mPlayback.seekTo((int) position);
            }
    
            @Override
            public void onPlayFromMediaId(String mediaId, Bundle extras) {
                LogHelper.d(TAG, "playFromMediaId mediaId:", mediaId, "  extras=", extras);
                mQueueManager.setQueueFromMusic(mediaId);
                handlePlayRequest();
            }
    
            @Override
            public void onPause() {
                LogHelper.d(TAG, "pause. current state=" + mPlayback.getState());
                handlePauseRequest();
            }
    
            @Override
            public void onStop() {
                LogHelper.d(TAG, "stop. current state=" + mPlayback.getState());
                handleStopRequest(null);
            }
    
            @Override
            public void onSkipToNext() {
                LogHelper.d(TAG, "skipToNext");
                if (mQueueManager.skipQueuePosition(1)) {
                    handlePlayRequest();
                } else {
                    handleStopRequest("Cannot skip");
                }
                mQueueManager.updateMetadata();
            }
    
            @Override
            public void onSkipToPrevious() {
                if (mQueueManager.skipQueuePosition(-1)) {
                    handlePlayRequest();
                } else {
                    handleStopRequest("Cannot skip");
                }
                mQueueManager.updateMetadata();
            }
    
            @Override
            public void onCustomAction(@NonNull String action, Bundle extras) {
                if (CUSTOM_ACTION_THUMBS_UP.equals(action)) {
                    LogHelper.i(TAG, "onCustomAction: favorite for current track");
                    MediaSessionCompat.QueueItem currentMusic = mQueueManager.getCurrentMusic();
                    if (currentMusic != null) {
                        String mediaId = currentMusic.getDescription().getMediaId();
                        if (mediaId != null) {
                            String musicId = MediaIDHelper.extractMusicIDFromMediaID(mediaId);
                            mMusicProvider.setFavorite(musicId, !mMusicProvider.isFavorite(musicId));
                        }
                    }
                    // playback state needs to be updated because the "Favorite" icon on the
                    // custom action will change to reflect the new favorite state.
                    updatePlaybackState(null);
                } else {
                    LogHelper.e(TAG, "Unsupported action: ", action);
                }
            }
    
      .....
        }
    

    在这个回调中就会调用PlaybackManager中实现了Callback类的中的方法

    我看了很多关于这个项目的解析,很多前辈对mediaBrowser的解析和这个类在这个框架中的作用是很少的,所以总结为以下图

    image.png

    下一篇将会将重点放在model层,关于数据是怎么获取和如何使用MediaIDHelper解析的

    links:
    如何阅读Android hide api
    MediaSession框架介绍

    相关文章

      网友评论

        本文标题:Universal Music Player 源码解析(一)--

        本文链接:https://www.haomeiwen.com/subject/dlvcrftx.html