【はじめてのKotlinプログラミング(23)】BGMの再生と、ライフサイクル

kotlinで、アプリにBGMを流す方法について解説します。(Android Studio 2020.3.1 for Windows)

今回の主な学習のテーマは2つあって。

●1つは音楽を再生する「MediaPlayer」
●実はそれと同じくらい重要なのが「ライフサイクル」っていう概念です。

動画

コード

Activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:textSize="34sp"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.kt

package com.example.simplemusicapp

import android.graphics.Color
import android.media.MediaPlayer
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView

class MainActivity : AppCompatActivity() {
    //5)mpを横断的に使えるようにここに書く
    private lateinit var mp:MediaPlayer

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //3)MediaPlayerをひとまずここに用意
        //val mp:MediaPlayer

        //1)viewを取得⇒ボタンを押したら文字を赤色
        val tv:TextView=findViewById(R.id.tv)
        val btn:Button=findViewById(R.id.btn)
        btn.setOnClickListener {
            tv.setTextColor(Color.RED)
        }
        //2)音楽ファイルをres/rawディレクトリに入れる
        //4)MediaPlayerを再生(起動と同時)
        mp = MediaPlayer.create(this,R.raw.samplebgm)
        mp.isLooping = true
        mp.start()
    }

    //6)再開
    override fun onResume() {
        super.onResume()
        mp.start()
    }

    //5)一時停止
    override fun onPause() {
        super.onPause()
        mp.pause()
    }

    //7)終了・メモリの解放
    override fun onDestroy() {
        super.onDestroy()
        mp.stop() //終了・停止
        mp.release() //解放
    }


}

ライフサイクルとは?

アプリでは、画面の状態によって、通るメソッドが違います。

●起動するとき・・・onCreate()内に書く
●再開するとき・・・onResume()内に書く
●一時停止するとき・・・onPause()内に書く
●終了するとき・・・onDestroy()内に書く

このような仕組みになっています。

例えば、

「画面が非アクティブになったら音楽を止めたい」というようなときはonPause()内に、音楽を止めるコードを書きます。

また

「画面が再度アクティブになったら、音楽を再生させたい」というような時はonResume()内に、音楽を再生させるコードを書きます。

こうやって、画面の状態の変化に応じて、使う(通る)メソッドが変わるのです。

もちろん、

終了するときに、何か処理を実行させたい場合もあります。そういう時はonDestroy()内に、処理させたい命令を記述します。

テキスト

xmlは割愛

それではプログラミングをやっていきましょう。
ktを開いてください。

で、言うまでもないですがktのところには、
いつものようにonCreateがあらかじめ用意されています。

今まで何気につかってたと思うんですけど
これはアプリを起動するときには必ず実行しなければいけないやつなので
事前に用意されてる、というわけですね。

で今回は、このonCreate{}下に
一時停止のonPauseとか、
再開させるonResumeとかを記述して
音楽が止まる命令とか、音が再度開始される命令とかを記述して、実際に動作を確認していきましょう。

1.ボタンクリックで文字色を変えよう

で、本題に行く前に、もう1仕事だけ。
初期画面か途中画面かの違いがわかるように、
今のこれが初期状態の画面だとして
ボタンを押したら、文字の色が赤に変わって、
作業途中の画面だと区別できるようにしておきましょう。

onCreate波カッコ閉じるの前に書いていきます。

//1)viewを取得⇒ボタンを押したら文字を赤色

まずはviewを取得しておきましょう。

        val tv:TextView=findViewById(R.id.tv)
        val btn:Button=findViewById(R.id.btn)

で、ボタンをおしたら、ということなので

btn.setOnClickListener {
}

テキストビューの色を変えたい場合は
tv.setTextColor() としてあげて。

色の指定方法は色々あるんですが、ここにColorと続けてあげると
主要な色をandroid先生が用意してくれていますので
てっとりばやくREDとかにしておきましょう。

        //1)viewを取得⇒ボタンを押したら文字を赤色
        val tv:TextView=findViewById(R.id.tv)
        val btn:Button=findViewById(R.id.btn)
        btn.setOnClickListener {
            tv.setTextColor(Color.RED)
        }

