About Media Browser Service

Keywords: Session Android Mobile MediaPlayer

Today's topic is related to media broadcasting, especially music broadcasting. When it comes to music broadcasting, everyone should have used music App.
Usually, the implementation of a music App mainly involves the following points:
1. Getting Music Data from Server
2. Player's various playing States and UI display in different states when playing music
3. Controlling the state of the player through UI interface during playback
4. UI controls how to associate with playback services and synchronize status
4. How to ensure that the broadcasting service is not killed in the background broadcasting process

For the above points, Android has provided us with a complete solution. It has packaged these operations very well. We only need to pay attention to data acquisition and song playing. Android provides a compatible version of the API in support-v4, so it's best to use this version to compatible with low-version systems.

The key categories are as follows:
1. Media Browser Service Compat Media Browser Service
2. Media Browser Compat Media Browser
2. Media Controller Compat Media Controller
3. Media Session Compat Media Session
Let's talk about it one by one.

MediaBrowserServiceCompat

This class has two functions:
1. Backstage services for music broadcasting
2. The music data acquisition service in the client, through which all the music data can be obtained interactively (or directly from the local music data in the mobile phone)

Since we know that this class is a subset of Service, it is understandable that it is a background service for music playing. However, as a background service, this class is not implemented directly by itself, but through the class of MediaSession Compat media session. During use, media sessions are associated with the service, and all playback operations are implemented by MediaSession Compat.

Data acquisition is controlled by the following two methods of MediaBrowser Service Compat:

@Override
public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid,
                             Bundle rootHints) {
    /**
     * Before returning the data, we can control the black and white list to control different clients to browse different media resources.
     * */
    if(!PackageUtil.isCallerAllowed(this, clientPackageName, clientUid)) {
        return new BrowserRoot(null, null);
    }
    //This method is invoked only when the service is connected
    //Returning a BrowserRoot whose rootId is not empty indicates that the client can connect to the service or browse its media resources.
    //If null is returned, the client cannot flow media resources
    return new BrowserRoot(BrowserRootId.MEDIA_ID_ROOT, null);
}

@Override
public void onLoadChildren(@NonNull String parentId, @NonNull Result<List<MediaItem>> result) {

    /***
     * The parentId in this method has nothing to do with the RootId returned in the above method onGetRoot
     * After the client connects, it can initiate a data acquisition request by calling MediaBrowserCompat.subscribe() repeatedly.
     * Each time subscribe() method is called, an onLoadChildren () callback is sent to the service, and a list of MediaBrowser. MediaItem (music data) objects is returned.
     *
     * Each MediaItem has a unique ID string, which is actually an implicit token.
     * When a customer wants to open a submenu or play an item, it passes in an ID.
     */
    if(BrowserRootId.MEDIA_ID_MUSIC_LIST_REFRESH.equals(parentId)) {
        //result.detach() must be called before the current method is executed and returned, otherwise the request cannot be initiated.
        result.detach();
        MusicProvider.getInstance().requestMusic(result);
        //If you want to get data through http requests, you must call result.detach(); method first, as mentioned above; otherwise, an exception will occur. After the http request is completed, the data is returned by calling result. sendResult (mMetadata CompatList), which is displayed on the interface through callback in the registered interface MediaBrowserCompat.SubscriptionCallback.
        //And the data type returned here must be MediaBrowser.MediaItem
    } else {
        result.detach();
    }
}

MediaBrowserCompat

As mentioned earlier, Media Browser Service Compat (Media Browsing Service) is used as a data request service to obtain data, so there will be a media browsing client to initiate media data acquisition requests, which is the client.
Before calling the MediaBrowserCompat.subscribe() method to initiate data requests, we must ensure that MediaBrowserCompat connects to the media browsing service in the following way:

//Connect MediaBrowserServiceCompat with the following code to obtain the media session token after successful connection
//Creating Media Controller Compat through Media Session token 
//At this point, the Media Controller Compat is associated with the Media Session Compat
MediaBrowserCompat mediaBrowser = new MediaBrowserCompat(this,
                new ComponentName(this, MusicService.class), mConnectionCallback, null);

//Callback interface after successful connection of media browsing service
final MediaBrowserCompat.ConnectionCallback mConnectionCallback =
    new MediaBrowserCompat.ConnectionCallback() {
        @Override
        public void onConnected() {

            try {
                //Get the media session token associated with MediaBrowserServiceCompat
                MediaSessionCompat.Token token = mMediaBrowser.getSessionToken();
                //Create and associate media controllers through media session token
                //After the association, the media controller can control the various playing states of the player.
                MediaControllerCompat mediaController = new MediaControllerCompat(this, token);
                //Associate the media controller with the current Context context Context
                //After this association, we can get the current MediaController Compat through the current Context context when we operate some UI s on the interface.
                //MediaControllerCompat controller = MediaControllerCompat.getMediaController((Activity) context);
                MediaControllerCompat.setMediaController(this, mediaController);
                //Register the callback interface mediaController. registerCallback (mMedia Controller Callback) for the media controller;
            } catch (RemoteException e) {
                onMediaControllerConnectedFailed();
            }
        }
    };

