[Android] ๊ฐ„๋‹จํ•œ ๋ฎค์ง ํ”Œ๋ ˆ์ด์–ด ์•ฑ ๋งŒ๋“ค๊ธฐ : Service, MediaPlayer API
๋ฐ˜์‘ํ˜•

 

 

 

 

 

 

 

์ด๋ฒˆ์—” Service๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•œ ๋ฎค์ง ํ”Œ๋ ˆ์ด์–ด ์•ฑ์„ ๋งŒ๋“ค์–ด ๋ณด์•˜๋‹ค. ์—ญ์‹œ Joyce๋‹˜์˜ ์„œ์ ์„ ์ฐธ๊ณ ํ•˜์˜€๋‹ค.

 


 

๐Ÿ’ก ์Œ์•… ์žฌ์ƒ ๋ฐฉ๋ฒ•

(1) Raw ๋ฆฌ์†Œ์Šค๋ฅผ ์ด์šฉํ•˜์—ฌ ์žฌ์ƒ (res/raw ํด๋”์— ์Œ์•… ํŒŒ์ผ์„ ์ง์ ‘ ์‚ฝ์ž…)

val mPlayer : MediaPlayer? = MediaPlayer.create(this, R.raw.FILE_NAME)
mPlayer?.start()


(2) URI๋ฅผ ์‚ฌ์šฉํ•ด ์žฌ์ƒ (๊ธฐ๊ธฐ ์•ˆ์— ์žˆ๋Š” ์˜ค๋””์˜ค ํŒŒ์ผ์˜ ์œ„์น˜ URI ๊ฐ’์œผ๋กœ ์žฌ์ƒ)

val myUri: Uri = ... // URI ์ดˆ๊ธฐํ™”
val mediaPlayer: MediaPlayer? = MediaPlayer().apply {
	setAudioStreamType(AudioManager.STREAM_MUSIC)
    setDataSource(applicationContextm, myUri)
    prepare()
    start()
}

 

 

 

 


 

๐Ÿ’ก MediaPlayer ํด๋ž˜์Šค ์ง€์› ํ•จ์ˆ˜

  • ํŒŒ์ผ ์ค€๋น„
    - prepare() : ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰, ์šฉ๋Ÿ‰ ํด ๊ฒฝ์šฐ ANR ๊ฐ€๋Šฅ์„ฑ
    - prepareAsync() : ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰, ANR(์‘๋‹ต์—†์Œ) ๋ฐœ์ƒํ•˜์ง€ ์•Š์Œ, onPreparedListener ์‚ฌ์šฉ ๊ฐ€๋Šฅ
    - setDataResource()
  • ํŒŒ์ผ ์žฌ์ƒ
    - start() : ์žฌ์ƒ
    - pause() : ์ผ์‹œ๋ฉˆ์ถค
  • ํŒŒ์ผ ๋ฉˆ์ถค
    - reset() : ํ˜„์žฌ ์žฌ์ƒ๋˜๋Š” ๋ฏธ๋””์–ด ๋ฉˆ์ถค ๋ฐ MediaPlayer ๊ฐ์ฒด ์ดˆ๊ธฐํ™”
  • ์Œ์•… ๊ธธ์ด ์ฐพ๊ธฐ
    - getDuration() : ์Œ์•…์˜ ๊ธธ์ด, ๋‹จ์œ„๋Š” ๋ฐ€๋ฆฌ์ดˆ(ms)๋กœ ๋ฐ˜ํ™˜
  • ํŠน์ • ๊ตฌ๊ฐ„ ์ด๋™
    - seekTo()
  • ์ž์› ํ•ด์ œ
    - release() : ์‚ฌ์šฉํ•˜๋˜ ๋ฉ”๋ชจ๋ฆฌ์™€ ์ž์› ํ•ด์ œ. MediaPlayer๋ฅผ ๋” ์ด์ƒ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๋ฐ˜๋“œ์‹œ release() ํ˜ธ์ถœ

 

 

 

 


 

๐Ÿ’ก ์„œ๋น„์Šค์˜ ์ƒ๋ช… ์ฃผ๊ธฐ

