メディア ブラウザ クライアントの作成

クライアント / サーバーの設計を完了するには、UI のコード、関連する MediaController、MediaBrowser を含むアクティビティ コンポーネントを作成する必要があります。

MediaBrowser は、MediaBrowserService への接続と、それに続く UI 用 MediaController の作成という、2 つの重要な機能を担います。

注: MediaBrowser の推奨実装は MediaBrowserCompat です。これは Media-Compat サポート ライブラリで定義されています。 このページ全体を通して、「MediaBrowser」という用語は MediaBrowserCompat のインスタンスを指します。

MediaBrowserService への接続

クライアント アクティビティでは、作成されたときに MediaBrowserService に接続します。少し握手とダンスが伴います。アクティビティのライフサイクル コールバックを次のように変更します。

  • onCreate() では、MediaBrowserCompat を構築します。ここで、MediaBrowserService の名前と、定義した MediaBrowserCompat.ConnectionCallback を渡します。
  • onStart() では、MediaBrowserService に接続します。ここで、MediaBrowserCompat.ConnectionCallback が利用されます。接続が成功したら、onConnect() コールバックでメディア コントローラを作成し、それをメディア セッションに接続して UI を MediaController に接続したうえで、メディア セッションからのコールバックを受信するようコントローラを登録します。
  • onResume() では、アプリがデバイスの音量コントロールに応答できるように音声ストリームを設定します。
  • onStop() では、MediaBrowser を切断し、アクティビティが停止した場合は MediaController.Callback の登録を解除します。

Kotlin

class MediaPlayerActivity : AppCompatActivity() {

    private lateinit var mediaBrowser: MediaBrowserCompat

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ...
        // Create MediaBrowserServiceCompat
        mediaBrowser = MediaBrowserCompat(
                this,
                ComponentName(this, MediaPlaybackService::class.java),
                connectionCallbacks,
                null // optional Bundle
        )
    }

    public override fun onStart() {
        super.onStart()
        mediaBrowser.connect()
    }

    public override fun onResume() {
        super.onResume()
        volumeControlStream = AudioManager.STREAM_MUSIC
    }

    public override fun onStop() {
        super.onStop()
        // (see "stay in sync with the MediaSession")
        MediaControllerCompat.getMediaController(this)?.unregisterCallback(controllerCallback)
        mediaBrowser.disconnect()
    }
}

Java

public class MediaPlayerActivity extends AppCompatActivity {
  private MediaBrowserCompat mediaBrowser;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // ...
    // Create MediaBrowserServiceCompat
    mediaBrowser = new MediaBrowserCompat(this,
      new ComponentName(this, MediaPlaybackService.class),
        connectionCallbacks,
        null); // optional Bundle
  }

  @Override
  public void onStart() {
    super.onStart();
    mediaBrowser.connect();
  }

  @Override
  public void onResume() {
    super.onResume();
    setVolumeControlStream(AudioManager.STREAM_MUSIC);
  }

  @Override
  public void onStop() {
    super.onStop();
    // (see "stay in sync with the MediaSession")
    if (MediaControllerCompat.getMediaController(MediaPlayerActivity.this) != null) {
      MediaControllerCompat.getMediaController(MediaPlayerActivity.this).unregisterCallback(controllerCallback);
    }
    mediaBrowser.disconnect();

  }
}

MediaBrowserCompat.ConnectionCallback のカスタマイズ

アクティビティで MediaBrowserCompat を構築するときは、ConnectionCallback のインスタンスを作成する必要があります。その onConnected() メソッドを変更して、MediaBrowserService からメディア セッション トークンを取得し、そのトークンを使用して MediaControllerCompat を作成します。

コンビニエンス メソッド MediaControllerCompat.setMediaController() を使用して、コントローラへのリンクを保存します。これにより、メディアボタンの処理が可能になります。また、トランスポート コントロールを作成する際に MediaControllerCompat.getMediaController() を呼び出してコントローラを取得することもできます。

次のコードサンプルは、onConnected() メソッドをどのように変更するかを示しています。

Kotlin

private val connectionCallbacks = object : MediaBrowserCompat.ConnectionCallback() {
    override fun onConnected() {

        // Get the token for the MediaSession
        mediaBrowser.sessionToken.also { token ->

            // Create a MediaControllerCompat
            val mediaController = MediaControllerCompat(
                    this@MediaPlayerActivity, // Context
                    token
            )

            // Save the controller
            MediaControllerCompat.setMediaController(this@MediaPlayerActivity, mediaController)
        }

        // Finish building the UI
        buildTransportControls()
    }

    override fun onConnectionSuspended() {
        // The Service has crashed. Disable transport controls until it automatically reconnects
    }

    override fun onConnectionFailed() {
        // The Service has refused our connection
    }
}

Java

private final MediaBrowserCompat.ConnectionCallback connectionCallbacks =
  new MediaBrowserCompat.ConnectionCallback() {
    @Override
    public void onConnected() {

      // Get the token for the MediaSession
      MediaSessionCompat.Token token = mediaBrowser.getSessionToken();

      // Create a MediaControllerCompat
      MediaControllerCompat mediaController =
        new MediaControllerCompat(MediaPlayerActivity.this, // Context
        token);

      // Save the controller
      MediaControllerCompat.setMediaController(MediaPlayerActivity.this, mediaController);

      // Finish building the UI
      buildTransportControls();
    }

    @Override
    public void onConnectionSuspended() {
      // The Service has crashed. Disable transport controls until it automatically reconnects
    }

    @Override
    public void onConnectionFailed() {
      // The Service has refused our connection
    }
  };

