0Pricing
Android Academy · レッスン

音声と動画の再生

Androidでシンプルなメディア再生を実装します。

「音声と動画の再生」はCoddyKit上の無料Android Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAndroid Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Android Academyコースには全4レッスンが含まれています。

画像の先へ:メディア再生

Coilは画像を美しく表示できますが、アプリでは通知音、ポッドキャスト、バックグラウンドの動画、動画フィードなど、音声や動画も再生します。Androidにはメディア専用のAPIがあります。

現在推奨されているライブラリはMedia3 (ExoPlayer)です。古いMediaPlayerよりも、ストリーミング、形式、バッファリング、再生コントロールをはるかに適切に処理できます。このレッスンでは、Composeアプリで簡単な音声と動画を再生します。

Media3の依存関係を追加する

Media3は複数のモジュールに分かれています。ほとんどのアプリでは、プレーヤーエンジンのmedia3-exoplayerと、すぐに使えるプレーヤービューのmedia3-uiを使用します。Composeで動画を表示する場合は、AndroidView経由でPlayerViewを使うこともできます。

// build.gradle.kts (module level)
dependencies {
    implementation("androidx.media3:media3-exoplayer:1.5.0")
    implementation("androidx.media3:media3-ui:1.5.0")
}

ExoPlayerを作成する

ExoPlayerはContextから構築します。URLまたはローカルファイルを指定したMediaItemを渡し、prepare()を呼び出して再生を開始します。独自のスレッドで動作するため、UIをブロックしません。

重要なのは、プレーヤーがシステムリソースを保持するため、解放が必要なことです。詳しくは後ほど説明します。

import android.content.Context
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer

fun createPlayer(context: Context, url: String): ExoPlayer {
    val player = ExoPlayer.Builder(context).build()
    player.setMediaItem(MediaItem.fromUri(url))
    player.prepare()
    player.playWhenReady = true
    return player
}

音声を再生する

音声のみを再生する場合も設定は同じで、MediaItemに音声URL(mp3、aacなど)を指定するだけです。表示するビューはないため、プレーヤーオブジェクトを通じてplay()、pause()、seekTo()を呼び出して制御します。

これだけでポッドキャストや音楽プレーヤーを構築できます。

import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer

fun playPodcast(player: ExoPlayer, audioUrl: String) {
    player.setMediaItem(MediaItem.fromUri(audioUrl))
    player.prepare()
    player.play()
}

fun togglePause(player: ExoPlayer) {
    if (player.isPlaying) player.pause() else player.play()
}

Composeでプレーヤーを記憶する

Composeでは、プレーヤーを一度だけ作成し、再コンポジションをまたいで保持する必要があります。rememberを使うと、再コンポジションのたびに新しいプレーヤーが作成されるのを防げます。

リソースのリークを防ぐため、適切なクリーンアップ(次のシーン)と組み合わせてください。

import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer

@Composable
fun rememberPlayer(url: String): ExoPlayer {
    val context = LocalContext.current
    return remember {
        ExoPlayer.Builder(context).build().apply {
            setMediaItem(MediaItem.fromUri(url))
            prepare()
        }
    }
}

プレーヤーを解放する

プレーヤーはコーデック、バッファ、オーディオフォーカスを保持します。解放し忘れるとメモリリークが発生し、画面がなくなった後も再生が続くことがあります。Composeでは、コンポーザブルがコンポジションを離れるときにDisposableEffectがクリーンアップを実行します。

そこで必ずplayer.release()を呼び出してください。

import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.media3.exoplayer.ExoPlayer

@Composable
fun ManagedPlayer(player: ExoPlayer) {
    DisposableEffect(player) {
        onDispose {
            player.release()
        }
    }
    // ... use player here
}

PlayerViewで動画を表示する

Media3には、コントロール付きのすぐに使えるPlayerViewが用意されています。Composeにはネイティブの動画用コンポーザブルがないため、AndroidViewを使ってViewを埋め込み、プレーヤーを接続します。

これだけで、再生と一時停止、シークバー、全画面表示を利用できます。

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView

@Composable
fun VideoPlayer(player: ExoPlayer, modifier: Modifier = Modifier) {
    AndroidView(
        factory = { context ->
            PlayerView(context).apply {
                this.player = player
            }
        },
        modifier = modifier
    )
}

動画画面を完成させる

ここまでの内容をまとめましょう。プレーヤーを作成して記憶し、PlayerViewに接続し、破棄時に解放します。これは再利用できる自己完結型の動画画面です。

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView

