모바일앱개발 강좌

모바일앱개발 강좌 #16 - 배경음악 리소스에 등록하기, MediaPlayer사용하여 배경음악 재생하기, MediaPlayer, SoundPool 객체 미사용시 리소스 해제하기, AudioManager를 이용해서 Sound On/Off 기능 넣기, 볼륨UI 표시하기

두결앱개발 2025. 11. 1. 06:23
반응형
SMALL

 

안녕하세요~ 두결입니다.

 

지난시간에는 SoundPool을 사용하여 간단한 효과음을 재생하는 방법과 사운드파일 로드가 완료된 시점에 재생하는 방법에 대해서 알아보았습니다.

 

이번시간에는 앱에 배경음악 넣기를 해보겠습니다.


▣  배경음악 리소스에 등록하기, MediaPlayer사용하여 배경음악 재생하기

간단한 효과음은 SoundPool로 했는데

MP3같은 음악파일은 어떻게 재생할수 있을까요?

 

바로 MediaPlayer로 할수있습니다.

 

일단, 재생할 배경음악이 있어야겠죠?

배경음악 하나쯤은 가지고 계시죠? ㅎㅎ 혹시 없으시면 무료 배경음악 검색하셔서 다운로드 받으셔도 되고

유튜브 스튜디오에도 무료 배경음악이 많이있습니다.

 

 

유튜브 계정이 있긴해야합니다.

 

꼭 배경음악아니더라도 일반 노래로 해도 상관은 없습니다.

 

 

저는 가지고 있는 노래중에 하나 골라서 bgsound1.mp3파일이라고 한뒤에

이전시간에 한것처럼 복사한 뒤 res/raw폴더에 붙여넣기 해주었습니다.

 

 

 

네 위와같이 bgsound1.mp3파일이 리소스에 잘 등록이 되었습니다.

 

 

MediaPlayer로 음악을 재생하는것은 간단합니다.

MediaPlayer mediaPlayer = MediaPlayer.create(this, R.raw.bgsound1);
mediaPlayer.setLooping(true);
mediaPlayer.start();

 

위와같이 3줄이면 됩니다.

 

MediaPlayer는 앱에서 비디오 및 오디오를 재생하는데 사용하는 API입니다. 

MediaPlayer를 통해 리소스에 등록된 미디어 파일을 재생할 수 있습니다.

MediaPlayer.create()함수로 mediaPlayer객체를 생성하는데 이때 리소스 등록한 bgsound1 파일을 지정합니다.

 

다음으로

MediaPlayer.setLooping()함수는 반복재생을 할지를 결정하는 함수로

true이면 미디어 재생이 끝나면 처음부터 다시 반복재생을 하고

false이면 한번만 재생되고 종료됩니다.

 

그리고, 

MediaPlayer.start()로 재생이 시작됩니다.

MediaPlayer.pause()로 일시정지도 할수 있고

MediaPlayer.stop()으로 재생을 정지할수도 있습니다.

 

 

자, 배경음악이 잘 나오는지 실행해보겠습니다.

 

 

 

네.. 배경음악이 잘 나오네요...^^

 

배경음악은 첨밀밀입니다. ^^

 

 

아. 그런데 여기서 잠깐!

 

MediaPlayer변수 선언을 Local로 하면 배경음악이 나오다가 잘리는 경우가 발생하더라구요~

또한, 효과음과 같이사용했을때도 배경음악이 안나오는 경우가 있습니다.

저도 이번에 처음 알았네요..

MediaPlayer변수는 Local이 아닌 전역변수로 해주세요~

 

int nSoundID = 0;
MediaPlayer mediaPlayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    EdgeToEdge.enable(this);
    setContentView(R.layout.activity_main);


    mediaPlayer = MediaPlayer.create(this, R.raw.bgsound1);
    mediaPlayer.setLooping(true);
    mediaPlayer.start();

 

위와같이 onCreate()함수 위쪽에 선언하면 전역변수입니다.

전역변수란 MainActivity.java 소스안에서 어디서든 사용할수 있는 변수를 애기합니다.

 

 


▣  MediaPlayer, SoundPool 객체 미사용시 리소스 해제하기

타이머도 그렇지만 MediaPlayer나 SoundPool객체는 미사용시에는 리소스를 해제시켜주어야 합니다.

그렇지 않으면 리소스에 계속 남아서 메모리 누수등의 문제가 발생할수 있습니다.