์•ˆ๋“œ๋กœ์ด๋“œ 4๋Œ€ ๊ตฌ์„ฑ์š”์†Œ. ์„œ๋น„์Šค๋Š” ๋…๋ฆฝ๋œ ๊ตฌ์„ฑ์š”์†Œ์ด๋ฏ€๋กœ ๋…๋ฆฝ๋œ ์ƒ๋ช… ์ฃผ๊ธฐ๋ฅผ ๊ฐ€์ง (์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ์†Œ๋ฉธ๋˜์–ด๋„ ์‹คํ–‰์ค‘)
ํฌ๊ฒŒ ์‹œ์ž‘๋œ ์„œ๋น„์Šค์™€ ๋ฐ”์ธ๋“œ๋œ ์„œ๋น„์Šค ๋‘ ๊ฐœ๋กœ ๋‚˜๋‰˜๋ฉฐ, ๋ณดํ†ต์€ ์ด ๋‘ ๊ฐœ๋ฅผ ๊ฐ™์ด ๊ฐ–๊ณ  ์žˆ๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Œ

 

(1) startService()

  • ์•กํ‹ฐ๋น„ํ‹ฐ๋‚˜ ๋‹ค๋ฅธ ์„œ๋น„์Šค์—์„œ startService() ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์„œ๋น„์Šค ์‹œ์ž‘
  • ์„œ๋น„์Šค ๋‚ด์˜ ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ์ธ onCreate(), onStartCommand()๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉฐ ์‹œ์ž‘๋œ ์ƒํƒœ๊ฐ€ ๋จ
  • ํ•œ ๋ฒˆ ์‹œ์ž‘๋œ ์ƒํƒœ์˜ ์„œ๋น„์Šค๋Š” stopSelf() ํ•จ์ˆ˜๋กœ ์•Œ์•„์„œ ์ค‘์ง€ํ•˜๊ฑฐ๋‚˜, ๋‹ค๋ฅธ ๊ตฌ์„ฑ์š”์†Œ๊ฐ€ stopService()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ข…๋ฃŒํ•˜๊ธฐ ์ „๊นŒ์ง€๋Š” ๊ณ„์† ์‹คํ–‰์ค‘์ธ ์ƒํƒœ๋กœ ์กด์žฌ
  • stopSelf() ํ˜น์€ stopService()๋กœ ์ข…๋ฃŒ๋œ ๋’ค onDestroy()๋ฅผ ๊ฑฐ์ณ ์„œ๋น„์Šค ์ข…๋ฃŒ
  • ์ฐธ๊ณ ๋กœ ์•„๋ฌด๋ฆฌ ๋งŽ์€ ๊ตฌ์„ฑ์š”์†Œ์—์„œ startService()๋ฅผ ํ˜ธ์ถœํ•˜๋”๋ผ๋„ ์„œ๋น„์Šค ๊ฐ์ฒด ์ƒ์„ฑ ๋ฐ onCreate() ํ˜ธ์ถœ์€ ์ฒ˜์Œ ํ•œ ๋ฒˆ

(2) bindService()

  • ๋ฐ”์ธ๋“œ๋œ ์„œ๋น„์Šค๋Š” ๋‹ค๋ฅธ ๊ตฌ์„ฑ์š”์†Œ์™€ ์—ฐ๊ฒฐ์ด ๋œ ๋™์•ˆ ์‹คํ–‰๋˜๋Š” ์„œ๋น„์Šค
  • ๋‹ค๋ฅธ ๊ตฌ์„ฑ์š”์†Œ๋“ค์— ๋ฐ”์ธ๋“œ๋œ ๋™์•ˆ์—๋งŒ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์ด๊ณ , ๊ณ„์† ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์€ ์•„๋‹˜

 

 

๋ฐ”์ธ๋”ฉ๋œ ์„œ๋น„์Šค ๊ฐœ์š”  |  Background work  |  Android Developers

์ด ํŽ˜์ด์ง€๋Š” Cloud Translation API๋ฅผ ํ†ตํ•ด ๋ฒˆ์—ญ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋ฐ”์ธ๋”ฉ๋œ ์„œ๋น„์Šค ๊ฐœ์š” ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•ด ์ •๋ฆฌํ•˜๊ธฐ ๋‚ด ํ™˜๊ฒฝ์„ค์ •์„ ๊ธฐ์ค€์œผ๋กœ ์ฝ˜ํ…์ธ ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋ถ„๋ฅ˜ํ•˜์„ธ์š”. ๋ฐ”์ธ๋“œ๋œ ์„œ๋น„์Šค๋ž€ ํด๋ผ์ด์–ธํŠธ-

developer.android.com

 

