音视频框架音视频开发Android开发经验谈

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

2018-05-03  本文已影响0人  kolibreath

文章集合:
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框架介绍

上一篇下一篇

猜你喜欢

热点阅读