미디어 브라우저 클라이언트 빌드

클라이언트/서버 디자인을 완료하려면 UI 코드, 연결된 MediaController 및 MediaBrowser를 포함하는 활동 구성요소를 빌드해야 합니다.

MediaBrowser에는 두 가지 중요한 기능이 있는데, 하나는 MediaBrowserService에 연결하는 것이고 다른 하나는 연결 이후 UI의 MediaController를 만드는 것입니다.

참고: MediaBrowser의 권장 구현은 Media-Compat 지원 라이브러리에 정의된 MediaBrowserCompat입니다. 이 페이지에서 '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의 인스턴스를 만들어야 합니다. MediaBrowserService에서 미디어 세션 토큰을 검색하고 토큰을 사용하여 MediaControllerCompat를 만들려면 onConnected() 메서드를 수정합니다.

편의 메서드 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 요소의 onClickListeners를 설정해야 합니다. 각각에 적절한 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 및 Metadata에서 설명한 대로 미디어 세션의 현재 상태를 표시해야 합니다. 전송 컨트롤을 만들면 세션의 현재 상태를 가져와 UI에 표시하고, 상태 및 사용 가능한 작업에 따라 전송 컨트롤을 사용 설정 또는 사용 중지할 수 있습니다.

상태 또는 메타데이터가 변경될 때마다 미디어 세션에서 콜백을 수신하려면 다음 두 가지 메서드를 사용하여 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
    }
  };