文章集合:
Universal Music Player 源码解析(一)--MediaSession框架
Univeral Music Player 源码解析 -- 让人头疼的playback
Universal Music Player 源码解析(二)--MusicService 和 MediaController
Universal Music Player 源码分析 (三)-- 其他类分析
MediaSession 框架介绍
大体印象:MediaBrowser
通过调用getSessionToken()
得到一个token,通过这个token,我们的MediaController
对象才可以被创建,而MediaSession
和MediaController
之间通过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的方式启动,
传进去的另外一个mConnectionCallback
的onConnected()
将会在回调之前获取到一个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的解析和这个类在这个框架中的作用是很少的,所以总结为以下图
下一篇将会将重点放在model层,关于数据是怎么获取和如何使用MediaIDHelper
解析的
links:
如何阅读Android hide api
MediaSession框架介绍