본문 바로가기
Android 개발/Service, Receiver

Android Foreground Service 로 Background 에서 음악 플레이 하기

by Developer88 2020. 1. 18.
반응형

폰을 사용하다가 음악을 들어보신적이 있죠.

네이버 뮤직 이나 멜론 같은 앱을 실행해서 플레이를 시킨후, 

다른앱을 사용해도 꺼지지 않고 플레이가 되는데요.

이렇게 background에서 동작하는 컴포넌트가 바로 Service입니다.

 

오늘은 이 Service 에 대해서 정리해보도록 하겠습니다.

 

Service는 UI를 가지고 있지 않기 때문에, 인터랙션이 필요한 경우에는 Activity로 연결시켜야 하구요.

쓰레드 관련해서도 주의가 필요한데요. 

디폴트로 메인쓰레드에서 동작하므로, cpu를 많이 쓰는 일을 할 때는 ,다른 쓰레드에서 작동될수 있도록 해줘야 합니다.

이 밖에도 여러가지가 있지만 아래에서 관련 요소들을 보시면서 정리해볼께요.

 

Service를 실행시키는 방법에는 두가지가 있는데요.

startService() 메소드 를 사용하거나, onBindService() 를 통해서 실행시키는 방법입니다.

 

1. StartService vs BindService

그럼, StartService와 BindeService를 비교해 보겠습니다.

 

StartService는 앱내의 액티비티같은 컴포넌트가 호출했을 때 실행되는데요.

백그라운데에서 한가지일을 하며, 결과를 호출했던 컴포넌트에게 보내지 않습니다.

예를들면, 다운로드라든가, 파일을 업로드하는 것들이 있습니다.

 

bindService는 startService와는 다르게, 자신을 호출했던 컴포넌트와 인터랙션을 주고 받을 수 있습니다.

처리한 결과를 주고받을 수 있는 건데요. 서로 다른 프로세스상에 있어도 이 처리를 할 수 있습니다.

한 서비스에  여러 컴포넌트가 붙을 수도 있어요.

 

startService와 관련된 메소드만 구현하거나, bindService와 관련된 메소드만 구현해서,

둘중 하나만 동작하도록 할 수 있지만, 둘다 동시에 구현해서 실행할 수 도 있습니다.

 

우선 startService에 대한 구현에 대해서 정리해 보도록 하겠습니다.

 

2. StartService 의 라이프사이클

2-1. onCreate

액티비티의 onCreate와 비슷한 역할로 보시면 될 것 같습니다. 

서비스가 처음 초기화 될때 수행되는 메소드입니다.

 

2-2. onStartCommand

service를 다른 컴포넌트가 호출했을 경우 수행되는 데요,

서비스가 백그라운드에서 동작되기 시작합니다.

 

onStartCommand()에서 다음의 3가지 중 한가지를 리턴 해 줄 수 있는데요.

 

구분 내용
START_NOT_STICKY 서비스가 시스템에 의해 종료되었을때 PendingIntent가 따로 있지않다면 다시 생성시켜주지 않는 것
START_STICKY 시스템에 의해 종료되어도 다시 생성시켜주는 것입니다. 
왜 sticky라고 했는지 알 것같네여요.
하지만 이것이 무적은 아닌 것이 마지막 받았던 인텐트를 넘겨주지 않고,
서비스 객체만 다시 생성시켜줍니다.

미디어 플레이어에는 적합한 값이구요.
START_REDELIVER_INTENT 시스템에 의해 종료되어도 서비스는 물론, 전달되었던 intent값까지 모두 유지시켜는 값입니다.

파일다운로드처럼 중간에 값을 잃으면 않될경우에 적합합니다.     

 

2-3. onDestroy

서비스가 더이상 사용되지 않을 때 호출되는 메소드 입니다.

 

다른 컴포넌트에서 startService()로 호출할 경우 onStartCommand가 실행되구요,

stopService나 stopSelf()가 호출되면 서비스가 동작을 멈추고,

시스템이 onDestory를 호출되는 사이클이에요.

 

그럼 이론적인 부분들은 여기까지 정리해보고,

실제 서비스로 음악을 플레이하고, 백그라운드에서도 계속 들을 수 있도록 구현하면서 더 정리해보겠습니다.

 

3. StartService 구현 준비

음악을 플레이해야하니, mp3파일을 한번 준비해보겠습니다.

플레이할 mp3파일을 raw폴더에 넣어볼께요.

아래와 같이 resource directory를 생성해주시구요.

 

 

 

리소스타입을 raw로 선택해주면 됩니다.

이렇게 생성된 폴더에 파인더에서 파일을 찾아 복사해 줍니다.

 

 

정상적으로 하셨다면, 아래와 같은 형태로 복사됩니다.

 

 

3. UI 생성

다음으로, 서비스를 호출해서 음악을 플레이할 수 있는 버튼과 정지하는 버튼을 만들겠습니다. 

레이아웃 파일에서 아래와 같이 만들어 줍니다.

 

 

 

MainActivity 에서 onClickListener를 달아줍니다.

