AndroidでBGMを再生するにはMediaPlayerクラスを使います。
(よく似たクラスにSoundPoolがありますが、こちらは効果音など短めの曲を流すのに使います)
Serviceを使う
アプリがアクティブでない時*1も再生し続けたい場合は、Serviceを使います。
実装が必要なメソッドはonStartCommandとonPreparedの2つです。
(onBindもoverrideを求められますが、バインドサービスとして使用するのでなければ中身は空でも問題ありません)
class MyService: Service(), MediaPlayer.OnPreparedListener {
private var mMediaPlayer: MediaPlayer? = null
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
}
override fun onPrepared(mp: MediaPlayer?) {
}
}
onStartCommandにMediaPlayerの初期化処理を書いていきます。
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
when(intent.action) {
ACTION_PLAY -> {
}
}
}
ここではまずIntentからアクションを取り出しています。
ここで分岐させることによって、再生・停止などの処理を分岐させることができます。
ACTION_PLAYは定数として
private const val ACTION_PLAY: String = "com.example.musiccplayerblogsample.PLAY"
のように定義しておきます。
この定数はServiceを起動する側でも同じ定数を指定します。
val intent = Intent(this, MyService::class.java)
intent.action = ACTION_PLAY
startService(intent)
再生準備
ここまでで、Serviceの起動と呼び出しイベントの分岐が出来ました。
次に再生ボタンが押された時のロジックを実装していきます。
ACTION_PLAY -> {
mMediaPlayer = MediaPlayer()
mMediaPlayer?.apply {
setDataSource(this@MyService, Uri.parse("android.resource://" + packageName + "/" + R.raw.sample_bgm))
setOnPreparedListener(this@MyService)
prepareAsync()
}
}
setDataSourceで再生する曲のリソースを指定しています。
ここではrawフォルダ内の曲をStringで指定し、Uriにパースしました。
なぜMediaPlayer.create()ではなくsetDataSource()で指定しているかというと、
create()が自動的にprepare()まで行ってしまうからです。
prepare()は呼び出し元と同一スレッドで実行されるため、処理に時間がかかった場合UIのタスクをブロックしてしまう可能性があります。
再生
非同期によるMediaPlayerの準備処理が終わると、setOnPreparedListenerで指定したリスナーのonPrepared(mp: MediaPlayer?)メソッドが呼び出されます。
override fun onPrepared(mp: MediaPlayer?) {
mMediaPlayer?.start()
}
このメソッド内でstart()を呼び出すことによって再生が開始されます。
バックグラウンドで再生する方法は以上です。
リソースの開放
MediaPlayerはシステムリソースを大量に消費するので、適切にリリースすることが推奨されています。
そのため、以下もServiceクラスに忘れずに実装しましょう。
override fun onDestroy() {
super.onDestroy()
mediaPlayer?.release()
}
まとめ
最後に今回作成したServiceクラスをまとめておきます。
package com.example.musicplayerblogsample
import android.app.Service
import android.content.Intent
import android.media.MediaPlayer
import android.net.Uri
import android.os.IBinder
private const val ACTION_PLAY: String = "com.example.musiccplayerblogsample.PLAY"
class MyService: Service(), MediaPlayer.OnPreparedListener {
private var mMediaPlayer: MediaPlayer? = null
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
when(intent.action) {
ACTION_PLAY -> {
mMediaPlayer = MediaPlayer()
mMediaPlayer?.apply {
setDataSource(this@MyService, Uri.parse("android.resource://" + packageName + "/" + R.raw.sample_bgm))
setOnPreparedListener(this@MyService)
prepareAsync()
}
}
}
return START_NOT_STICKY
}
override fun onPrepared(mp: MediaPlayer?) {
mMediaPlayer?.start()
}
override fun onBind(intent: Intent?): IBinder? {
TODO("Not yet implemented")
}
}