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()를 호출하여 해당 서비스에 바인딩되는 서비스 유형
  • 클라이언트-서버 인터페이스를 제공하여 컴포넌트가 서비스와 상호작용하게 하며, 결과를 받을 수도 있다.
  • 다른 컴포넌트가 바인딩 되어있는 경우에만 실행

 

안드로이드의 4대 컴포넌트

서비스 생성 방법

  • 패키지명 우클릭 → 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)
    }
}

Foreground Service 알림이 생긴 것을 볼 수 있다.