(3) ๊ฒฐ๋ก 

  • ์‹œ์ž‘๋œ ์„œ๋น„์Šค๋Š” ๋‹ค๋ฅธ ๊ตฌ์„ฑ์š”์†Œ์™€์˜ ์—ฐ๊ฒฐ๊ณ ๋ฆฌ๊ฐ€ ์—†์œผ๋ฉฐ ํฌ๊ธฐ๊ฐ€ ํฐ ๋™์˜์ƒ์„ ๋‹ค์šด ๋ฐ›๋Š” ๊ฒฝ์šฐ๋ฅผ ์ƒ๊ฐํ•ด๋ณผ ์ˆ˜ ์žˆ์Œ
  • ๋ฐ”์ธ๋“œ๋œ ์„œ๋น„์Šค๋Š” ๋‹ค๋ฅธ ๊ตฌ์„ฑ์š”์†Œ์™€ ์†Œํ†ต์„ ํ•˜๋ฉฐ, ์—ฐ๊ฒฐ์„ ๋Š๊ฒŒ๋˜๋ฉด ์„œ๋น„์Šค๊ฐ€ ์ข…๋ฃŒ๋˜๋ฏ€๋กœ ๊ณ„์† ์‚ด์•„์žˆ์–ด์•ผ ํ•˜๋Š” ํƒœ์Šคํฌ์—๋Š” ๋ถ€์ ์ ˆํ•จ
  • ๋ณดํ†ต์€ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์— ๋‚จ์•„์žˆ๋Š” ๋™์‹œ์— ๋‹ค๋ฅธ ๊ตฌ์„ฑ์š”์†Œ์™€ ์—ฐ๊ฒฐ๋˜์–ด ์†Œํ†ต๋„ ๊ฐ€๋Šฅํ•˜๊ฒŒ๋” ๋‘ ๊ฐœ๋ฅผ ๋™์‹œ์— ์‚ฌ์šฉ
    → startService()์™€ bindService() ๋‘˜ ๋‹ค ์‹คํ–‰

 

 

 

 


 

๐Ÿ’ก ํฌ๊ทธ๋ผ์šด๋“œ์™€ ๋ฐฑ๊ทธ๋ผ์šด๋“œ

  • ํฌ๊ทธ๋ผ์šด๋“œ๋Š” ์ƒํƒœ ํ‘œ์‹œ์ค„์— ์•Œ๋ฆผ์ด ํ‘œ์‹œ๋˜๋ฉฐ, ์‚ฌ์šฉ์ž๊ฐ€ ์„œ๋น„์Šค๊ฐ€ ์‹คํ–‰๋˜๊ณ  ์žˆ์Œ์„ ๋Šฅ๋™์ ์œผ๋กœ ์ธ์ง€ํ•  ์ˆ˜ ์žˆ๋Š” ์„œ๋น„์Šค
  • ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋ณด์ด์ง€ ์•Š๋Š” ๊ณณ์—์„œ ์กฐ์šฉํžˆ ์ž‘์—… ์ˆ˜ํ–‰
  • ์•ˆ๋“œ๋กœ์ด๋“œ API ๋ ˆ๋ฒจ 26๋ถ€ํ„ฐ๋Š” ํฌ๊ทธ๋ผ์šด๋“œ ์„œ๋น„์Šค๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž์—๊ฒŒ ์•Œ๋ฆฌ๊ฒŒ๋” ํ•จ
    startForegroundService()
 

[Android: Basic] ํฌ๊ทธ๋ผ์šด๋“œ ์„œ๋น„์Šค ( Foreground Service ) ๊ฐœ๋…

๋“ค์–ด๊ฐ€๋ฉฐ.. ๋ฉด์ ‘ ์งˆ๋ฌธ์œผ๋กœ 4๋Œ€ ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•œ ์งˆ๋ฌธ์„ ์ข…์ข… ๋ฐ›์•˜๋˜ ๊ธฐ์–ต์ด ์žˆ๋‹ค. ์—…๋ฌด๋ฅผ ํ•˜๋ฉฐ ๊ธฐ๊ต๋งŒ ๋Š˜์–ด์„œ์ธ๊ฐ€, ์ด๋Ÿฐ ์›๋ก ์ ์ธ ์งˆ๋ฌธ์— ์ œ๋Œ€๋กœ ๋‹ตํ•˜์ง€ ๋ชปํ•˜๋Š” ์ž์‹ ์˜ ํ—›์ ์„ ๋ฉ”๊พธ๊ณ ์ž ๋‚ด๊ฐ€ ์•„

