Android基於MediaBroswerService的App實現概述

前言

如何實現一個音樂播放App,而後讓其能夠被第三方的Android app打開,並獲取其中的歌單,曲目列表,同時控制其播放呢?現有應用市場上,已經有相應的實現。好比百度CarLife對QQ音樂,喜馬拉雅等的調用。android

百度Carlife

百度Carlife

在百度的Carlife App中,咱們能夠看到,只要咱們本地的裝了QQ音樂App,其就能夠喚起,而後獲取其中的歌曲數據,而後進行播放,這個是如何實現的呢?session

需求

  • 能夠獲取音樂播放器的歌曲列表
  • 能夠控制音樂播放器的播放
  • 能夠將音樂播放器的狀態同步到第三方App
  • 可以和第三方App間進行相互通訊

相似於CarLife 對音樂App的喚起,首先第三方App開啓後,便可拉起音樂App,而後獲取其中的歌單,打開歌單以後,獲取歌單內的歌曲列表,點擊進行播放,能夠進行播放,暫停,下一首,上一首的控制。架構

技術實現

谷歌官方提供了MediaBroswerService,經過其能夠幫助咱們實現上述的需求。app

MediaBroswerService

  • Android多媒體架構

Android多媒體播放採用client,server架構,一個server能夠對應多個client,client在使用的時候須要先鏈接到server,雙方經過設置的一些callback來進行狀態的同步。ide

Android多媒體架構

使用MediaBrowserService播放ui

Android多媒體架構

客戶端須要建立MediaBrowser,服務端須要實現MediaBrowserService,在創建鏈接後,兩端之間的交互主要經過MediaController和MediaSession。兩個類之間經過預先定義的callback進行交互,MediaSession控制着播放器的播放,MediaController來控制着UI的變化。this

MediaController和MediaSession

  • Media session

一個session持有了播放器的狀態和關於正在播放的一些信息,一個seesion能夠接收來自一個或多個媒體播放器的callback。這使得經過其它設備來控制成爲可能。spa

  • Media controller

咱們的UI只是和Media controller交互,而不是Player 自己,Media controller會將一些控制信息傳遞給Media Session,它也會在seesion發生變化的時候,獲得來自session的回調,一個media controller一次只能夠鏈接一個session。當使用一個media contoller和Session的時候,咱們能夠在運行期部署多個播放器,在其執行的時候根據設備去修改app的外觀。code

使用MediaBrowserService可讓Android Wear, Auto很是容易找咱們的App,鏈接它,瀏覽它的內容,控制其播放,而徹底不須要接觸咱們的UI Activity。component

服務端實現

  • 服務端基礎配置

mainfeat 配置

<service android:name=".MediaPlaybackService">
  <intent-filter>
    <action android:name="android.media.browse.MediaBrowserService" />
  </intent-filter>
</service>

MediaPlaybackService的初始化

public class MediaPlaybackService extends MediaBrowserServiceCompat {

  @Override

  public void onCreate() {

    super.onCreate();

    // 1. 初始化 MediaSession
        mSession = new MediaSessionCompat(this, "MusicService");

    // 2. 設置 MedisSessionCallback
        mSession.setCallback(mSessionCallback);

    // 3. 開啓 MediaButton 和 TransportControls 的支持
        mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
                 MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);

    // 4. 初始化 PlaybackState
         mStateBuilder = new PlaybackStateCompat.Builder()
                 .setActions(
                         PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PLAY_PAUSE);
         mSession.setPlaybackState(mStateBuilder.build());

    // 5. 關聯 SessionToken
         setSessionToken(mSession.getSessionToken());
  }
}

根據包名作權限判斷以後,返回根路徑

  @Override

  public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) {

    // 根據包名對每一個訪問端作一些訪問權限判斷等

  }

用來根據mediaID來返回第三放App所須要得到媒體數據

  @Override

  public void onLoadChildren(final String parentMediaId,

    final Result<List<MediaItem>> result) {

    // 根據parentMediaId返回播放列表相關信息

  }

客戶端鏈接

private void initMediaBrowser() {

    //1.待鏈接的服務

    ComponentName componentName = new ComponentName("com.example.android.uamp","com.example.android.uamp.MusicService");

    //2.建立MediaBrowser

    mMediaBrowser = new MediaBrowserCompat(this, componentName, mConnectionCallbacks, null);

    //3.創建鏈接

    mMediaBrowser.connect();

}

設置相應的callback,鏈接Callback,數據變化Callback

鏈接狀態同步

數據變化Callback設置

private final MediaBrowserCompat.ConnectionCallback mConnectionCallbacks =
        new MediaBrowserCompat.ConnectionCallback() {

      @Override
      public void onConnected() {
        //鏈接成功回調
       }

       @Override
       public void onConnectionSuspended() {
       //鏈接中斷回調
       }

      @Override
      public void onConnectionFailed() {
        //鏈接失敗回調
     }
};
MediaControllerCompat.Callback controllerCallback =

    new MediaControllerCompat.Callback() {
          public void onSessionDestroyed() {
         //Session銷燬
        }

        @Override
        public void onRepeatModeChanged(int repeatMode) {
          //循環模式發生變化
        }

        @Override
        public void onShuffleModeChanged(int shuffleMode) {
          //隨機模式發生變化
        }

        @Override
        public void onMetadataChanged(MediaMetadataCompat metadata) {
        //數據變化
        }

        @Override
        public void onPlaybackStateChanged(PlaybackStateCompat state) {
        //播放狀態變化
        }
};

