Building an audio app - Building a media browser client Android


Building a media browser client

https://developer.android.com/guide/topics/media-apps/audio-app/building-a-mediabrowser-client

클라이언트/서버 디자인을 완료하기 위해 UI코드를 포함하는 미디어 컨트롤러, 미디어 브라우져와 연동된 액티비티 컴포넌트를 만들어야 한다.

미디어 브라우져는 두 가지 중요한 기능을 수행하는데, 미디어 브라우져 서비스와 연결하고 UI를 위한 미디어 컨트롤러를 생성한다.

일러두기: 미디어브라우져의 추천되는 구현은 MediaBrowserCompat 인데 Media-Compat support library 에서 정의되어 있다. 이 페이지에서의 용어 "MediaBrowser" 는 MediaBrowserCompat 의 인스턴스를 나타낸다.

MediaBrowserService 로 접속

클라이언트 액티비티가 생성되면 MediaBrowserService 로 접속한다. 핸드쉐이크와 댄스가 연결된다. 액티비티의 라이프사이클 콜백을 다음과 같이 수정한다.
- onCreate() 는 MediaBrowserCompat 을 생성한다. MediaBrowserService와 정의한 MediaBrowserCompat.ConnectionCallback 의 이름을 전달한다.
- onStart() 는 MediaBrowserService 에 연결한다. 여기에 MediaBrowserCompat.ConnectionCallback 의 마법이 등장한다. 연결이 성공하면 onConnect()콜백은 미디어 컨트롤러를 생성하고 미디어 세션에 연결하고, UI제어를 MediaController 에 연결하고 미디어 세션으로 부터 콜백을 받기 위해 컨트롤러를 등록한다.
- onResume() 는 오디오 스트림을 할당해 장치상의 볼륨 컨트롤에 반응하게 한다.
- onStop() 은 액티비티가 멈추면 미디어 브라우져와의 접속 종료하고 MediaController.Callback 을 등록취소한다.

class MediaPlayerActivity: AppCompatActivity() {
  private lateinit var mMediaBrowser: MediaBrowserCompat
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // MediaBrowserServiceCompat 을 생성
    mMediaBrowser = MediaBrowserCompat(this, ComponentName(this, MediaPlaybackService::class.java), mConnectionCallbacks, null)
  }
  public override fun onStart() {
    super.onStart()
    mMediaBrowser.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.getMedaController(this)?.unregisterCallback(controllerCallback)
    mMediaBrowser.disconnect()
  }
}

MediaBrowserCompat.ConnectionCallback 커스터마이즈

액티비티가 MediaBrowserCompat 을 생성하면 ConnectionCallback 인스턴스를 반드시 생성해야 한다. onConnected() 를 통해 MediaBrowserService 로부터 미디어 세션 토큰을 얻으며 이 토큰을 통해 MediaControllerCompat 을 생성한다.

MediaControllerCompat.setMediaController() 편의 함수를 사용하여 컨트롤러의 연결을 저장한다. 이를 통해 미디어 버튼을 다룰 수 있게 한다. MediaControllerCompat.getMediaController() 를 호출해 트랜스포트 컨트롤을 만들 때 컨트롤을 얻을 수 있게 한다.

다음 코드는 onConnected() 메소드를 어떻게 수정하는지 보여준다.

private val mConnectionCallbacks = object: MediaBrowserCompat.ConnectionCallback() {
  override fun onConnected() {
    // 미디어 세션으로 부터 토큰을 얻는다
    mMediaBrowser.sessionToken.also { token ->
      // MediaControllerCompat을 생성한다.
      val mediaController = MediaControllerCompat(this@MediaPlayerActivity, token)
      // 컨트롤러를 저장한다
      MediaControllerCompat.setMediaController(this@MediaPlayerActivity, mediaController)
    }
    // UI 만들기를 끝마친다
    buildTransportControls()
  }
  override fun onConnectionSuspended() {
    // 서비스가 크래쉬됨. 이게 자동적으로 재접속될때까지 transport 컨트롤을 막는다
  }
  override fun onConnectionFailed() {
    // 서비스가 접속을 거부함
  }
}

미디어 컨트롤러에 UI접속하기

위의 ConnectionCallback 샘플 코드는 buildTransportControls() 로의 호출을 포함한다. 플레이어를 제어하는 UI엘리먼트에 onClickListeners를 설정해야 한다. 각각을 위한 적절한 MediaControllerCompat.TransportControls 메소드를 선택한다.

코드는 이와 유사한데 각 버튼을 위한 onClickListener 가 있다.

fun buildTransportControls() {
  val mediaController = MediaControllerCompat.getMediaController(this@MediaPlayerActivity)
  // 재생/일시정지 버튼을 위한 뷰를 잡는다
  mPlayPause = fundViewById<ImageView>(R.id.play_pause).apply {
    setOnClickListener {
      // 이게 재생/일시정지 버튼이므로 현 상태를 혹인하고 적절한 액션을 선택
      val pbState = mediaController.playbackState.state
      if (pbState == PlaybackStateCompat.STATE_PLAYING) {
        mediaController.transportControls.pause()
      } else {
        mediaController.transportControls.play()
      }
    }
  }

  // 초기 상태를 보여준다
  val metadata = mediaController.metadata
  val pbState = mediaController.playbackState
  // 싱크상에 콜백을 유지하도록 등록
  mediaController.registerCallback(controllerCallback)
}

TransportControls 메소드는 서비스의 미디어 세션에 콜백을 보낸다. 각 컨트롤을 위한 MediaSessionCompat.Callback 메소드를 정의했는지 확인한다.

미디어 세션에서 동기화상태로 유지하기

UI는 플레이스테이트와 메타데이터로 미디어 세션의 현상태를 보여주어야 한다. 트랜스포트 컨트롤을 생성할 때 세션의 현상태를 얻을 수 있으며 이를 UI에 표시한다. 그리고 상태와 사용가능한 액션을 기반으로 트랜스포트 컨트롤을 활성화하고 비활성화 할 수 있다.

미디어 세션으로부터 상태 또는 메타데이터 변화로 부터 콜백을 받으려면 MediaControllerCompat.Callback을 다음의 두 메소드와 함께 정의한다.

private var controllerCallback = object: MediaControllerCompat.Callback() {
  override fun onMetadataChanged(metadata: MediaMetadataCompat?) {}
  override fun onPlaybackStateChanged(state: PlaybackStateCompat?) {}
}

트랜스포트 컨트롤을 만들고 콜백을 등록하며 액티비티 종료때 등록제거한다 (액티비티의 onStop() 라이프사이클 메소드에서)



덧글

댓글 입력 영역