parade621.tistory.com

 

 

 

 


 

๐Ÿ’ก ๋งค๋‹ˆํŽ˜์ŠคํŠธ์— ๊ถŒํ•œ ๋ฐ ์„œ๋น„์Šค ์ถ”๊ฐ€

<manifest>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> // ์ถ”๊ฐ€
    
    <application   
    
        <service android:name=".MusicPlayerService"/> // ์ถ”๊ฐ€      
        
    </application>
</manifest>

 

 

 

๐Ÿ’ก ๊ธฐ๋ณธ ์„œ๋น„์Šค ํ‹€ ๊ตฌํ˜„

package com.limheejin.musicplayer

import android.app.Service
import android.content.Intent
import android.os.IBinder

class MusicPlayerService : Service() {

    // onCreate() : startService()๋กœ ์ƒ์„ฑํ•˜๋“  bindService()๋กœ ์ƒ์„ฑํ•˜๋“ , ์„œ๋น„์Šค๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ ๋”ฑ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰
    override fun onCreate() {
        super.onCreate()
        startForegroundService() // ํฌ๊ทธ๋ผ์šด๋“œ ์„œ๋น„์Šค ์‹œ์ž‘
    }

    // ๋ฐ”์ธ๋“œ : bindService()๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜, ๋ฐ”์ธ๋“œ๊ฐ€ ํ•„์š”์—†๋‹ค๋ฉด null์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋จ
    override fun onBind(intent: Intent?): IBinder? {  // IBinder : ์„œ๋น„์Šค์™€ ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ์ด์–ด์ฃผ๋Š” ๋งค๊ฐœ์ฒด ์—ญํ• 
        TODO("Not yet implemented")
    }

    // ์‹œ์ž‘๋œ ์ƒํƒœ & ๋ฐฑ๊ทธ๋ผ์šด๋“œ : startForegroundService() ํ˜ธ์ถœํ•  ๋•Œ ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        return super.onStartCommand(intent, flags, startId)
    }

    // ์„œ๋น„์Šค ์ข…๋ฃŒ : onCreate()์—์„œ ์ƒํƒœํ‘œ์‹œ์ค„์— ๋ณด์—ฌ์ฃผ์—ˆ๋˜ ์•Œ๋ฆผ ํ•ด์ œ
    override fun onDestroy() {
        super.onDestroy()
    }

	// ๋ฏธ๋ฆฌ ํ•จ์ˆ˜ ๊ตฌ์„ฑ ์ƒ์„ฑํ•ด๋†“๊ธฐ
    fun startForegroundService() {} 
    fun isPlaying() {}
    fun play() {}
    fun pause() {}
    fun stop() {}

}

 

 

๐Ÿ’ก ๊ตฌ์ฒด์ ์ธ ์„œ๋น„์Šค ์ˆ˜์ •

package com.limheejin.musicplayer

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Intent
import android.media.MediaPlayer
import android.os.Binder
import android.os.Build

import android.os.IBinder
import android.widget.Toast

class MusicPlayerService : Service() {

    var mMediaPlayer: MediaPlayer? = null // ๋ฏธ๋””์–ด ํ”Œ๋ ˆ์ด์–ด ๊ฐ์ฒด๋ฅผ null๋กœ ์ดˆ๊ธฐํ™”
    var mBinder: MusicPlayerBinder = MusicPlayerBinder()

    inner class MusicPlayerBinder : Binder() { // ๋ฐ”์ธ๋”๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์„œ๋น„์Šค ํ•จ์ˆ˜ ์“ธ ์ˆ˜ ์žˆ๋„๋ก
        fun getService(): MusicPlayerService {
            return this@MusicPlayerService
        }
    }

    // onCreate() : startService()๋กœ ์ƒ์„ฑํ•˜๋“  bindService()๋กœ ์ƒ์„ฑํ•˜๋“ , ์„œ๋น„์Šค๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ ๋”ฑ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰
    override fun onCreate() {
        super.onCreate()
        startForegroundService() // ํฌ๊ทธ๋ผ์šด๋“œ ์„œ๋น„์Šค ์‹œ์ž‘
    }

