GDSC HUFS 3기/Android with Kotlin Team 1
[1팀] Android 서비스와 포어그라운드 서비스
이승민👨💻
2021. 12. 6. 02:49
이 글은 이것이 안드로이드다 with 코틀린(개정판)를 참고하여 작성하였습니다.
작성자 : 이승민
개발환경은 Windows, Android Studio입니다.
1. 서비스
- 안드로이드의 4대 컴포넌트 중 하나
- 사용자와의 상호작용 없이 백그라운드에서 수행되기 때문에, Activity와 달리 화면이 없다. (UI가 없다)
- Activity와 동일하게 메인 쓰레드에서 실행된다.
서비스의 유형
Foreground Service, Background Service, Bound Service
Foreground Service
- 디바이스 화면에서 어떤 서비스가 작동하는지 사용자가 직접 확인 가능한 Service
- 반드시 알림(Notification)을 표시해야만 한다.
- 어떤 앱이 백그라운드에서 실행되고 있으면 사용자는 알지 못한다 → 앱이 오래 사용되면 시스템 자원을 많이 사용하여 전력 소모가 크다
- 서비스가 돌아가고 있다는 것을 사용자에게 알려서 주의를 주기 위해서 장시간 동작하는 서비스들은 포어그라운드 서비스로 만들게끔 구글이 강제한다.
- 시스템 사용 정도나 자원의 여유를 따져서 안드로이드가 강제로 서비스를 가장 먼저 죽여버린다.
ex) 음악 재생, 파일 다운로드와 같은 서비스
Background Service
- 사용자에게 직접적으로 보이지 않는 작업을 수행
ex) 게임 업데이트 중 화면을 나가도 업데이트 진행되는 상태
Bound Service
- 컴포넌트들 중 하나가 bindService()를 호출하여 해당 서비스에 바인딩되는 서비스 유형
- 클라이언트-서버 인터페이스를 제공하여 컴포넌트가 서비스와 상호작용하게 하며, 결과를 받을 수도 있다.
- 다른 컴포넌트가 바인딩 되어있는 경우에만 실행
서비스 생성 방법
- 패키지명 우클릭 → New → Service → Service
activity_main 레이아웃 설정
- 4개의 버튼을 만들어 서비스 스타트, 스탑, 바인드, 언바인드 버튼으로 설정
MyService.kt 코드
package com.example.service
import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.IBinder
import android.util.Log
class MyService : Service() {
companion object {
val ACTION_CREATE = "create"
val ACTION_DELETE = "delete"
}
inner class MyBinder: Binder() {
fun getService():MyService {
return this@MyService
}
}
//bindService()로 바인딩을 실행할 때 호출. IBinder 반환
override fun onBind(intent: Intent): IBinder {
return MyBinder()
}
//startService()로 서비스를 시작할 때 호출됨
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val action = intent?.action
when(action) {
ACTION_CREATE -> create()
ACTION_DELETE -> delete()
}
return super.onStartCommand(intent, flags, startId)
}
fun create() {
Log.d("서비스", "create()가 호출됨")
}
fun delete() {
Log.d("서비스", "delete()가 호출됨")
}
}
MainActivity.kt 코드
package com.example.service
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.IBinder
import android.view.View
class MainActivity : AppCompatActivity() {
private lateinit var serviceIntent:Intent
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
serviceIntent = Intent(this, MyService::class.java)
}
fun serviceStart(view: View) {
serviceIntent.action = MyService.ACTION_CREATE
startService(intent)
}
fun serviceStop(view: View) {
stopService(intent)
}
//binding 코드
var myService: MyService? = null
var isService = false
val connection = object: ServiceConnection {
// Ctrl + i로 2개 구현
// 서비스에 연결되었을 때
override fun onServiceConnected(p0: ComponentName?, iBinder: IBinder?) {
isService = true
val binder = iBinder as MyService.MyBinder
myService = binder.getService()
}
// 서비스 연결이 해제될 때
override fun onServiceDisconnected(p0: ComponentName?) {
isService = false
}
}
fun serviceBind(vies: View) {
bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
fun serviceCommand() {
myService?.create()
myService?.delete()
}
fun serviceUnbind(view: View) {
}
}
2. 포어그라운드 서비스
manifest에 포어그라운드 서비스 퍼미션 추가
- 서비스와 다르게 포어그라운드 서비스가 띄우는 알림이 채널을 사용
- 해당 채널로 알림이 오면 그 채널에서 사용할 수 있게끔 한다.
activity_main 레이아웃 설정
Foreground.kt 코드
package com.example.foregroundservice
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat
import kotlin.concurrent.thread
class Foreground : Service() {
val CHANNEL_ID = "FGS153"
val NOTI_ID = 153
// 채널 생성
fun createNotificationChannel() {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val serviceChannel = NotificationChannel(CHANNEL_ID, "FOREGROUND", NotificationManager.IMPORTANCE_DEFAULT)
val manager = getSystemService(NotificationManager::class.java)
manager.createNotificationChannel(serviceChannel)
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
createNotificationChannel()
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setSmallIcon(R.mipmap.ic_launcher_round)
.build()
startForeground(NOTI_ID, notification)
runBackground()
return super.onStartCommand(intent, flags, startId)
}
fun runBackground() {
thread(start=true) {
for( i in 0..100) {
Thread.sleep(1000)
Log.d("서비스", "Count===>$i")
}
}
}
override fun onBind(intent: Intent): IBinder {
return Binder()
}
}
MainActivity.kt 코드
package com.example.foregroundservice
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import androidx.core.content.ContextCompat
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun serviceStart(view: View) {
val intent = Intent(this, Foreground::class.java)
ContextCompat.startForegroundService(this, intent)
}
fun serviceStop(view: View) {
val intent = Intent(this, Foreground::class.java)
stopService(intent)
}
}