@Override
protected void onDestroy()
{
    super.onDestroy();

    if(mediaPlayer !=null)
    {
        mediaPlayer.stop();
        mediaPlayer.release();
        mediaPlayer = null;
    }

    if(soundPool !=null)
    {
        soundPool.release();
        soundPool = null;
    }
    Log.d("EXIT", "종료시 호출되나?");
}

 

위와같이 onDestroy()함수에서

MediaPlayer객체가 null이 아니라면 stop()후에 release()해주고 null로 만들어주었습니다.

MediaPlayer.release()함수는 사용하던 시스템 리소스를 즉시 반환해주는 함수입니다.

 

그리고, onDestroy()함수는

액티비티가 완전히 소멸되기 직전에 시스템이 호출하는 마지막 콜백 함수입니다.

이 함수에서 관련된 모든 자원을 정리하거나, 진행중인 작업을 중지하는 등의 최종 마무리 작업을 수행할수 있습니다.

 

MediaPlayer와 비슷하게 

SoundPool도 release()한 후에 null로 만들어 주었습니다.

 

onDestroy()함수가 잘 동작하는 지 확인하기 위해 

화면상에 종료버튼을 하나 만들어보겠습니다.

 

 

 

위와같이 화면 우측상단에 버튼을 하나 갖다놓구요~

 

<Button
    android:id="@+id/btnExit"
    android:layout_width="60dp"
    android:layout_height="wrap_content"
    android:text="X"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintHorizontal_bias="0.9"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.01" />

 

ID는 btnExit

text는 "X"라고 하였습니다.

그리고, width는 60dp로 설정하였습니다.

"X"글자 하나만 표시하면 되니까 작게 설정하였습니다.

 

Button btn_Exit = findViewById(R.id.btnExit);
btn_Exit.setTextSize(20);
btn_Exit.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        finishAndRemoveTask();
    }
});

 

 

그리고, btn_Exit객체 생성하고

글씨크기는 20으로 하고

버튼 클릭이벤트에서 finishAndRemoveTask()로 앱이 종료되게 하였습니다.

 

실행을 해서 로그를 확인해보면~

 

 

종료버튼 눌렀을때

onDestroy()함수가 실행되어서 아래쪽 로그창에

"종료시 호출되나?"라는 로그가 잘 표시됨을 확인할 수 있습니다.

 

 


▣  AudioManager를 이용해서 Sound On/Off 기능 넣기, 볼륨UI 표시하기

자, 마지막으로 Sound On/Off 기능을 한번 넣어보겠습니다.

 

Sound On/Off기능은 AudioManager 클래스의 adjustStreamVolume()함수로 가능합니다.

 

먼저, 버튼 2개를 만들어보겠습니다.

 

네 위와같이 버튼 2개를 갖다놓구요~

 

<Button
    android:id="@+id/btnSoundOn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Sound ON"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintHorizontal_bias="0.15"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.01"  />

<Button
    android:id="@+id/btnSoundOff"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Sound OFF"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintHorizontal_bias="0.6"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.01"  />

 

ID는 btnSoundOn, btnSoundOff라고 하고

text는 "Sound ON", "Sound OFF"라고 하였습니다.

 

실행을 해보면

 

네 위와같이 화면 상단에 버튼 2개가 잘 보이네요..^^

 

Button btn_SoundOn = findViewById(R.id.btnSoundOn);
btn_SoundOn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        
    }
});

Button btn_SoundOff = findViewById(R.id.btnSoundOff);
btn_SoundOff.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {

    }
});

 

MainActivity.java에서

2개의 버튼에 대한 객체를 생성하고 클릭이벤트까지 만들었습니다.

 

자, 이제 AudioManager를 사용해보겠습니다.

AudioManager audioManager;
audioManager = (AudioManager)getSystemService(AUDIO_SERVICE);

Button btn_SoundOn = findViewById(R.id.btnSoundOn);
btn_SoundOn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,AudioManager.ADJUST_UNMUTE,0);
    }
});

Button btn_SoundOff = findViewById(R.id.btnSoundOff);
btn_SoundOff.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,AudioManager.ADJUST_MUTE,0);
    }
});

 

먼저, AudioManager 객체를 getSystemService()를 통해 생성하였습니다.

getSystemService()는 단순히 사운드뿐만 아니라 네트워크, 위치정보 등과 같은 시스템 서비스에 접근하기 위한 함수입니다.

getSystemService(AUDIO_SERVICE)는 오디오 관련 서비스에 접근하기위한 함수입니다.

 

이렇게 AudioManager객체를 생성한 뒤에

Sound On/Off 버튼의 클릭이벤트에서 