    // ๋ฐ”์ธ๋“œ : bindService()๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜, ๋ฐ”์ธ๋“œ๊ฐ€ ํ•„์š”์—†๋‹ค๋ฉด null์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋จ
    override fun onBind(intent: Intent?): IBinder? {  // IBinder : ์„œ๋น„์Šค์™€ ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ์ด์–ด์ฃผ๋Š” ๋งค๊ฐœ์ฒด ์—ญํ• 
        return mBinder // ์œ„์—์„œ ๋งŒ๋“ค์–ด๋‘” ๋ฐ”์ธ๋” ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜๊ฐ’์œผ๋กœ ์ „๋‹ฌ
    }

    // ์‹œ์ž‘๋œ ์ƒํƒœ & ๋ฐฑ๊ทธ๋ผ์šด๋“œ : startForegroundService() ํ˜ธ์ถœํ•  ๋•Œ ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        return START_STICKY // START_STICKY, START_NOT_STICKY, START_REDELIVER_INTENT ์ค‘ ํ•˜๋‚˜ ๋ฐ˜ํ™˜
        // START_STICKY : ์‹œ์Šคํ…œ์ด ์„œ๋น„์Šค๋ฅผ ์ค‘๋‹จํ•˜๋ฉด ์„œ๋น„์Šค๋ฅผ ๋‹ค์‹œ ์‹คํ–‰ํ•˜๊ณ  onStartCommand() ํ˜ธ์ถœ
        // START_NOT_STICKY : ์‹œ์Šคํ…œ์ด ์„œ๋น„์Šค๋ฅผ ์ค‘๋‹จ์‹œํ‚ค๋ฉด ์„œ๋น„์Šค๋ฅผ ์žฌ์ƒ์„ฑํ•˜์ง€ ์•Š์Œ
        // START_REDELIVER_INTENT : ์‹œ์Šคํ…œ์ด ์„œ๋น„์Šค๋ฅผ ์ค‘๋‹จํ•˜๋ฉด ์„œ๋น„์Šค๋ฅผ ๋‹ค์‹œ ์‹คํ–‰ํ•˜๊ณ  onStartCommnad() ํ˜ธ์ถœ
        //                          +์„œ๋น„์Šค ์ข…๋ฃŒ ์ „ ๋งˆ์ง€๋ง‰์œผ๋กœ ์ „๋‹ฌ๋œ ์ธํ…ํŠธ ์žฌ์ „๋‹ฌ. (๋ฐ˜๋“œ์‹œ ๋ช…๋ น์„ ์‹คํ–‰ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ)
    }

    // ์„œ๋น„์Šค ์ข…๋ฃŒ : onCreate()์—์„œ ์ƒํƒœํ‘œ์‹œ์ค„์— ๋ณด์—ฌ์ฃผ์—ˆ๋˜ ์•Œ๋ฆผ ํ•ด์ œ
    override fun onDestroy() {
        super.onDestroy()
        stopForeground(true) // ์„œ๋น„์Šค๊ฐ€ ์ค‘๋‹จ๋  ๋•Œ ํฌ๊ทธ๋ผ์šด๋“œ ์„œ๋น„์Šค ๋ฉˆ์ถค
    }

    fun startForegroundService() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
            val mChannel = NotificationChannel( // ์•Œ๋ฆผ ์ฑ„๋„ ์ƒ์„ฑ (ํ•„์ˆ˜)
                "CHANNEL_ID",
                "CHANNEL_NAME",
                NotificationManager.IMPORTANCE_DEFAULT
            )
            notificationManager.createNotificationChannel(mChannel)
        }
        val notification: Notification = Notification.Builder(this, "CHANNEL_ID")
            .setSmallIcon(R.drawable.ic_play) // ์•Œ๋ฆผ ์•„์ด์ฝ˜
            .setContentTitle("๋ฎค์ง ํ”Œ๋ ˆ์ด์–ด ์•ฑ") // ์•Œ๋ฆผ ์ œ๋ชฉ ์„ค์ •
            .setContentText("์•ฑ์ด ์‹คํ–‰์ค‘์ž…๋‹ˆ๋‹ค.") // ์•Œ๋ฆผ ๋‚ด์šฉ ์„ค์ •
            .build()
        startForeground(222, notification) // ์ธ์ˆ˜๋กœ ์•Œ๋ฆผ ID ์‹๋ณ„์ž์™€ ์•Œ๋ฆผ ์ง€์ •, ๊ทธ๋Ÿฐ๋ฐ ์ด ๋ถ€๋ถ„์—์„œ ์˜ค๋ฅ˜๋‚˜์„œ ๊บผ์ง, ์ฃผ์„ ์ฒ˜๋ฆฌ ํ•  ๊ฒƒ
    }

    fun isPlaying(): Boolean {
        return (mMediaPlayer != null && mMediaPlayer?.isPlaying ?: false)
    }

    fun play() {
        if (mMediaPlayer == null) {
            // ์Œ์•… ํŒŒ์ผ์˜ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ฐ€์ ธ์™€ ๋ฏธ๋””์–ด ํ”Œ๋ ˆ์ด์–ด ๊ฐ์ฒด ํ• ๋‹น
            mMediaPlayer = MediaPlayer.create(this, R.raw.singing_mp3)
            mMediaPlayer?.setVolume(1.0f, 1.0f) // ๋ณผ๋ฅจ ์ง€์ •
            mMediaPlayer?.isLooping = true // ๋ฐ˜๋ณต ์žฌ์ƒ
            mMediaPlayer?.start() // ์Œ์•… ์žฌ์ƒ
        } else { // ์Œ์•… ์žฌ์ƒ ์ค‘์ธ ๊ฒฝ์šฐ
            if (mMediaPlayer!!.isPlaying){
                Toast.makeText(this, "์ด๋ฏธ ์Œ์•…์ด ์‹คํ–‰์ค‘์ž…๋‹ˆ๋‹ค.", Toast.LENGTH_SHORT).show()
            }else {
                mMediaPlayer?.start() // ์Œ์•… ์žฌ์ƒ
            }
        }
    }
    fun pause() {
        mMediaPlayer?.let{
            if (it.isPlaying){
                it.pause() // ์ผ์‹œ์ •์ง€
            }
        }
    }
    fun stop() {
        mMediaPlayer?.let {
            if(it.isPlaying) {
                it.stop() // ์Œ์•… ๋ฉˆ์ถค
                it.release() // ๋ฏธ๋””์–ด ํ”Œ๋ ˆ์ด์–ด์— ํ• ๋‹น๋œ ์ž์› ํ•ด์ œ
                mMediaPlayer = null
            }
        }
    }

}

 

 

