Bootstrap

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

请看官方架构图
使用MediaSessionService
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是对应的

TransportControlsMediaSession.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端就能收到相应回调,做具体的播放控制

服务端回调给客户端

MediaSessionMediaController.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创建的播放器示例代码,仅供参考

;