ExoPlayer는 플레이어의 상태 이벤트 콜백을 제공하는 리스너 인터페이스를 제공한다!!
플레이어 리스너 인터페이스를 생성하는 익명함수 playbackStateListener를 만든다. 리스너 인터페이스는 상태변화 이벤트를 제공하는 onPlaybackStateChanged 함수를 오버라이드 한다.
private val playbackStateListener: Player.Listener = playbackStateListener()
private fun playbackStateListener() = object : Player.Listener {
    override fun onPlaybackStateChanged(playbackState: Int) {
        val stateString: String = when (playbackState) {
            ExoPlayer.STATE_IDLE -> "ExoPlayer.STATE_IDLE      -"
            ExoPlayer.STATE_BUFFERING -> "ExoPlayer.STATE_BUFFERING -"
            ExoPlayer.STATE_READY -> "ExoPlayer.STATE_READY     -"
            ExoPlayer.STATE_ENDED -> "ExoPlayer.STATE_ENDED     -"
            else -> "UNKNOWN_STATE             -"
        }
        Log.d(TAG, "changed state to $stateString")
    }
}
상태는 총 4가지로 각각
1. STATE_IDLE
: 플레이어가 인스턴스화되었지만 아직 준비되지는 않았습니다.
2. STATE_BUFFERING 
: 버퍼링된 데이터가 충분하지 않아 플레이어가 현재 위치에서 재생할 수 없습니다.
3. STATE_READY 
: 플레이어가 현재 위치에서 즉시 재생할 수 없습니다. 즉, 플레이어의 playWhenReady 속성이 true이면 플레이어가 미디어 재생을 시작합니다. 이 속성이 false이면 플레이어가 일시중지됩니다.
4. STATE_ENDED
: 플레이어가 미디어 재생을 완료했습니다.
💡 조건 (playWhenReady = true && STATE_READY) 을 만족 할 때 재생 중이라고 할 수 있다.
💡 현재 재생중인지 또한 리스너를 받을 수 있다.
override fun onIsPlayingChanged(isPlaying: Boolean) { super.onIsPlayingChanged(isPlaying) Log.d(TAG, "onIsPlayingChanged $isPlaying") }
리스너를 초기화 함수에서 등록해준다.
private void initializePlayer() {
   [...]
   exoPlayer.seekTo(currentWindow, playbackPosition)
   exoPlayer.addListener(playbackStateListener)
   [...]
}
플레이어를 해제하는 것과 마찬가지로 리스너 또한 메모리릭의 문제를 발생할 수 있으므로 같이 해제해준다.
private fun releasePlayer() {
        player?.let { exoPlayer ->
            playbackPosition = exoPlayer.currentPosition
            currentItem = exoPlayer.currentMediaItemIndex
            playWhenReady = exoPlayer.playWhenReady
            exoPlayer.removeListener(playbackStateListener)
            exoPlayer.release()
        }
        player = null
    }