๐Ÿ’ก ๊ฐ„๋‹จํ•œ ์•กํ‹ฐ๋น„ํ‹ฐ ์ฝ”๋“œ ํ‹€

package com.limheejin.musicplayer

import android.os.Bundle
import android.view.View
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.limheejin.musicplayer.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() , View.OnClickListener { // ๋ทฐ๋ฅผ ํด๋ฆญํ–ˆ์„ ๋•Œ ํ–‰๋™ ์ •ํ•ด์ฃผ๋„๋ก ๊ตฌํ˜„

    private lateinit var binding : ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        enableEdgeToEdge()
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }
        
        binding.btnPlay.setOnClickListener(this)
        binding.btnPause.setOnClickListener(this)
        binding.btnStop.setOnClickListener(this)
    }
    
    override fun onClick(v: View?) {
        when(v?.id){
            R.id.btn_play -> {
                play()
            }
            R.id.btn_pause -> {
                pause()
            }
            R.id.btn_stop -> {
                stop()
            }
        }
    }

    override fun onResume() {
        super.onResume()
    }

    override fun onPause() {
        super.onPause()
    }
    
    private fun play() {
    }

    private fun pause() {
    }

    private fun stop() {
    }
}

 

 

๐Ÿ’ก ์ƒ์„ธํ•œ ์•กํ‹ฐ๋น„ํ‹ฐ ์ฝ”๋“œ

