再生イベントをリッスンする
状態の変化や再生エラーなどのイベントは、登録済みの Player.Listener
インスタンスに報告されます。このようなイベントを受信するリスナーを登録するには:
Kotlin
// Add a listener to receive events from the player. player.addListener(listener)
Java
// Add a listener to receive events from the player. player.addListener(listener);
Player.Listener
には空のデフォルト メソッドがあるため、必要なメソッドのみを実装する必要があります。メソッドとそれが呼び出されるタイミングの詳細については、Javadoc をご覧ください。最も重要なメソッドについて、以下で詳しく説明します。
リスナーは、個別のイベント コールバックを実装するか、1 つ以上のイベントが同時に発生した後に呼び出される汎用の onEvents
コールバックを実装するかを選択できます。さまざまなユースケースに適したものについては、Individual callbacks vs onEvents
をご覧ください。
再生状態の変化
プレーヤーの状態の変更を受信するには、登録済みの Player.Listener
に onPlaybackStateChanged(@State int state)
を実装します。プレーヤーは、次の 4 つの再生状態のいずれかになります。
Player.STATE_IDLE
: 初期状態、プレーヤーが停止した状態、再生に失敗したときの状態です。この状態では、プレーヤーは限られたリソースのみを保持します。Player.STATE_BUFFERING
: プレーヤーは現在の位置からすぐに再生できません。これは主に、より多くのデータを読み込む必要があることが原因で発生します。Player.STATE_READY
: プレーヤーは現在の位置からすぐに再生できる。Player.STATE_ENDED
: プレーヤーがすべてのメディアの再生を終了しました。
これらの状態に加えて、プレーヤーにはユーザーがプレイする意思を示す playWhenReady
フラグが表示されます。このフラグの変更を受信するには、onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason)
を実装します。
次の 3 つの条件がすべて満たされる場合、プレーヤーは再生中(位置が進んでおり、メディアがユーザーに表示されている状態)です。
- プレーヤーが
Player.STATE_READY
状態である playWhenReady
の場合はtrue
Player.getPlaybackSuppressionReason
から返された理由で再生が抑制されていない
これらのプロパティを個別に確認しなくても、Player.isPlaying
を呼び出すことができます。この状態の変更を受信するには、onIsPlayingChanged(boolean isPlaying)
を実装します。
Kotlin
player.addListener( object : Player.Listener { override fun onIsPlayingChanged(isPlaying: Boolean) { if (isPlaying) { // Active playback. } else { // Not playing because playback is paused, ended, suppressed, or the player // is buffering, stopped or failed. Check player.playWhenReady, // player.playbackState, player.playbackSuppressionReason and // player.playerError for details. } } } )
Java
player.addListener( new Player.Listener() { @Override public void onIsPlayingChanged(boolean isPlaying) { if (isPlaying) { // Active playback. } else { // Not playing because playback is paused, ended, suppressed, or the player // is buffering, stopped or failed. Check player.getPlayWhenReady, // player.getPlaybackState, player.getPlaybackSuppressionReason and // player.getPlaybackError for details. } } });
再生エラー
再生が失敗する原因となるエラーは、登録済みの Player.Listener
に onPlayerError(PlaybackException error)
を実装することで受け取ることができます。エラーが発生すると、再生状態が Player.STATE_IDLE
に遷移する直前にこのメソッドが呼び出されます。失敗または停止した再生を再試行するには、ExoPlayer.prepare
を呼び出します。
なお、一部の Player
実装では、PlaybackException
のサブクラスのインスタンスを渡すことで、失敗に関する追加情報が提供されます。たとえば、ExoPlayer
は ExoPlaybackException
を渡します。これには、type
、rendererIndex
、その他の ExoPlayer 固有のフィールドが含まれます。
次の例は、HTTP ネットワークの問題が原因で再生が失敗したことを検出する方法を示しています。
Kotlin
player.addListener( object : Player.Listener { override fun onPlayerError(error: PlaybackException) { val cause = error.cause if (cause is HttpDataSourceException) { // An HTTP error occurred. val httpError = cause // It's possible to find out more about the error both by casting and by querying // the cause. if (httpError is InvalidResponseCodeException) { // Cast to InvalidResponseCodeException and retrieve the response code, message // and headers. } else { // Try calling httpError.getCause() to retrieve the underlying cause, although // note that it may be null. } } } } )
Java
player.addListener( new Player.Listener() { @Override public void onPlayerError(PlaybackException error) { @Nullable Throwable cause = error.getCause(); if (cause instanceof HttpDataSourceException) { // An HTTP error occurred. HttpDataSourceException httpError = (HttpDataSourceException) cause; // It's possible to find out more about the error both by casting and by querying // the cause. if (httpError instanceof HttpDataSource.InvalidResponseCodeException) { // Cast to InvalidResponseCodeException and retrieve the response code, message // and headers. } else { // Try calling httpError.getCause() to retrieve the underlying cause, although // note that it may be null. } } } });
再生リストの切り替え
プレーヤーが再生リスト内の新しいメディア アイテムに変更するたびに、登録済みの Player.Listener
オブジェクトで onMediaItemTransition(MediaItem mediaItem,
@MediaItemTransitionReason int reason)
が呼び出されます。理由は、自動移行、player.next()
呼び出し後のシーク、同じアイテムの繰り返し、プレイリストの変更(現在再生中のアイテムが削除された場合など)のいずれであるかを示します。
メタデータ
player.getCurrentMediaMetadata()
から返されるメタデータは、再生リストの移行、インストリーム メタデータの更新、再生中の現在の MediaItem
の更新など、さまざまな理由で変更される可能性があります。
現在のタイトルを表示する UI を更新する場合など、メタデータの変更が必要な場合は、onMediaMetadataChanged
をリッスンできます。
シークしています
Player.seekTo
メソッドを呼び出すと、登録済みの Player.Listener
インスタンスに対する一連のコールバックが発生します。
onPositionDiscontinuity
はreason=DISCONTINUITY_REASON_SEEK
に置き換えます。これは、Player.seekTo
を呼び出した直接的な結果です。コールバックには、シークの前後の位置を示すPositionInfo
フィールドがあります。onPlaybackStateChanged
: シークに関連する即時の状態変更に置き換えます。そのような変化はない可能性があります。
個別のコールバックと onEvents
リスナーは、onIsPlayingChanged(boolean isPlaying)
などの個別のコールバックを実装するか、汎用の onEvents(Player player, Events events)
コールバックを実装するかを選択できます。汎用コールバックは、Player
オブジェクトへのアクセスを提供し、同時に発生した events
のセットを指定します。このコールバックは常に、個々のイベントに対応するコールバックの後に呼び出されます。
Kotlin
override fun onEvents(player: Player, events: Player.Events) { if ( events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED) || events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED) ) { uiModule.updateUi(player) } }
Java
@Override public void onEvents(Player player, Events events) { if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED) || events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)) { uiModule.updateUi(player); } }
個別のイベントは、次のような場合に推奨されます。
- リスナーが関心を持っているのは変更の理由です。たとえば、
onPlayWhenReadyChanged
やonMediaItemTransition
に指定された理由などです。 - リスナーは、コールバック パラメータを通じて提供された新しい値に対してのみ動作するか、コールバック パラメータに依存しない他の値をトリガーします。
- リスナーの実装では、イベントをトリガーした要因がメソッド名でわかりやすく判読可能になるようにすることをおすすめします。
- リスナーは、すべての個々のイベントと状態変化について把握する必要がある分析システムに報告します。
次の場合は、汎用の onEvents(Player player, Events events)
を使用することをおすすめします。
- リスナーが複数のイベントに対して同じロジックをトリガーしたいと考えています。たとえば、
onPlaybackStateChanged
とonPlayWhenReadyChanged
の両方の UI を更新する場合です。 - リスナーは
Player
オブジェクトにアクセスして、さらにイベント(メディア アイテムの遷移後のシークなど)をトリガーする必要があります。 - リスナーは、複数の状態値を個別のコールバックで、または
Player
ゲッター メソッドと組み合わせてレポートします。たとえば、onTimelineChanged
で提供されるTimeline
とPlayer.getCurrentWindowIndex()
を併用することは、onEvents
コールバック内でのみ安全です。 - リスナーは、イベントが論理的に同時に発生したかどうかに関心があります。たとえば、メディア アイテムの遷移により
onPlaybackStateChanged
からSTATE_BUFFERING
が選択されます。
場合によっては、リスナーが個々のコールバックを汎用の onEvents
コールバックと組み合わせることが必要になる場合があります(たとえば、onMediaItemTransition
でメディア アイテムの変更理由を記録する場合など)。ただし、すべての状態変更を onEvents
で併用できる場合にのみ動作します。
AnalyticsListener
の使用
ExoPlayer
を使用する場合は、addAnalyticsListener
を呼び出して AnalyticsListener
をプレーヤーに登録できます。AnalyticsListener
の実装では、分析やロギングに役立つ詳細なイベントをリッスンできます。詳細については、分析ページをご覧ください。
EventLogger
の使用
EventLogger
は、ロギングのためにライブラリから直接提供される AnalyticsListener
です。EventLogger
を ExoPlayer
に追加すると、1 行で便利な追加のロギングが有効になります。
Kotlin
player.addAnalyticsListener(EventLogger())
Java
player.addAnalyticsListener(new EventLogger());
詳細については、デバッグ ロギングのページをご覧ください。
指定した再生位置でイベントを呼び出す
ユースケースによっては、指定した再生位置でイベントを発生させる必要があります。これは、PlayerMessage
を使用してサポートされます。PlayerMessage
は、ExoPlayer.createMessage
を使用して作成できます。実行する再生位置は、PlayerMessage.setPosition
を使用して設定できます。デフォルトでは、メッセージは再生スレッドで実行されますが、PlayerMessage.setLooper
を使用してカスタマイズできます。PlayerMessage.setDeleteAfterDelivery
を使用すると、指定した再生位置に到達するたびにメッセージを実行するか(シークモードと繰り返しモードにより複数回発生する場合もあります)か、初回のみを実行するかを制御できます。PlayerMessage
を構成したら、PlayerMessage.send
を使用してスケジュール設定できます。
Kotlin
player .createMessage { messageType: Int, payload: Any? -> } .setLooper(Looper.getMainLooper()) .setPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 120000) .setPayload(customPayloadData) .setDeleteAfterDelivery(false) .send()
Java
player .createMessage( (messageType, payload) -> { // Do something at the specified playback position. }) .setLooper(Looper.getMainLooper()) .setPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 120_000) .setPayload(customPayloadData) .setDeleteAfterDelivery(false) .send();