客戶端與服務端數據交互

MediaBrowser經過調用subscribe,會回調到MediaService的onLoadChildren,在這裏作一個判斷而後構造相應的列表將列表數據返回。返回數據以後。

  • 根據MediaID獲取數據

客戶端經過調用subscribe方法,傳遞MediaID,在SubscriptionCallback的方法中進行處理。

mMediaBrowser.subscribe("ID", new MediaBrowserCompat.SubscriptionCallback() {
    @Override
    public void onChildrenLoaded(@NonNull String parentId, @NonNull List<MediaBrowserCompat.MediaItem> children) {
       //children 爲來自Service的列表數據
    }
});

服務端和客戶端之間傳遞的數據爲MediaItem列表。MediaItem中具有的字段:MediaId,Title,SubTitle,Description,Icon,IconUri,MediaUri等字段。經過其能夠幫助咱們攜帶一些數據來進行歌曲的展現和播放。

@Override
 public void onLoadChildren(@NonNull final String parentMediaId,
                            @NonNull final Result<List<MediaItem>> result) {
     List<MediaItem> items = new ArrayList<>();
      //根據MediaID作數據填充
     switch (parentMediaId) {
         case:
         default: break;
     }
     result.sendResult(items);
 }
  • 發送自定義數據獲取內容

客戶端經過調用sendCustomAction,根據與服務端的協商,制定相應的action類型,進行數據的傳遞交互。

mMediaBrowser.sendCustomAction(action, extras, new MediaBrowserCompat.CustomActionCallback() {
    @Override
    public void onProgressUpdate(String action, Bundle extras, Bundle data) {
        super.onProgressUpdate(action, extras, data);
    }

    @Override
    public void onResult(String action, Bundle extras, Bundle resultData) {
        super.onResult(action, extras, resultData);
    }

    @Override
    public void onError(String action, Bundle extras, Bundle data) {
        super.onError(action, extras, data);
    }
});

服務端實現onCustomAction,根據action類型返回相應的數據

@Override
 public void onCustomAction(@NonNull String action, Bundle extras, @NonNull Result<Bundle> result) {
      //分支判斷
     if (GET_LIST.equals(action)) {
         Bundle bundle = new Bundle();
         ArrayList<String> list = new ArrayList<>();
          //填充數據
         bundle.putStringArrayList(LIST_NAMES, list);
         result.sendResult(bundle);
     }
 }

播放控制

  • 客戶端

客戶端經過getMediaController getTransportControls()來進行播放,暫停,上一首,下一首的控制。

//獲取播放狀態
int pbState = MediaControllerCompat.getMediaController(MainActivity.this).getPlaybackState().getState();
//根據播放狀態進行播放控制
if (pbState == PlaybackStateCompat.STATE_PLAYING) {
    MediaControllerCompat.getMediaController(MainActivity.this).getTransportControls().pause();
} else {
    MediaControllerCompat.getMediaController(MainActivity.this).getTransportControls().play();
}
  • 服務端

在服務端爲MediaSession設置SessionCallback,來實現相應的播放功能。

mSession.setCallback(mSessionCallback);

image.png

客戶端經過MediaController能夠進行播放,暫停,根據MediaID播放下一個音樂,音樂播放快進等。全部的操做會回調到服務端的MediaSessionCallback的play,seekTo等方法,須要咱們本身實現,在其中控制播放隊列,而後根據列表播放的狀況來動態的變動隊列。

播放狀態同步

對於播放狀態的同步,好比當前播放到哪個歌曲,當前是暫停仍是播放中。客戶端經過Controller回調就能夠獲得相應的變化,可是,變化狀態,服務端如何發送呢?

setMetadata(android.media.MediaMetadata));
setPlaybackState(android.media.session.PlaybackState));

設置當前的歌曲信息,設置當前的播放狀態。設置以後,客戶端將會獲得更新。

獲取手機內的媒體服務

private void discoverBrowseableMediaApps(Context context) {
    PackageManager packageManager = context.getPackageManager();
    Intent intent = new Intent(MediaBrowserService.SERVICE_INTERFACE);
    List<ResolveInfo> services = packageManager.queryIntentServices(intent, 0);
    for (ResolveInfo resolveInfo : services) {
        if (resolveInfo.serviceInfo != null && resolveInfo.serviceInfo.applicationInfo != null) {

            ApplicationInfo applicationInfo = resolveInfo.serviceInfo.applicationInfo;
            String label = (String) packageManager.getApplicationLabel(applicationInfo);
            Drawable icon = packageManager.getApplicationIcon(applicationInfo);
            String className = resolveInfo.serviceInfo.name;
            String packageName = resolveInfo.serviceInfo.packageName;

            MusicService service = new MusicService();
            service.icon = icon;
            service.lable = label;
            service.className = className;
            service.packageName = packageName;
            musicServiceList.add(service);
        }
    }

}

結語

經過本篇文章,對MediaBroswerService作了一個簡單的介紹,但對於播放器的具體實現,特別是在服務端仍是比較複雜的,須要維護歌曲隊列,進行播放,同時負責狀態的更新。

相關文章
相關標籤/搜索