MediaSession原理以及使用
原理分析
申明下述代码都是基于android10
上图
createSession时序:
从类开始分析
MediaSession
MediaSession与其管理的播放器共存。您应该在媒体会话及其关联播放器所属 Activity 或服务的 onCreate() 方法中创建并初始化MediaSession
mPlaybackState = new PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_NONE,0,1.0f)
.build();
mediaSession = new MediaSessionCompat(this, LOG_TAG);
// Enable callbacks from MediaButtons and TransportControls
mediaSession.setPlaybackState(mPlaybackState);
请看下面这张媒体应用架构图
MediaSession的作用是控制播放状态的实现部分,从上面类图可以看到.MediaSession创建之后会有生成一个MediaSessionRecord对象,这个对象提供了很多Binder,SessionStub,ControllerStub,SessionCb
SessionCb 这个是给Service用的,MediaSessionService 是系统Service,拿MediaMutton来说,最终MediaSessionService会调用当前active的SessionCb接口,这个接口的结果是给SessionStub的,SessionStub是MediaSession持有的,可以这么说,MediaSession就是媒体播放器的服务端
再来看ControllerStub,这个是通过Token传递给MediaController,下面会单独对MediaController做解析,这地方先不说了
MediaController
MediaSession创建的时候会创建一个Token对象,这个对象很重要,他是MediaController能和MediaSession交互的基石
MediaController初始化
mController = new MediaControllerCompat(mContext,mBrowser.getSessionToken());
// Save the controller
MediaControllerCompat.setMediaController(getActivity(), mController);
mController.registerCallback(ControllerCallback);
我写的代码有MediaBrowser的,所有Token是从MediaBrowser获取到的,但是基本原理都是一样的
MediaBrowser
android提供这个类的目的为了提供一个类.在服务端有一个类专门去获取数据的,然后获取到之后传到界面显示,这个类就提供了这个功能
有个MediaBrowserService与之对应,通过连接Service,并且订阅,这两个就可以交互了,针对MediaBrowserService在下面讲
MediaBrowser初始化
mBrowser = new MediaBrowserCompat(getActivity(),
new ComponentName(getActivity(), musicService.getClass()),//绑定浏览器服务
BrowserConnectionCallback,//设置连接回调
null
);
MediaBrowserService
请看官方架构图
activity侧初始化MediaBrowser,并且连接MediaBrowserService,连接时候,MediaBrowserService初始化,创建MediaSession以及Player,然后activity端订阅当前的Service,获取到MediaSession的token对象,创建MediaController,这样两边的MediaSession和MediaController就能交互了.中间的连接过程还有一些细节,比如获取内容信息等等
注册Service
<service android:name=".Service.MusicService">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
MediaBrowserService他也是继承Service的,具有和Service一样的功能,也能bind,也能start,但是他有一些特殊方法,最重要的两个必须实现的方法
public BrowserRoot onGetRoot(@NonNull String clientPackageName,
int clientUid, @Nullable Bundle rootHints) {
return new BrowserRoot(MY_MEDIA_ROOT_ID, null);
}
如果想客户端成功连接这个Service,必须在onGetRoot方法中返回一个BrowserRoot对象,并且不能有耗时操作
public void onLoadChildren(@NonNull String parentId,
@NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {
if(mCursor != null) {
while (mCursor.moveToNext()) {
MediaMetadataCompat metadata = new MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, "" + mCursor.getLong(1))
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, mCursor.getString(0))
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION,mCursor.getLong(2))
.build();
Log.v(LOG_TAG, "id" + mCursor.getLong(1) + " title:" + mCursor.getString(0));
mediaItems.add(createMediaItem(metadata));
mInfoMap.put(mediaItems.size()-1,metadata);
}
}
result.sendResult(mediaItems);
}
这部分可以做耗时操作,返回一系列mediaItems对象,这个完成之后,如果你在客户端注册了
SubscriptionCallback这个callback会回调其onChildrenLoaded()方法,返回的信息就是上面onLoadChildren的时候获取到的信息,这样客户端也就有了相关的音乐信息了
MediaSessionManager
这个类是系统类,android系统的,他的主要作用就是能和MediaSessionSession交互,类似android manager用法都差不多,这里就不说了,直接看Service
MediaSessionService
这里面有个 SessionManagerImpl 类,他是继承ISessionManager.Stub的,MediaSessionManager其实就是和它交互的,直接看createSession方法
public ISession createSession(String packageName, ISessionCallback cb, String tag,
Bundle sessionInfo, int userId) throws RemoteException {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
enforcePackageName(packageName, uid)
int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
false /* allowAll */, true /* requireFull */, "createSession", packageName);
if (cb == null) {
throw new IllegalArgumentException("Controller callback cannot be nul");
}
return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag,
sessionInfo).getSessionBinder();
} finally {
Binder.restoreCallingIdentity(token);
}
}
这地方就直接到MediaSessionService去了,调用createSessionInternal方法,到加锁的createSessionLocked方法,
private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
String callerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo) {
FullUserRecord user = getFullUserRecordLocked(userId);
if (user == null) {
Log.w(TAG, "Request from invalid user: " + userId + ", pkg=" + callerPackageName);
throw new RuntimeException("Session request from invalid user.");
}
final MediaSessionRecord session = new MediaSessionRecordz(callerPid, callerUid, userId,
callerPackageName, cb, tag, sessionInfo, this, mHandler.getLooper());
try {
cb.asBinder().linkToDeath(session, 0);
} catch (RemoteException e) {
throw new RuntimeException("Media Session owner died prematurely.", e);
}
user.mPriorityStack.addSession(session);
mHandler.postSessionsChanged(userId);
if (DEBUG) {
Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag);
}
return session;
}
这个方法里面就很重要了,首先它拿了一个FullUserRecord对象,这个里面有个 MediaSessionStack记录了所有的创建的MediaSessionRecord对象,每次创建完成之后都会
user.mPriorityStack.addSession(session);
add到对应userid的MediaSessionStack中,然后系统有啥信息,比如说mediakey派发,他就会拿到当前userid对应的stack去做事情
下面是mediakey派发
private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid,
boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked();
if (session != null) {
if (DEBUG_KEY_EVENT) {
Log.d(TAG, "Sending " + keyEvent + " to " + session);
}
if (needWakeLock) {
mKeyEventReceiver.aquireWakeLockLocked();
}
// If we don't need a wakelock use -1 as the id so we won't release it later.
session.sendMediaButton(packageName, pid, uid, asSystemService, keyEvent,
needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
mKeyEventReceiver);
if (mCurrentFullUserRecord.mCallback != null) {
try {
mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession(
keyEvent, session.getSessionToken());
} catch (RemoteException e) {
Log.w(TAG, "Failed to send callback", e);
}
}
} else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null
|| mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
if (needWakeLock) {
mKeyEventReceiver.aquireWakeLockLocked();
}
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
// TODO: Find a way to also send PID/UID in secure way.
String callerPackageName =
(asSystemService) ? mContext.getPackageName() : packageName;
mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callerPackageName);
try {
if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver;
if (DEBUG_KEY_EVENT) {
Log.d(TAG, "Sending " + keyEvent
+ " to the last known PendingIntent " + receiver);
}
receiver.send(mContext,
needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
mediaButtonIntent, mKeyEventReceiver, mHandler);
if (mCurrentFullUserRecord.mCallback != null) {
ComponentName componentName = mCurrentFullUserRecord
.mLastMediaButtonReceiver.getIntent().getComponent();
if (componentName != null) {
mCurrentFullUserRecord.mCallback
.onMediaKeyEventDispatchedToMediaButtonReceiver(
keyEvent, componentName);
}
}
} else {
ComponentName receiver =
mCurrentFullUserRecord.mRestoredMediaButtonReceiver;
int componentType = mCurrentFullUserRecord
.mRestoredMediaButtonReceiverComponentType;
UserHandle userHandle = UserHandle.of(mCurrentFullUserRecord
.mRestoredMediaButtonReceiverUserId);
if (DEBUG_KEY_EVENT) {
Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
+ receiver + ", type=" + componentType);
}
mediaButtonIntent.setComponent(receiver);
try {
switch (componentType) {
case FullUserRecord.COMPONENT_TYPE_ACTIVITY:
mContext.startActivityAsUser(mediaButtonIntent, userHandle);
break;
case FullUserRecord.COMPONENT_TYPE_SERVICE:
mContext.startForegroundServiceAsUser(mediaButtonIntent,
userHandle);
break;
default:
// Legacy behavior for other cases.
mContext.sendBroadcastAsUser(mediaButtonIntent, userHandle);
}
} catch (Exception e) {
Log.w(TAG, "Error sending media button to the restored intent "
+ receiver + ", type=" + componentType, e);
}
if (mCurrentFullUserRecord.mCallback != null) {
mCurrentFullUserRecord.mCallback
.onMediaKeyEventDispatchedToMediaButtonReceiver(
keyEvent, receiver);
}
}
} catch (CanceledException e) {
Log.i(TAG, "Error sending key event to media button receiver "
+ mCurrentFullUserRecord.mLastMediaButtonReceiver, e);
} catch (RemoteException e) {
Log.w(TAG, "Failed to send callback", e);
}
}
}
系统都是通过这个stack去管理所有的MediaSession
MediaSessionRecord
MediaSessionRecord是createsession的最终产物,它里面信息很庞大,其实就是一些Binder服务端,
public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName,
ISessionCallback cb, String tag, Bundle sessionInfo,
MediaSessionService service, Looper handlerLooper) {
mOwnerPid = ownerPid;
mOwnerUid = ownerUid;
mUserId = userId;
mPackageName = ownerPackageName;
mTag = tag;
mSessionInfo = sessionInfo;
mController = new ControllerStub();
mSessionToken = new MediaSession.Token(mController);
mSession = new SessionStub();
mSessionCb = new SessionCb(cb);
mService = service;
mContext = mService.getContext();
mHandler = new MessageHandler(handlerLooper);
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
mAudioAttrs = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build();
}
看他的初始化过程,new了很多的对象,最终MediaSession和MediaController交互都是通过它来完成的.
MediaSession.Token
这个类的关键作用就是有一个ISessionController对象,MediaController要和MediaSession交互要通过它来完成.很重要,没有这个token,MediaSesson和MediaController就没有任何关系了
TransportControls
TransportControls和MediaSession的callback是对应的
TransportControls | MediaSession.Callback |
---|---|
play() | onPlay() |
stop() | onStop() |
pause() | onPause() |
seekTo(long pos) | onSeekTo(long) |
fastForward() | onFastForward() |
rewind() | onRewind() |
skipToNext() | onSkipToNext() |
skipToPrevious() | onSkipToPrevious() |
skipToQueueItem(long) | onSkipToQueueItem(long) |
playFromMediaId(String,Bundle) | onPlayFromMediaId(String,Bundle) |
playFromSearch(String,Bundle) | onPlayFromSearch(String,Bundle) |
playFromUri(Uri,Bundle) | onPlayFromUri(Uri,Bundle) |
sendCustomAction(String,Bundle) | onCustomAction(String,Bundle) |
setRating(Rating rating) | onSetRating(Rating) |
这里意义就不讲了,上述的方法中很容易就能看出来啥意思,根绝需求,自己做选择
MediaController很明显就是给ui用的,TransportControls里面是它的具体方法,根据需求,给界面调用,服务端,或者说MediaSession端就能收到相应回调,做具体的播放控制
服务端回调给客户端
MediaSession | MediaController.Callback |
---|---|
setMetadata(MediaMetadata) | onMetadataChanged(MediaMetadata) |
setPlaybackState(PlaybackState) | onPlaybackStateChanged(PlaybackState) |
setQueue(List MediaSession.QueueItem>) | onQueueChanged(List MediaSession.QueueItem>) |
setQueueTitle(CharSequence) | onQueueTitleChanged(CharSequence) |
还有别的方法具体参考Aandroid源生类
以上都是根据类来分析MediaSession的下面通过示例代码来分析
MediaSessionManager使用
MediaSessionService 存了当前所有的 MediaSession,不管是active还是非active的
MediaSessionManager提供了getActiveSessions,来获取当前的active的MediaSession相关信息
你也可以通过给MediaSessionManager 添加addOnActiveSessionsChangedListener这个回调来接收,当前MediaSession active状态改变,以及获取到当前的active MediaSession
另外 通过getActiveSessions 获取到MediaSession信息是有缓存的,如果当前的MediaSession没有PlayBackState状态改变,并且在播放,则可能导致,获取到的信息是落后的
代码示例
以MediaBrowser+MediaBrowserService 使用mediasession以及MediaPlayer创建一个播放器
只传客户端以及服务端的关键代码,其他没用的就不传了
Fragment代码
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.music_fragment, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
mAlbumArt = getActivity().findViewById(R.id.image);
mTitleTextView = getActivity().findViewById(R.id.name);
mContext = getContext();
musicService = new MusicService();
mBrowser = new MediaBrowserCompat(
getActivity(),
new ComponentName(getActivity(), musicService.getClass()),//绑定浏览器服务
BrowserConnectionCallback,//设置连接回调
null
);
}
private void handlerPlayEvent(){
switch (mController.getPlaybackState().getState()){
case PlaybackStateCompat.STATE_PLAYING:
mController.getTransportControls().pause();
break;
case PlaybackStateCompat.STATE_PAUSED:
mController.getTransportControls().play();
break;
default:
Bundle bundle = new Bundle();
bundle.putInt("index",1);
//默认播放index是1的歌曲,自己设置
mController.getTransportControls().playFromMediaId(list.get(1).getMediaId(),bundle);
break;
}
}
@Override
public void onStart() {
super.onStart();
//连接Service
mBrowser.connect();
}
@Override
public void onStop() {
super.onStop();
if (MediaControllerCompat.getMediaController(getActivity()) != null) {
MediaControllerCompat.getMediaController(getActivity()).unregisterCallback(ControllerCallback);
}
}
@Override
public void onDestroy() {
super.onDestroy();
mBrowser.disconnect();
}
//设置连接时的回调
private MediaBrowserCompat.ConnectionCallback BrowserConnectionCallback =
new MediaBrowserCompat.ConnectionCallback() {
@Override
public void onConnected() {
if (mBrowser.isConnected()) {
String mediaId = mBrowser.getRoot();
//连接成功之后,subscribe,并set回调,接收成功通知
mBrowser.subscribe(mediaId, BrowserSubscriptionCallback);
}
//根绝传回的browser获取Token,创建MediaController
mController = new MediaControllerCompat(mContext,mBrowser.getSessionToken());
MediaControllerCompat.setMediaController(getActivity(), mController);
//给Controller设置回调
mController.registerCallback(ControllerCallback);
musicService.setController(mController);
buildTransportControls();
}
@Override
public void onConnectionFailed() {
Log.e(TAG, "连接失败!");
}
};
public void buildTransportControls(){
mPlayButton = (Button) getActivity().findViewById(R.id.play);
mPlayButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handlerPlayEvent();
MediaMetadataCompat metadata = mController.getMetadata();
}
});
mNextButton = (Button) getActivity().findViewById(R.id.next);
mNextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mController.getTransportControls().skipToNext();
}
});
mBar = (SeekBar) getActivity().findViewById(R.id.bar);
mBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
// mBar.setText(turnTime(i));
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
Log.d(TAG, "onStopTrackingTouch: " + seekBar.getProgress() + " max:" + seekBar.getMax()
);
mController.getTransportControls().seekTo(
seekBar.getProgress()*100/seekBar.getMax());
}
});
list = new ArrayList<>();
layoutManager = new LinearLayoutManager(mContext);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
adapter = new RecyclerViewAdapter(mContext,list);
adapter.setOnItemClickListener(new RecyclerViewAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Bundle bundle = new Bundle();
bundle.putString("title",list.get(position).getDescription().getTitle().toString());
bundle.putInt("index",position);
mController.getTransportControls().playFromMediaId(list.get(position).getMediaId(),bundle);
}
@Override
public void onItemLongClick(View view, int position) {
}
});
recyclerView = (RecyclerView) getActivity().findViewById(R.id.recycler);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
}
private final MediaControllerCompat.Callback ControllerCallback =
new MediaControllerCompat.Callback() {
@Override
public void onPlaybackStateChanged(PlaybackStateCompat state) {
switch (state.getState()){
case PlaybackStateCompat.STATE_NONE://无任何状态
mTitleTextView.setText("none");
break;
case PlaybackStateCompat.STATE_PAUSED:
break;
case PlaybackStateCompat.STATE_PLAYING:
break;
}
}
//播放歌曲改变,服务端调用setmetadata之后客户端能收到该回调
@Override
public void onMetadataChanged(MediaMetadataCompat metadata) {
Log.d(TAG, "onMetadataChanged");
if (metadata == null) {
return;
}
mTitleTextView.setText(metadata.getDescription().getTitle());
}
@Override
public void onSessionDestroyed() {
super.onSessionDestroyed();
}
@Override
public void onQueueChanged(List<MediaSessionCompat.QueueItem> queue) {
super.onQueueChanged(queue);
}
};
private final MediaBrowserCompat.SubscriptionCallback BrowserSubscriptionCallback =
new MediaBrowserCompat.SubscriptionCallback() {
@Override
public void onChildrenLoaded(@NonNull String parentId,
@NonNull List<MediaBrowserCompat.MediaItem> children) {
//children 即为Service发送回来的媒体数据集合
for (MediaBrowserCompat.MediaItem item : children) {
Log.v(TAG, item.getDescription().getTitle().toString());
list.add(item);
}
//在onChildrenLoaded可以执行刷新列表UI的操作
adapter.notifyDataSetChanged();
}
};
服务端MusicService代码
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("MusicService");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
Log.d(LOG_TAG,"oncreate");
if(mContext == null) {
mContext = this;
}
// 我用的是获取MediaProvider的数据,查询出来歌曲之后,
mCursor = new MusicAcquire(mContext).getMusic();
//创建mediaPlayer
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setOnPreparedListener(PreparedListener);
mMediaPlayer.setOnCompletionListener(CompletionListener);
mMediaPlayer.setOnErrorListener(ErrorListener);
mPlaybackState = new PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_NONE,0,1.0f)
.build();
//创建mediasession
mediaSession = new MediaSessionCompat(this, LOG_TAG);
// Enable callbacks from MediaButtons and TransportControls
mediaSession.setFlags(
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mediaSession.setPlaybackState(mPlaybackState);
callback = new MediaSessionCallback(this,mContext,mMediaPlayer,mPlaybackState,mediaSession);
mediaSession.setCallback(callback);
//这一步很重要,设置当前的mediasessiontoken
setSessionToken(mediaSession.getSessionToken());
audioManager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
playbackAttributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_GAME)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build();
focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setAudioAttributes(playbackAttributes)
.setAcceptsDelayedFocusGain(true)
.setOnAudioFocusChangeListener(FocusChangerListener, mServiceHandler)
.build();
// 请求audiofocus
int res = audioManager.requestAudioFocus(focusRequest);
synchronized(focusLock) {
......
}
try {
mMediaNotificationManager = new MediaNotificationManager(this);
} catch (RemoteException e) {
throw new IllegalStateException("Could not create a MediaNotificationManager", e);
}
}
private AudioManager.OnAudioFocusChangeListener FocusChangerListener = new AudioManager.OnAudioFocusChangeListener(){
// 根据官方建议,根据focuse状态,设置Player的状态
@Override
public void onAudioFocusChange(int focusChange) {
Log.d(LOG_TAG, "onAudioFocusChange");
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
.....
break;
case AudioManager.AUDIOFOCUS_LOSS:
.....
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
.....
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
mMediaPlayer.setVolume(0.3f, 0.3f);
break;
}
}
};
private MediaPlayer.OnPreparedListener PreparedListener = new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
if(playbackNowAuthorized) {
mMediaPlayer.start();
}
mPlaybackState = new PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_PLAYING,0,1.0f)
.build();
mediaSession.setPlaybackState(mPlaybackState);
callback.setPlaybackState(mPlaybackState);
mMediaNotificationManager.startNotification();
Log.d(LOG_TAG, "mPlaybackState:" + mPlaybackState.getPlaybackState());
}
} ;
private MediaPlayer.OnCompletionListener CompletionListener = new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
Log.d(LOG_TAG, "onCompletion");
mPlaybackState = new PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_NONE,0,1.0f)
.build();
mediaSession.setPlaybackState(mPlaybackState);
mMediaPlayer.reset();
callback.onSkipToNext();
}
};
private MediaPlayer.OnErrorListener ErrorListener = new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mediaPlayer, int i, int i1) {
Log.e(LOG_TAG, "Error");
Bundle bundle = new Bundle();
bundle.putString("title",mediaItems.get(1).getDescription().getTitle().toString());
Uri uri = MediaStore.Audio.Media.getContentUri("external");
Uri uri2 = Uri.parse(
String.valueOf(uri.buildUpon().appendPath(String.valueOf(mediaItems.get(1).getMediaId()))));
return false;
}
} ;
@Nullable
@Override
public BrowserRoot onGetRoot(@NonNull String clientPackageName,
int clientUid, @Nullable Bundle rootHints) {
return new BrowserRoot(MY_MEDIA_ROOT_ID, null);
}
@Override
public void onLoadChildren(@NonNull String parentId,
@NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {
// 给result设置信息,这地方的结果会回调到客户端
if(mCursor != null) {
while (mCursor.moveToNext()) {
MediaMetadataCompat metadata = new MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, "" + mCursor.getLong(1))
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, mCursor.getString(0))
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION,mCursor.getLong(2))
.build();
Log.v(LOG_TAG, "id" + mCursor.getLong(1) + " title:" + mCursor.getString(0));
mediaItems.add(createMediaItem(metadata));
mInfoMap.put(mediaItems.size()-1,metadata);
}
}
result.sendResult(mediaItems);
}
private MediaBrowserCompat.MediaItem createMediaItem(MediaMetadataCompat metadata){
return new MediaBrowserCompat.MediaItem(
metadata.getDescription(),
MediaBrowserCompat.MediaItem.FLAG_PLAYABLE
);
}
@Override
public void onDestroy() {
Log.d(LOG_TAG, "music service ondestory");
mMediaNotificationManager.stopNotification();
mediaSession.release();
}
服务端callback代码
public MediaSessionCallback(MusicService service,
Context context,
MediaPlayer player,
PlaybackStateCompat state,
MediaSessionCompat sessionCompat){
musicService = service;
mMediaPlayer = player;
mPlaybackState = state;
mSessionCompat = sessionCompat;
mContext = context;
}
@Override
public void onAddQueueItem(MediaDescriptionCompat description) {
}
@Override
public void onRemoveQueueItem(MediaDescriptionCompat description) {
}
@Override
public boolean onMediaButtonEvent(Intent mediaButtonEvent) {
Log.d(TAG,"onMediaButtonEvent" + mediaButtonEvent.getParcelableExtra(Intent.EXTRA_KEY_EVENT).toString());
super.onMediaButtonEvent(mediaButtonEvent);
return false;
}
public void setPlaybackState(PlaybackStateCompat state) {
mPlaybackState = state;
}
@Override
public void onPrepare() {
Log.d(TAG,"onPrepare");
}
@Override
public void onPlay() {
Log.e(TAG,"onPlay:" + mPlaybackState.getState());
if(mPlaybackState.getState() == PlaybackStateCompat.STATE_PAUSED){
mMediaPlayer.start();
mPlaybackState = new PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_PLAYING,0,1.0f)
.build();
mSessionCompat.setPlaybackState(mPlaybackState);
}
mSessionCompat.setActive(true);
}
@Override
public void onPause() {
Log.e(TAG,"onPause " + mPlaybackState.getState());
if(mPlaybackState.getState() == PlaybackStateCompat.STATE_PLAYING){
mMediaPlayer.pause();
Log.e(TAG,"onPause");
mPlaybackState = new PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_PAUSED,0,1.0f)
.build();
mSessionCompat.setPlaybackState(mPlaybackState);
}
mSessionCompat.setActive(false);
}
@Override
public void onStop() {
mSessionCompat.setActive(false);
}
@Override
public void onPlayFromUri(Uri uri, Bundle extras) {
Log.e(TAG,"onPlayFromUri");
try {
switch (mPlaybackState.getState()){
case PlaybackStateCompat.STATE_PLAYING:
case PlaybackStateCompat.STATE_PAUSED:
case PlaybackStateCompat.STATE_NONE:
mMediaPlayer.reset();
mMediaPlayer.setDataSource(mContext,uri);
mMediaPlayer.prepare();//准备同步
mSessionCompat.setMetadata(musicService.mInfoMap.get(index));
break;
}
}catch (IOException e){
e.printStackTrace();
}
}
@Override
public void onPlayFromSearch(String search, Bundle extras) {
}
@Override
public void onPlayFromMediaId(String mediaId, Bundle extras) {
if(extras != null) {
index = extras.getInt("index");
}
Uri uri = rawToUri(Integer.valueOf(musicService.mediaItems.get(index).getMediaId()));
onPlayFromUri(uri,null);
}
private Uri rawToUri(int id){
Uri uri = MediaStore.Audio.Media.getContentUri("external");
Uri uri2 = Uri.parse(
uri.buildUpon().appendPath(String.valueOf(id))
.toString());
return uri2;
}
@Override
public void onSkipToNext() {
if (index < musicService.mediaItems.size() - 1){
index++;
} else {
index = 0;
}
Uri uri = rawToUri(Integer.valueOf(musicService.mediaItems.get(index).getMediaId()));
onPlayFromUri(uri,null);
}
@Override
public void onSkipToPrevious() {
if (index > 0){
index--;
} else {
index = musicService.mediaItems.size()-1;
}
Uri uri = rawToUri(Integer.valueOf(musicService.mediaItems.get(index).getMediaId()));
onPlayFromUri(uri,null);
}
@Override
public void onSeekTo(long pos) {
long pose = musicService.mInfoMap.get(index).getLong(MediaMetadataCompat.METADATA_KEY_DURATION);
mMediaPlayer.seekTo((int) (pose*pos)/100);
}
notification代码这地方也不放了,以上就是 创建MediaBrowserService,使用MediaSession以及MediaController创建的播放器示例代码,仅供参考