これで、ボタンをおしたら、テキストの色が赤になったと思うので
エミュレータで確認してみましょう。

2.音楽ファイルをrawフォルダ(ディレクトリ)に入れる

では音楽を再生していきましょう。ここから先は、何かしら音楽ファイルを用意して進めてください。

で、これからやる作業、
つまり音楽ファイルの再生は主に3ステップです。

①まず、音楽ファイルをres(リソース)の中にrawフォルダを作ってその中に入れる
②MediaPlayerを用意(インポート)
③MediaPlayerを再生

rawっていうのは「生の」って言う意味なんですけど
音楽のデータをベタに、ただ入れておく時に使用します。

このrawフォルダは、実はプロジェクトを作成した段階では用意されていません。
なので、使いたい時に、使いたい人がその都度作ってあげる必要があるので
これだけ覚えておく必要があります。

てことで、なんとなく流れがつかめたところで作業していきましょう。

まずは、用意した音楽ファイルをres(resourse/リソース)のrawの中に入れます

//2)音楽ファイルをres/rawディレクトリに入れる

フォルダのことを、プログラミングの世界ではちょっとかっこつけて
[ディレクトリ]と言ったりしますので覚えておいてください。
意味は同じです。

てことで左サイドのres、リソースを展開してみましょう。
画像を扱うdrawableとか、layoutとか、よく使うやつはあらかじめ用意されているんですけど
音楽を入れるrawフォルダは用意されていません。

てことでリソース、resを右クリックして new⇒ディレクトリ(これはフォルダのことですね)
raw と入力してエンター。

そうするとリソースの中にrawフォルダができました。
あとはこの中に、先ほどの音楽ファイルを入れたら準備完了です。

このままドラッグアンドドロップしてもいいですし、
コントロールを押しながらドラッグすると、このようにコピーになりますので
お好きな方をご利用ください。

はいこれで入りました。

あとは、MediaPlayerをインポートして、再生してみましょう。

3-4.MediaPlayerをインポートして再生

それではMediaPlayerを使っていきましょう。
setContentViewの下に、ひとまず用意します。

//3)MediaPlayerをひとまずここに用意

ひとまずっていうのはですね、今はこれonCreateの中ですよね。あとでonResumeとかonPauseとか横断的に使えるようにこれの上に書きなおす予定ですが、ひとまず今は、従来通りonCreateの中に書いていきます。

で、まずは名前を付けてあげる必要があるので
val で宣言して、MediaPlayer 略してmp とかにしてあげましょう。

で、これにつづけてMediaPlayerと書いていると、ペロっと出てくるので選択。
そうするとインポートされて、文字通り入ってきたので、使えるようになります。

てことでmpを使って、MediaPlayerを再生してみましょう。

2の下に続けていきます。

****************************************
//4)MediaPlayerを再生(起動と同時)

まずは先ほどのファイルを指定してあげましょう。
mp = MediaPlayer.create(this,R.raw.samplebgm)
次にループもしておきましょう。
mp.isLooping =true

最後に、開始するということで
mp.start()
****************************************

以上です。ここももう決まり文句なんで一言一句覚えるというより、
書いてある意味さえ理解すれば、あとは使いたい時にMediaPlayerとかで検索するなり
この動画を見返してもらうなりでそのまま書けばOKです。
こういうのを一言一句覚えようとすると挫折の元ですからね。
肝心の用語だけ覚えて、細かい使い方は必要な時に検索して調べる、というようにしましょう。

         //4)MediaPlayerを再生(起動と同時)
        mp = MediaPlayer.create(this,R.raw.game_bgm)
        mp.isLooping =true
        mp.start()

てことで、ひとまずこれで、エミュレータを起動して、音楽が再生されるかどうか確認してみましょう。

と、いきたいところなんですが、実は今の状態だと微妙にエラーになります。音楽が途中で止まったり、流れなかったり。これは

val mp:MediaPlayer

をonCreate内に書いているので、その1つ上の階層に書いてあげれば解決します。

てことで、次にonPause()を書きながら、もろもろ解決していきましょう

