文章集合:
Universal Music Player 源碼解析(一)--MediaSession框架
Univeral Music Player 源碼解析 -- 讓人頭疼的playback
Universal Music Player 源碼解析(二)--MusicService 和 MediaController
Universal Music Player 源碼分析 (三)-- 其他類分析
MediaSession 框架介紹
大體印象:MediaBrowser
通過調(diào)用getSessionToken()
得到一個token,通過這個token,我們的MediaController
對象才可以被創(chuàng)建,而MediaSession
和MediaController
之間通過MediaSession.Token
對象相聯(lián)系
以下代碼出自BaseActivity
private void connectToSession(MediaSessionCompat.Token token) throws RemoteException {
MediaControllerCompat mediaController = new MediaControllerCompat(this, token);
MediaControllerCompat.setMediaController(this, mediaController);
mediaController.registerCallback(mMediaControllerCallback);
...
}
connectToSession()
是在mConnectionCallback
中被調(diào)用,
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
的構(gòu)造函數(shù)的參數(shù)被使用,后面會說到這一點(diǎn),根據(jù)函數(shù)見名知意的特點(diǎn),這個回調(diào)將會在mediaBrowser
connect的時候被調(diào)用.
繼續(xù)分析,關(guān)于這行代碼:
MediaControllerCompat.setMediaController(this, mediaController);
跟蹤源碼可以發(fā)現(xiàn)MediaController
是嚴(yán)格和Activity對象綁定在一起的,所以才可以通過Activity來獲取MediaController
,這個在后面分析MediaSession框架層和UI的聯(lián)系的時候會再次說明.
MediaControllerCompat
controllerCompat = MediaControllerCompat.getMediaController(this);
之前說過,MediaBrowser
是通過構(gòu)造函數(shù)創(chuàng)建的:
mMediaBrowser = new MediaBrowserCompat
(this
,new ComponentName(this, MusicService.class),
,mConnectionCallback,
null);
簡單來說,這個構(gòu)造函數(shù)傳入了一個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的方式啟動,
傳進(jìn)去的另外一個mConnectionCallback
的onConnected()
將會在回調(diào)之前獲取到一個Token對象.
再說一下真正關(guān)心的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是根據(jù)``MusicService`綁定的,之后可以根據(jù)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);
}
}
.....
}
在這個回調(diào)中就會調(diào)用PlaybackManager
中實(shí)現(xiàn)了Callback
類的中的方法
我看了很多關(guān)于這個項(xiàng)目的解析,很多前輩對mediaBrowser的解析和這個類在這個框架中的作用是很少的,所以總結(jié)為以下圖
下一篇將會將重點(diǎn)放在model層,關(guān)于數(shù)據(jù)是怎么獲取和如何使用MediaIDHelper
解析的