버튼을 누르면, service를 실행시켜서 음악이 백그라운드에서도 재생될 수 잇도록 해보겠습니다.

 

 

4. Service 생성

Service를 생성할때는, Activity나 Fragment를 만드는 것처럼 AndroidStudio를 이용해서 만들면 편합니다.

 

 

 

클릭하면 아래와 같은 화면이 나오는데요.

원하는 이름으로 바꾼다음 넘어가주면 됩니다.

참고로 Exported와 Enabled옵션이 체크되어 있는 것을 알 수 있는데요.

Exported는 자신의 앱에서만 Service를 실행시킬 수 있도록 할 것인제에 대한 옵션인데요.

false로 해 놓으면 다른 앱에서는 실행시킬 수 없습니다.

Enabled는 시스템에 의해서 객체화 시킬 수 있는지에 대한 옵션인데요.

true로 해 놓으면 시스템에 의해서 시작 될 수 있습니다.

알람앱 같은 곳에서는 사용될 수 있을 것 같습니다.

 

 

이렇게 하면,  Manifest에도 선택한 옵션값들이 등록됩니다.

Manifest 파일에서 service가 아래와 같이 선언되도록 해 주면 됩니다.

 

<service
    android:name=".TestousSoundService"
    android:foregroundServiceType="mediaPlayback"	
    android:enabled="true"
    android:exported="false"
/>

 

위에서 enabled와 exported 그리고 foregroundServiceType은 다음과 같은 의미를 가집니다.

 

구분  
enabled 안드로이드 시스템에 의해서 서비스를 시작하도록 허락할지에 관한 옵션입니다.
exported 다른 앱이 해당 서비스를 시작할 수 있도록 할지에 관한 옵션입니다.
foregroundServiceType foregroundServiceType 이 mediaPlayback, mediaProjection, or phoneCall 중 하나일 경우,
notification을 지체하지 않고 보여줍니다.

 

 

그리고 manifest의 상단에 FOREGROUND_SERVICE 에 대한 permission을 추가해 주어야 합니다.

다행히 동적인 permission은 아니므로, manifest에서 선언하는 것 만으로 추가적인 코딩은 필요하지 않습니다.

 

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

    <application ...>
        ...
    </application>
</manifest>

 

5. Service의 onStartCommand 구현

이제 TestousSoundService 클래스에서 onStartCommand를 구현해서,

음악을 실행할 수 있도록 하겠습니다.

 

intent로 true, false를 받아서 음악을 실행하거나, 정지시키기 위해 아래와 같이 구현하였습니다.

그리고, 위에서도 언급했던 return값은 START_NOT_STICKY로,

시스템에 의해 종료되면 다시 실행되지 않는 값으로 하겠습니다.

 

 

6. MainActity 에서 startService

이제 준비는 거의다 되었네요.

play버튼을 누를 경우에만 true값을 intent에 실어 보내서, 음악이 재생되도록 하고, 

그렇지 않을 경우에는 음악이 멈추도록 하겠습니다.

 

 

7. Notification 넣기

추가적으로, 실행하던 앱에서 백그라운드로 실행중에 NotificationBar에서 

실행중인 앱의 MainActivity로 진입할 수 있도록 해보겠습니다.

이 때, PendingIntent를 사용하는데요.(이 부분은 추후에 정리해 보려고 합니다.)

Notification빌더에서 setContentIntent() 메소드를 사용해서, 이 intent를 전달해주면됩니다.

 

 

 

22년도부터, 플레이스토어에 출시하는 앱들은 API31을 타게팅하도록 변경되었는데요.

위의 코드 중 PendingIntent 객체인 mPendingIntent 를 선언하는 부분을 아래와 같이 수정해 주어야 합니다.

 

val flag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
} else PendingIntent.FLAG_UPDATE_CURRENT
PendingIntent mPendingIntent = PendingIntent.getActivity(
	this, 1, mMainIntent, flag
)

 

참고로 API31 과 관련해서 수정해야 하는 부분에 대해서는 아래 글을 참조하시면 됩니다.

>> Target Api 31 ( Android12 ) 에서 적용해야 하는 사항들 # Location Intent Filters exported mutability

 

Target Api 31 ( Android12 ) 에서 적용해야 하는 사항들 # Location Intent Filters exported mutability

이제 개발하는 앱들은 모두 targetAPI를 31로 잡아야 하는데요. 오늘은 어떤 것들에 대응해야 하는지 정리해 보도록 하겠습니다. 1. TargetAPI 31(Android12) 22년 11월 부터는 TargetAPI가 31이하일 경우에는

developer88.tistory.com

 

위와 같이 하시면, 백그라운드 음악을 듣다가 노티피케이션을 내려서 보면 아래와 같은 화면을 보실 수 있습니다.

 

 

이상으로 Service에 대해서 정리해보았는데요.

이에 대한 업데이트는 이 글을 통해서 하도록 하겠습니다.

참고로 bindService에 대해서는 다른 글에서 다루었으니 아래 링크를 참조해 주세요.

>> BindService 의 생성과 Activity 에서의 Bind

 

728x90

댓글