質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

Q&A

解決済

1回答

2985閲覧

soundPoolのサウンド停止がスリープ(onPause)状態を通るとできなくなる

kakashi55

総合スコア25

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

0グッド

0クリップ

投稿2021/09/12 01:18

編集2021/09/12 12:08

kotlinにて、android studioでアプリの開発を行っております。

カウントダウンタイマーを実装しており、タイマーが0になったとき、soundPoolにて音を再生しています。

〇アプリが開いた状態
タイマーが0になり、音が再生されたとき、停止ボタンを押すことで、音を止めることができます。

〇スリープ状態
カウントダウン中に端末がスリープ状態になり、タイマーが0になり、音が再生されたとき、
端末のスリープを解除し、停止ボタンを押しても音が停止しません。

おそらくsoundpoolの設定を onResume, onPause で最適化できていないからなのかと思っておりますが、
この辺が解決できずに大変困っております。

解決方法がお分かりになる方がおりましたらご教授いただけないでしょうか?
宜しくお願い致します。

コードはその他機能も実装して冗長なため一部切り出して貼りました。

kotlin

1package com.example.xxxxxxxxx 2 3import android.media.AudioAttributes 4import android.media.SoundPool 5import androidx.appcompat.app.AppCompatActivity 6import android.os.Bundle 7import android.os.Handler 8import android.util.Log 9import com.example.handler.databinding.ActivityMainBinding 10 11 12class MainActivity : AppCompatActivity() { 13 private lateinit var binding: ActivityMainBinding 14 private var pomoTimerStarted = false //最初は停止 15 private var soundRunning = false //音が鳴っているかどうか 16 17 val handler = Handler() //一度だけ代入 18 var pomodoroTimeTime = 300 //5min 19 20 21 //音鳴らす////////////////////////////////////////////////////////////////////////////// 22 private lateinit var soundPool: SoundPool 23 private var soundResId = 0 24 private var soundOne = 0 25 var streamId = 0 26 27 //count Down 28 val runnableDown = object : Runnable { 29 override fun run() { 30 pomodoroTimeTime-- 31 32 // ?.letを用いて、nullではない場合のみ更新 33 timeToText(pomodoroTimeTime)?.let{ 34 binding.showTextTime.text = it 35 } 36 handler.postDelayed(this, 1000) 37 38 39 if(pomodoroTimeTime == 0) { 40 soundPlay() 41 } 42 else if(pomodoroTimeTime < 0){ 43 pomodoroTimeTime = 0 44 } 45 else{ 46 } 47 } 48 } 49 50 //アラーム再生 51 val runnableSound = object : Runnable{ 52 override fun run(){ 53 streamId = soundPool.play(soundOne, 1.0f, 1.0f, 0, 0, 1.0f) 54 } 55 } 56 57 override fun onCreate(savedInstanceState: Bundle?) { 58 super.onCreate(savedInstanceState) 59 binding = ActivityMainBinding.inflate(layoutInflater) 60 setContentView(binding.root) 61 62 binding.playStop.setOnClickListener{ 63 if(pomoTimerStarted && !soundRunning) { //downが動作⇒停止 64 breakStopTimer() 65 } 66 67 else if(!pomoTimerStarted && !soundRunning) { //downが停止⇒動作 68 breakStartTimer() 69 } 70 71 else { //音が鳴っている 72 stopAlarm() 73 } 74 } 75 76 77 public fun breakStartTimer(){ 78 binding.playStop.setImageResource(R.drawable.stop) 79 handler.post(runnableDown) 80 81 pomoTimerStarted = true 82 soundRunning = false 83 } 84 85 public fun breakStopTimer(){ 86 binding.playStop.setImageResource(R.drawable.play) 87 handler.removeCallbacks(runnableDown) 88 89 pomoTimerStarted = false 90 soundRunning = false 91 } 92 93 public fun soundPlay(){ 94 Log.d("debug", "soundPlay") 95 96 pomoTimerStarted = false 97 soundRunning = true 98 99 handler.removeCallbacks(runnableDown) //カウントダウンタイマー停止 100 handler.post(runnableSound) //音を鳴らす 101 } 102 103 public fun stopAlarm(){ 104 soundPool.stop(streamId) //アラーム停止もやる 105 handler.removeCallbacks(runnableSound) 106 107 pomodoroTimeTime = 300 108 109 pomoTimerStarted = false 110 soundRunning = false 111 112 binding.playStop.setImageResource(R.drawable.play) 113 } 114 115 public fun soundStarted(){ 116 soundPool.play(soundOne, 1.0f, 1.0f, 0, 0, 1.0f) 117 } 118 119 120 override fun onResume() { 121 super.onResume() 122 Log.d("debug", "onResume") 123 124 //soundPool設定 125 val audioAttributes = AudioAttributes.Builder() 126 .setUsage(AudioAttributes.USAGE_GAME) 127 .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) 128 .build() 129 130 soundPool = SoundPool.Builder() 131 .setAudioAttributes(audioAttributes) 132 .setMaxStreams(2) 133 .build() 134 135 soundOne = soundPool.load(this, R.raw.bellsound, 1) 136 137 // load が終わったか確認する場合 138 soundPool.setOnLoadCompleteListener{ soundPool, sampleId, status -> 139 Log.d("debug", "sampleId=$sampleId") 140 Log.d("debug", "status=$status") 141 } 142 143 144 //サウンドをメモリにロード 145 soundPool = SoundPool.Builder().run { 146 val audioAttributes = AudioAttributes.Builder().run { 147 setUsage(AudioAttributes.USAGE_ALARM) 148 build() 149 } 150 setMaxStreams(1) 151 setAudioAttributes(audioAttributes) 152 build() 153 } 154 soundResId = soundPool.load(this, R.raw.lazer, 1) 155 } 156 157 override fun onPause() { //アクティビティ非表示 (バックグラウンドでの動作) 158 super.onPause() 159 Log.d("debug", "onPause") 160 161 if(pomodoroTimeTime < 0){ 162 soundPlay() 163 } 164 } 165 166}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jimbe

2021/09/12 06:37

「停止ボタン」を押されたらどのメソッドが呼ばれるのでしょうか。
jimbe

2021/09/12 07:01 編集

onResume の中に SoundPool の Build が 2 回あります。 1 回目は( 2 回目に上書きされて)無意味と思いますが、何か意味があるのでしょうか。 onCreate の中の一連の if 文も、アプリ起動時には不要に思いますが…。(アプリを起動したらいきなりカウントダウンが始まるのでしょうか。)
kakashi55

2021/09/12 12:07

ご返答いただきありがとうございます。 SoundPoolのBuildが2回あるのは誤記です。 (コメントで気が付きました。。) onCreate内のif文は、ボタン処理の1行を消してしまっていたため訂正しました。
jimbe

2021/09/12 15:05

一行だけ追加されると {} の対応も変になりますけれども。 切り出されるのは結構ですが、検証・再現するにしましてもご提示頂いたコードを修正しなければならないのでは、肝心の問題点が分からなくなってしまいます。 ご質問されている状況が再現できるよう、コードとレイアウトのセットで動作する状態でのご提示をお願い致します。
guest

回答1

0

ベストアンサー

kotlin は不慣れなもので、"らしくない"コードになっているかと思いますが、ご容赦ください。
以下のコード・レイアウトの他、リソースとして raw フォルダに alarm.mp3 を置いてテストしました。

ご質問の原因は、onResume で SoundPool を作り直してしまっているためかと思います。
裏に回った後再び表に表示された場合に onResume が実行され、鳴らしていたものと違う SoundPool が作られて soundPool 変数に上書きされてしまうため、 soundPool.stop(streamId) としても新しい soundPool には streamId が無くて停止できないのではないでしょうか。

以下のコードでは、SoundManager なるクラスを作って音関係を入れてしまい、アクティビティの onCreate で一度だけ SoundPool を作る様にしています。
ついでに、 RunnableDown も innner class 化しました。

MainActivity.kt

kotlin

1package com.teratail.q359037 2 3import android.content.Context 4import android.media.AudioAttributes 5import android.media.SoundPool 6import androidx.appcompat.app.AppCompatActivity 7import android.os.Bundle 8import android.os.Handler 9import android.os.Looper 10import android.view.View 11import com.teratail.q359037.databinding.ActivityMainBinding 12 13class SoundManager(context : Context) { 14 private var soundPool: SoundPool 15 private var soundOne = 0 16 private var streamId = 0 17 18 init { 19 //soundPool設定 20 val audioAttributes = AudioAttributes.Builder() 21 .setUsage(AudioAttributes.USAGE_GAME) 22 .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) 23 .build() 24 25 soundPool = SoundPool.Builder() 26 .setAudioAttributes(audioAttributes) 27 .setMaxStreams(1) 28 .build() 29 30 soundOne = soundPool.load(context, R.raw.alarm, 1) 31 } 32 33 fun playOne() { 34 cancelOne() 35 streamId = soundPool.play(soundOne, 1.0f, 1.0f, 0, -1, 1.0f) 36 } 37 38 fun cancelOne() { 39 if(streamId != 0) { 40 soundPool.stop(streamId) 41 streamId = 0; 42 } 43 } 44} 45 46class MainActivity : AppCompatActivity() { 47 private lateinit var binding: ActivityMainBinding 48 private val handler = Handler(Looper.getMainLooper()) //一度だけ代入 49 private lateinit var soundManager : SoundManager 50 private var runnableDown : RunnableDown? = null 51 52 inner class RunnableDown(private var time : Int = 300) : Runnable { 53 override fun run() { 54 showTime(time) 55 if(time == 0) { 56 soundManager.playOne() 57 } else { 58 time -- 59 handler.postDelayed(this, 1000) 60 } 61 } 62 fun start() : RunnableDown { 63 handler.post(this) 64 return this; 65 } 66 fun cancel() { //handler と同じイベントループ(スレッド)上で呼ぶこと 67 handler.removeCallbacks(this) 68 soundManager.cancelOne() 69 } 70 } 71 72 fun showTime(time : Int) { 73 binding.showTextTime.text = "%d:%02d".format(time/60, time%60) 74 } 75 76 override fun onCreate(savedInstanceState: Bundle?) { 77 super.onCreate(savedInstanceState) 78 binding = ActivityMainBinding.inflate(layoutInflater) 79 setContentView(binding.root) 80 soundManager = SoundManager(this) 81 showTime(0) 82 } 83 84 public fun playTimer(v : View) { 85 runnableDown = RunnableDown(10).start() 86 } 87 88 public fun stopTimer(v : View){ 89 runnableDown?.cancel(); 90 runnableDown = null 91 } 92}

レイアウト: activity_main.xml

xml

1<?xml version="1.0" encoding="utf-8"?> 2<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:app="http://schemas.android.com/apk/res-auto" 4 xmlns:tools="http://schemas.android.com/tools" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 tools:context=".MainActivity"> 8 9 <TextView 10 android:id="@+id/showTextTime" 11 android:layout_width="wrap_content" 12 android:layout_height="wrap_content" 13 android:text="0:00" 14 android:textSize="40dp" 15 app:layout_constraintBottom_toTopOf="@id/play" 16 app:layout_constraintLeft_toLeftOf="parent" 17 app:layout_constraintRight_toRightOf="parent" 18 app:layout_constraintTop_toTopOf="parent" /> 19 20 <Button 21 android:id="@+id/play" 22 android:layout_width="wrap_content" 23 android:layout_height="wrap_content" 24 android:text="start" 25 android:onClick="playTimer" 26 app:layout_constraintBottom_toTopOf="@id/stop" 27 app:layout_constraintLeft_toLeftOf="parent" 28 app:layout_constraintRight_toRightOf="parent" 29 app:layout_constraintTop_toBottomOf="@id/showTextTime" /> 30 31 <Button 32 android:id="@+id/stop" 33 android:layout_width="wrap_content" 34 android:layout_height="wrap_content" 35 android:text="stop" 36 android:onClick="stopTimer" 37 app:layout_constraintBottom_toBottomOf="parent" 38 app:layout_constraintLeft_toLeftOf="parent" 39 app:layout_constraintRight_toRightOf="parent" 40 app:layout_constraintTop_toBottomOf="@id/play" /> 41 42</androidx.constraintlayout.widget.ConstraintLayout>

投稿2021/09/12 15:16

jimbe

総合スコア13209

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kakashi55

2021/09/15 13:20

御丁寧な回答ありがとうございます。 原因がonResume で SoundPool を作り直したがために、soundPool 変数が上書きされてしまっていたのですね。 言われてみれば確かにその通りでした。 またご丁寧に動作するサンプルコードについても教えていただきありがとうございます。 無事本件解決し、理解を深めることができました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問