5.lateinitと、一旦停止(onPause)

それでは今、若干不完全燃焼ぎみではあるんですが、画面を非アクティブ、つまり一時停止したときに
音楽が止まるようにしてあげましょう。

onCreateの波カッコ閉じるの外に
onPauseを書いていきます

//5)一時停止

ここにonPa と入れたあたりでonPauseが出てくるので選択。そうすると自動で色々定型文が入ります。

あとはこの中に
mp.pause()
と書いてあげるだけなのですが、
mpが赤くなって、アクセスできませんって言われています。

これは先ほど、mpをonCreate内で宣言しているから、onCreate波カッコの外では使えないってことです。
てことで、横断的に使いたい場合は、onCreateの1つ上の階層に書いてあげればOKです。

てことで3の方はいったんコメントにして

//val mp:MediaPlayer

その上に書いてあげます

//5)mpを横断的に使えるようにここに書く
val mp:MediaPlayer

で、これでOKかと思いきや、急に色々言われています。

まず、簡単な▲マークの方からいきましょうか。
これは以前四択クイズかなんかでもやったと思うんですけど
このクラスでしか使わないんだったら privateと書きなさいよってことなので
アクセス修飾子privateと書いてあげます。

これでprivateうんぬんのエラーは消えました。

問題は1行目の「プロパティ」~「イニシャライズド」~なんちゃらかんちゃらですね。
property must be initialized or be abstract

なんやよーわかりませんが、イニシャライズ、つまり
初期化しなさいよ的なことをおっしゃっています。

で、これももう、決まり文句みたいなもんなんですが
lateinit って書けば解決しますので
privateに続けてlateinitと書いてあげてください。

そうするとlateinitには大事なルールがあって、valではなくてvarにしなさいよって書いてありますので
これをvarにしてあげましょう。
これでエラーは消えました。

ということで、多少右往左往しましたが
onCreateの上に宣言したのでmpは
onCreateでも使えますし、onPause()でも横断的に使えるようになりました。

lateinitってのはですね、
lateと付いてるだけあって「遅らせる」ってことで、
直訳すると「初期化を遅らせる」、という意味になるんですが
onCreate() を呼び出しまで遅らせまっせということです。

もっとざっくり要約すると、ええ感じにタイミングを見計らってくれる呪文、くらいに思っておいてください。
こういうのを変に深堀りすると挫折のもとなんでね、
よーわからんけどイニシャライズしなさいって言われたのでこれを書きました、くらいでOKです。

それではこれでエミュレータを起動してみましょう。

これで、先ほど不具合だったonCreate、そして今回記述したonPauseもちゃんと一時停止しているのがわかります。

6.onResume

それでは一時停止したものを再開させたい時には、onResumeの中に命令を書いていきましょう。

//6)再開

onReと入れるとonResume()が出てくると思いますので選択。

中身はスタートさせてあげればいいので、onCreate同様
mpをスタート

mp.start()

これでOKです。
それではエミュレータを起動して、一時停止したあと再開されるか確認してみましょう。

7.終了 onDestroy

音楽はこれで無事流れるのが確認できたので、ひとまずもろもろOKです。
ただ最後に、アプリを終了する際に何かしら処理をさせたい場合は
onDestroyを書いて、その中に、書いていきます。

今回でいうとアプリを終了したら当然音楽のストップさせたいわけですが
メモリも解放してあげないと、メモリが蓄積されて圧迫されるので
その処理も書いてあげます。

onDestroyってのを書いて、終了します。

onDe って書いたあたりで出てくるかと思いますので選択。

あとはストップして
メモリを解放すれば完了です。

    override fun onDestroy() {
        mp.stop() //終了・停止
        mp.release() //解放
        super.onDestroy()
    }

ストップはそのままなんで分かると思いますが。

release(解放)。これをしないとどんどんメモリが圧迫されて負荷がかかってしまうんですよね。動作が重くなったり、エミュレータの表示がおかしくなったり。

なので、終了と同時に解放してあげる、ということです。

てことでBGMを鳴らしながら、ライフサイクルの解説でした。

    コメントを残す