//Media Controller Controls Callback Interface in Playback Process
final MediaControllerCompat.Callback mMediaControllerCallback =
   new MediaControllerCompat.Callback() {
        @Override
        public void onPlaybackStateChanged(@NonNull PlaybackStateCompat state) {
            //Callback when playback status changes
            onMediaPlayStateChanged(state);
        }

        @Override
        public void onMetadataChanged(MediaMetadataCompat metadata) {

            if(metadata == null) {
                return;
            }
            //Callback when the broadcast media data changes
            onPlayMetadataChanged(metadata);
        }
    };



//Initiate data requests
 //Unsubscribe first
 mediaBrowser.unsubscribe(BrowserRootId.MEDIA_ID_MUSIC_LIST_REFRESH);
 //Re-subscribe to Browser RootId
 //After calling this method, the onGetRoot and onLoadChildren methods in MusicService are executed
 //The onGetRoot method (called only once) determines whether the current client is allowed to connect to the service and access media data.
 //If the connection service is allowed and the media data is also allowed, the onLoadChildren method is then called to start the data acquisition.
 //When the data acquisition is successful, the subscription callback interface is invoked to return the data back.
 mediaBrowser.subscribe(BrowserRootId.MEDIA_ID_MUSIC_LIST_REFRESH, mSubscriptionCallback);

//Callback interface for initiating media browsing requests to media traffic services
final MediaBrowserCompat.SubscriptionCallback mSubscriptionCallback =
    new MediaBrowserCompat.SubscriptionCallback() {
        @Override
        public void onChildrenLoaded(@NonNull String parentId,
                                     @NonNull List<MediaBrowserCompat.MediaItem> children) {
            //Callback after successful data acquisition
        }

        @Override
        public void onError(@NonNull String id) {
            //Callback for data acquisition failure
        }
    };

MediaSessionCompat

As mentioned earlier, media playback of MediaBrowserServiceCompat is actually achieved through the associated MediaSession Compat, and the associated way is very simple:

MediaSessionCompat mSession = new MediaSessionCompat(this, "MusicService");
setSessionToken(mSession.getSessionToken());
mSession.setCallback(new MediaSessionCompat.Callback());
mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);

//The playback control of MediaSessionCompat is implemented entirely through the interface MediaSessionCompat.Callback.
@Override
 public void onPlay() {
//Triggered by clicking the Play button
//Triggered by MediaController Compat. getTransportControls (). play ();
 }

 @Override
 public void onSkipToQueueItem(long queueId) {
     //Triggered when playing a specified pair of column media
     //Triggered by MediaController Compat. getTransportControls (). onSkipToQueueItem (queueId); and
 }

 @Override
 public void onSeekTo(long position) {
     //Triggered when set to a specified schedule
     //MediaControllerCompat.getTransportControls().seekTo(position);
 }

 @Override
 public void onPlayFromMediaId(String mediaId, Bundle extras) {
//Triggered when playing specified media data
//MediaControllerCompat.getTransportControls().playFromMediaId(mediaItem.getMediaId(), null);        
 }

 @Override
 public void onPause() {
//Triggered on pause
//MediaControllerCompat.getTransportControls().pause();
 }

 @Override
 public void onStop() {
//Triggered when stop playing
//MediaControllerCompat.getTransportControls().stop();
 }

 @Override
 public void onSkipToNext() {
//Triggered by jumping to the next song
//MediaControllerCompat.getTransportControls().skipToNext();
 }

 @Override
 public void onSkipToPrevious() {
//Triggered by jumping to the last one
//MediaControllerCompat.getTransportControls().skipToPrevious();
 }
//Of course, there are many callback functions, you can check them by yourselves.
}

MediaControllerCompat

The way media controllers are created and associated is described above, and the way they control the state of the player is explained in the code comments above, which are basically controlled by MediaController Compat. getTransportControls ().

The relevant use of media services and attention points have been introduced here, using this api to achieve music APP is still very convenient and fast, and we can easily switch players, such as MediaPlayer,ExoPlayer, etc. If you have suggestions and questions, welcome to the blog on the page scan plus QQ group communication.

Posted by kavitam on Thu, 04 Jul 2019 11:46:45 -0700