UI のメディア コントローラへの接続

上記の ConnectionCallback サンプルコードには、UI を具現化する buildTransportControls() 呼び出しが含まれています。プレーヤーを制御する UI 要素には、onClickListener を設定する必要があります。それぞれに対して適切な MediaControllerCompat.TransportControls メソッドを選択してください。

各ボタンに onClickListener を設定したコードは、次のようになります。

Kotlin

fun buildTransportControls() {
    val mediaController = MediaControllerCompat.getMediaController(this@MediaPlayerActivity)
    // Grab the view for the play/pause button
    playPause = findViewById<ImageView>(R.id.play_pause).apply {
        setOnClickListener {
            // Since this is a play/pause button, you'll need to test the current state
            // and choose the action accordingly

            val pbState = mediaController.playbackState.state
            if (pbState == PlaybackStateCompat.STATE_PLAYING) {
                mediaController.transportControls.pause()
            } else {
                mediaController.transportControls.play()
            }
        }
    }

    // Display the initial state
    val metadata = mediaController.metadata
    val pbState = mediaController.playbackState

    // Register a Callback to stay in sync
    mediaController.registerCallback(controllerCallback)
}

Java

void buildTransportControls()
{
  // Grab the view for the play/pause button
  playPause = (ImageView) findViewById(R.id.play_pause);

  // Attach a listener to the button
  playPause.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
      // Since this is a play/pause button, you'll need to test the current state
      // and choose the action accordingly

      int pbState = MediaControllerCompat.getMediaController(MediaPlayerActivity.this).getPlaybackState().getState();
      if (pbState == PlaybackStateCompat.STATE_PLAYING) {
        MediaControllerCompat.getMediaController(MediaPlayerActivity.this).getTransportControls().pause();
      } else {
        MediaControllerCompat.getMediaController(MediaPlayerActivity.this).getTransportControls().play();
      }
  });

  MediaControllerCompat mediaController = MediaControllerCompat.getMediaController(MediaPlayerActivity.this);

  // Display the initial state
  MediaMetadataCompat metadata = mediaController.getMetadata();
  PlaybackStateCompat pbState = mediaController.getPlaybackState();

  // Register a Callback to stay in sync
  mediaController.registerCallback(controllerCallback);
}
}

TransportControls メソッドにより、サービスのメディア セッションにコールバックが送信されます。各コントロールに対応する MediaSessionCompat.Callback メソッドを定義していることを確認してください。

メディア セッションとの同期の維持

UI には、メディア セッションの現在の状態を PlaybackState とメタデータの内容に沿って表示するようにします。トランスポート コントロールを作成すると、セッションの現在の状態を取得して UI に表示し、その状態と現在実施可能なアクションに基づいてトランスポート コントロールを有効または無効にできます。

状態またはメタデータが変更されるたびにメディア セッションからコールバックを受信するには、次の 2 つのメソッドを使用して MediaControllerCompat.Callback を定義します。

Kotlin

private var controllerCallback = object : MediaControllerCompat.Callback() {

    override fun onMetadataChanged(metadata: MediaMetadataCompat?) {}

    override fun onPlaybackStateChanged(state: PlaybackStateCompat?) {}
}

Java

MediaControllerCompat.Callback controllerCallback =
  new MediaControllerCompat.Callback() {
    @Override
    public void onMetadataChanged(MediaMetadataCompat metadata) {}

    @Override
    public void onPlaybackStateChanged(PlaybackStateCompat state) {}
  };

このコールバックは、トランスポート コントロールの作成時に登録し(buildTransportControls() メソッドを参照)、アクティビティの停止時に(そのアクティビティの onStop() ライフサイクル メソッドで)登録を解除します。

メディア セッションが破棄されたときに接続を解除する

メディア セッションが無効になると、onSessionDestroyed() コールバックが発行されます。その場合、MediaBrowserService の存続期間中はセッションが再び機能しなくなります。MediaBrowser 関連の機能は引き続き機能しますが、破棄されたメディア セッションからの再生をユーザーが表示または制御することはできないため、アプリの価値が低下する可能性があります。

したがって、セッションが破棄された場合は、disconnect() を呼び出して MediaBrowserService との接続を解除する必要があります。これにより、ブラウザ サービスはクライアントにバインドされなくなり、OS によって破棄される可能性があります。後で MediaBrowserService に再接続する必要がある場合(アプリがメディアアプリへの永続的な接続を維持する場合など)は、古いインスタンスを再利用するのではなく、MediaBrowser新しいインスタンスを作成します。

次のコード スニペットは、メディア セッションが破棄されたときにブラウザ サービスから切断するコールバックの実装を示しています。

Kotlin

private var controllerCallback = object : MediaControllerCompat.Callback() {
    override fun onSessionDestroyed() {
      mediaBrowser.disconnect()
      // maybe schedule a reconnection using a new MediaBrowser instance
    }
}

Java

MediaControllerCompat.Callback controllerCallback =
  new MediaControllerCompat.Callback() {
    @Override
    public void onSessionDestroyed() {
      mediaBrowser.disconnect();
      // maybe schedule a reconnection using a new MediaBrowser instance
    }
  };