adjustStreamVolume()함수를 통해 볼륨을 조정하였습니다.

 

adjustStreamVolume()함수는 오디오 스트림의 볼륨을 조정해주는 함수입니다.

 

첫번째 인자는 streamType으로 

AudioManager.STREAM_MUSIC은 미디어 재생볼륨

AudioManager.STREAM_RING은 벨소리 볼륨

AudioManager.STREAM_ALARM은 알람 볼륨입니다.

 

두번째 인자는 direction으로

AudioManager.ADJUST_RAISE와 AudioManager.ADJUST_LOWER로 한단계씩 볼륨을 높이거나 낮출수 있습니다.

저희는 음소거를 할것이기 때문에

AudioManager.ADJUST_MUTE, AudioManager.ADJUST_UNMUTE를 사용하겠습니다.

 

세번째 인자는 flags인데

볼륨조절시 추가동작을 지정합니다.

AudioManager.FLAG_SHOW_UI로 하면 볼륨 조절 시 화면에 볼륨 UI를 표시합니다.

 

이 볼륨 UI도 한번 표시해보겠습니다.

audioManager = (AudioManager)getSystemService(AUDIO_SERVICE);

Button btn_SoundOn = findViewById(R.id.btnSoundOn);
btn_SoundOn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,AudioManager.ADJUST_UNMUTE,
                                        AudioManager.FLAG_SHOW_UI);

    }
});

Button btn_SoundOff = findViewById(R.id.btnSoundOff);
btn_SoundOff.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,AudioManager.ADJUST_MUTE,
                                        AudioManager.FLAG_SHOW_UI);
    }
});

 

네 위와같이 수정하였습니다.

 

 

원래는 볼륨조절하는 SeekBar도 넣어볼까했는데 볼륨UI가 있어서 별도로 만들필요가 없겠네요.. ^^

 

 

전체 소스입니다. 참고하세요~

MainActivity.java

package com.example.soundtest;

import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.SoundPool;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

public class MainActivity extends AppCompatActivity {
    int nSoundID = 0;
    MediaPlayer mediaPlayer;
    SoundPool soundPool;
    AudioManager audioManager;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);


        mediaPlayer = MediaPlayer.create(this, R.raw.bgsound1);
        mediaPlayer.setLooping(true);
        mediaPlayer.start();

        soundPool = new SoundPool.Builder().build();

        Button btn_OKSound = findViewById(R.id.btnOKSound);
        btn_OKSound.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                nSoundID =  soundPool.load(getApplicationContext(),R.raw.oksound,1);
                soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
                    @Override
                    public void onLoadComplete(SoundPool soundPool, int i, int i1) {
                        soundPool.play(i, 1f, 1f, 1, 0, 1f);
                    }
                });
            }
        });

        Button btn_NGSound = findViewById(R.id.btnNGSound);
        btn_NGSound.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                nSoundID =  soundPool.load(getApplicationContext(),R.raw.ngsound,1);
                soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
                    @Override
                    public void onLoadComplete(SoundPool soundPool, int i, int i1) {
                        soundPool.play(i, 1f, 1f, 1, 0, 1f);
                    }
                });
            }
        });

        audioManager = (AudioManager)getSystemService(AUDIO_SERVICE);

        Button btn_SoundOn = findViewById(R.id.btnSoundOn);
        btn_SoundOn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,AudioManager.ADJUST_UNMUTE,
                                                AudioManager.FLAG_SHOW_UI);

            }
        });

        Button btn_SoundOff = findViewById(R.id.btnSoundOff);
        btn_SoundOff.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,AudioManager.ADJUST_MUTE,
                                                AudioManager.FLAG_SHOW_UI);
            }
        });


        Button btn_Exit = findViewById(R.id.btnExit);
        btn_Exit.setTextSize(20);
        btn_Exit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finishAndRemoveTask();
            }
        });


        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });
    }

    @Override
    protected void onDestroy()
    {
        super.onDestroy();

        if(mediaPlayer !=null)
        {
            mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer = null;
        }

        if(soundPool !=null)
        {
            soundPool.release();
            soundPool = null;
        }
        Log.d("EXIT", "종료시 호출되나?");
    }
}

 

 

네 이번시간은 여기까지만 하도록 하구요~

 

다음시간에는 Sound On/Off 버튼과 "X"버튼을 이미지버튼으로 만들어서 현재 화면을 조금더 예쁘게 만들어보도록 하겠습니다.

 

그럼, 이만~

 

감사합니다. ^^

반응형
LIST