@Composable
fun VideoScreen(videoUrl: String) {
    val context = LocalContext.current
    val player = remember {
        ExoPlayer.Builder(context).build().apply {
            setMediaItem(MediaItem.fromUri(videoUrl))
            prepare()
            playWhenReady = true
        }
    }
    DisposableEffect(Unit) { onDispose { player.release() } }

    AndroidView(
        factory = { PlayerView(it).apply { this.player = player } },
        modifier = Modifier.fillMaxWidth().height(220.dp)
    )
}

再生状態を追跡する

UI(再生・一時停止アイコンや進行状況バーなど)を更新するには、Player.Listenerを使ってプレーヤーを監視します。バッファリング中、準備完了、再生終了などの状態変化に加え、再生がアクティブかどうかも通知されます。

画面が表示されたときにリスナーを追加し、破棄時に削除してください。

import androidx.media3.common.Player
import androidx.media3.exoplayer.ExoPlayer

fun observe(player: ExoPlayer, onPlaying: (Boolean) -> Unit): Player.Listener {
    val listener = object : Player.Listener {
        override fun onIsPlayingChanged(isPlaying: Boolean) {
            onPlaying(isPlaying)
        }
        override fun onPlaybackStateChanged(state: Int) {
            when (state) {
                Player.STATE_BUFFERING -> { /* show spinner */ }
                Player.STATE_READY -> { /* ready */ }
                Player.STATE_ENDED -> { /* finished */ }
            }
        }
    }
    player.addListener(listener)
    return listener
}

アプリがバックグラウンドに移行したら一時停止する

インライン動画(音楽アプリではない場合)では通常、ユーザーが画面を離れたら一時停止し、戻ってきたら再開したいものです。Lifecycleを監視し、ON_PAUSEで一時停止することで、再生をComposeのライフサイクルに連動させられます。

これにより、ユーザーに見えない動画の再生を防ぎ、バッテリーを節約できます。

import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.media3.exoplayer.ExoPlayer

@Composable
fun PauseWithLifecycle(player: ExoPlayer) {
    val lifecycleOwner = LocalLifecycleOwner.current
    DisposableEffect(lifecycleOwner) {
        val observer = LifecycleEventObserver { _, event ->
            when (event) {
                Lifecycle.Event.ON_PAUSE -> player.pause()
                Lifecycle.Event.ON_RESUME -> player.play()
                else -> {}
            }
        }
        lifecycleOwner.lifecycle.addObserver(observer)
        onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
    }
}

MediaSessionでバックグラウンド音声を再生する

アプリがバックグラウンドに移行しても音声(音楽やポッドキャスト)の再生を続けたい場合は、再生をMediaSessionServiceでラップします。これにより、システムコントロール、ロック画面、Bluetoothボタンと連携できます。

より複雑な構成になりますが、基本的な考え方はMediaSessionを通じてプレーヤーを公開することです。

import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.session.MediaSession
import androidx.media3.session.MediaSessionService

class PlaybackService : MediaSessionService() {
    private var session: MediaSession? = null

    override fun onCreate() {
        super.onCreate()
        val player = ExoPlayer.Builder(this).build()
        session = MediaSession.Builder(this, player).build()
    }

    override fun onGetSession(controllerInfo: MediaSession.ControllerInfo) = session

    override fun onDestroy() {
        session?.run { player.release(); release() }
        session = null
        super.onDestroy()
    }
}

クイックチェック

ExoPlayerを保持するCompose画面で、画面がコンポジションを離れるときにプレーヤーを解放するには、どのCompose APIを使うべきですか?

まとめ

音声と動画の再生機能を追加しました。

  • MediaPlayerの現代的な代替となるMedia3 ExoPlayerを使います。
  • プレーヤーを構築し、MediaItemを設定して、prepare()を呼び出し、その後play()を呼び出します。
  • Composeではプレーヤーをrememberし、DisposableEffectのonDisposeで解放します。
  • AndroidViewでPlayerViewを埋め込んで動画を表示します。
  • Player.Listenerで状態を追跡し、バックグラウンド音声にはMediaSessionServiceを使います。

これで、Coilを使った画像とメディアの読み込みコースは完了です。画像を滑らかに読み込み、最新のAndroidの方法でメディアを再生できるようになりました。

よくある質問

「音声と動画の再生」レッスンは無料ですか?

はい。「音声と動画の再生」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Android Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Android Academyコースには全4レッスンが含まれています。

「音声と動画の再生」で何を学びますか?

Androidでシンプルなメディア再生を実装します。 ブラウザで直接実行するハンズオンコードでAndroid Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Android Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのAndroid Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「音声と動画の再生」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このAndroid Academyレッスンでコードを書いて実行できますか?

はい。すべてのAndroid Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Coilで画像を読み込む
  2. プレースホルダーとエラー状態
  3. キャッシュとパフォーマンス
  4. 音声と動画の再生
← Android Academyに戻る