package com.limheejin.musicplayer

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Build
import android.os.Build.VERSION_CODES.P
import android.os.Bundle
import android.os.IBinder
import android.view.View
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.limheejin.musicplayer.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity(), View.OnClickListener { // ๋ทฐ๋ฅผ ํด๋ฆญํ–ˆ์„ ๋•Œ ํ–‰๋™ ์ •ํ•ด์ฃผ๋„๋ก ๊ตฌํ˜„

    private lateinit var binding: ActivityMainBinding
    private var mService: MusicPlayerService? = null // ์„œ๋น„์Šค ๋ณ€์ˆ˜

    private val mServiceConnection = object : ServiceConnection { // ์„œ๋น„์Šค์™€ ๊ตฌ์„ฑ์š”์†Œ ์—ฐ๊ฒฐ ์ƒํƒœ ๋ชจ๋‹ˆํ„ฐ๋ง
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            mService =
                (service as MusicPlayerService.MusicPlayerBinder).getService() // MusicPlayerBinder๋กœ ํ˜•๋ณ€ํ™˜
        }

        override fun onServiceDisconnected(name: ComponentName?) {
            mService = null // ๋งŒ์•ฝ ์„œ๋น„์Šค๊ฐ€ ๋Š๊ธฐ๋ฉด, mService๋ฅผ null๋กœ ๋งŒ๋“ฆ
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        enableEdgeToEdge()
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }

        binding.btnPlay.setOnClickListener(this)
        binding.btnPause.setOnClickListener(this)
        binding.btnStop.setOnClickListener(this)
    }

    override fun onClick(v: View?) {
        when (v?.id) {
            R.id.btn_play -> {
                play()
            }

            R.id.btn_pause -> {
                pause()
            }

            R.id.btn_stop -> {
                stop()
            }
        }
    }

    override fun onResume() {
        super.onResume()

        // ์„œ๋น„์Šค ์‹คํ–‰
        if (mService == null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                startForegroundService(Intent(this, MusicPlayerService::class.java))
            } else {
                startService(Intent(applicationContext, MusicPlayerService::class.java))
            }
        }

        // ์•กํ‹ฐ๋น„ํ‹ฐ๋ฅผ ์„œ๋น„์Šค์™€ ๋ฐ”์ธ๋“œ
        val intent = Intent(this, MusicPlayerService::class.java)
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE) // ๋ฐ”์ธ๋“œํ•  ์‹œ์ ์— ์„œ๋น„์Šค๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š์œผ๋ฉด AUTO CREATE
    }

    // onPause() : ์‚ฌ์šฉ์ž๊ฐ€ ์•กํ‹ฐ๋น„ํ‹ฐ๋ฅผ ๋– ๋‚ฌ์„ ๋•Œ ์‹คํ–‰
    override fun onPause() {
        super.onPause()
        
        if (mService != null) {
            if (!mService!!.isPlaying()) { // mService๊ฐ€ ์žฌ์ƒ๋˜๊ณ  ์žˆ์ง€ ์•Š๋‹ค๋ฉด ์„œ๋น„์Šค๋ฅผ ์ค‘๋‹จ
                mService!!.stopSelf() 
            }
            unbindService(mServiceConnection) // ์„œ๋น„์Šค๋กœ๋ถ€ํ„ฐ ์—ฐ๊ฒฐ ๋Š๊ธฐ. ์Œ์•…์ด ์‹คํ–‰ ์ค‘์ด๋ฉด ๋ฐ”์ธ๋”ฉ๋งŒ ํ•ด์ œ
            mService = null
        }
    }

    private fun play() {
        mService?.play()
    }

    private fun pause() {
        mService?.pause()
    }

    private fun stop() {
        mService?.stop()
    }
}

 

 

 


๐Ÿ”ฅ ์™„์„ฑ

 

  • ์„œ๋น„์Šค๋ฅผ ์ด์šฉํ•œ ์Œ์•… ์žฌ์ƒ ์•ฑ์ด ์™„์„ฑ๋˜์—ˆ๋‹ค.
  • ๋‹ค๋งŒ, ๋ช‡ ๊ฐ€์ง€ ๋ฌธ์ œ์ ์ด ์žˆ๋Š”๋ฐ
    (1) ์„œ๋น„์Šค์˜ startForeground ์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋‚˜์„œ ์•ฑ์ด ๋‹ค์šด๋˜๋Š” ๋ฌธ์ œ
    (2) ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์Œ์•… ์žฌ์ƒ์ด ์ง„ํ–‰์ด ๋˜๋‚˜, ์ ˆ์ „์œผ๋กœ ์ธํ•ด ๋ฉ”๋ชจ๋ฆฌ์—์„œ ๊ธˆ๋ฐฉ ์•ฑ์ด ํ‚ฌ๋ง๋˜์–ด ๋…ธ๋ž˜๊ฐ€ ๋ฉˆ์ถ”๋Š” ๋ฌธ์ œ
    ์œ„์™€ ๊ฐ™์€ ๋ฌธ์ œ์ ๋“ค์ด ์žˆ์—ˆ๋‹ค.

 

 

 

 

 

 

๋ฐ˜์‘ํ˜•
 ๐Ÿ’ฌ C O M M E N T