서비스
안드로이드 4대 컴포넌트 중 하나로 사용자가 직접 상호작용하지 않고 백그라운드에서 실행되는 컴포넌트
특징
1. 백그라운드라 다른 스레드이라고 생각하기 쉽지만 메인 스레드에서 작동하기 때문에 '화면이 없는 액티비티'라고 생각하면 편함
2. '메인 스레드에서의 작동' 이라는 특징 때문에 기존 백그라운드 처리와 다른 개념으로 접근해야 함
=> 별도의 스레드를 생성하는 방식으로 처리
서비스 실행 방식
크게 스타티드 서비스, 바운드로 구분 됨
스타티드 서비스
startService() 메서드를 호출하여 액티비티와 상관없이 독립적으로 동작할 때 사용함
이 때문에 일반적으로 백그라운드에서 많이 사용하는 방식
이미 백그라운드 서비스가 동작 중인 상태에서 서비스의 재시작을 요청할 경우 새로 만들지 않고, 생성되어 있는 서비스의 메서드를 호출합니다.
바운드 서비스
bindService() 메서드를 호출하여 액티비티와 값을 주고받을 필요가 있을 때 사용함
bindService() 가 호출되면 호출한 액티비티는 해당 서비스에 바인딩되고 바인딩된 서비스는 인터페이스를 제공하여 액티비티가 바인딩된 서비스와 값을 주고 받을 수 있도록 하게 함
여러 개의 액티비티와 같은 서비스를 사용할 수 있기 때문에 기존에 생성되어 있는 서비스를 바인딩해서 재사용함.
서비스 구조 방식
백그라운드 서비스
기본적으로 모든 서비스는 백그라운드 방식
액티비티와 상관없이 독립적으로 동작
일반적으로 안드로이드 서비스에서 말하는 백그라운드는 화면에 나타나지 않는다는 의미로
다른 스레드에서 작동한다는 말은 아니다!
포어그라운드 서비스
사용자에게 알림을 통해 현재 작업이 진행 중이라는 것을 알리는 서비스
백그라운드는 앱이 꺼지거나 자원이 부족하면 시스템에 의해 종료되지만, 포어그라운드 서비스는 사용자에게 진행 중인 것을 알리고 있기 때문에 위와 같은 이유로 종료되지 않음
백그라운드 서비스(스타티드 서비스) 구현
1. BackGround.kt 만들기
BackGround.kt
class BackGround : Service() {
override fun onBind(intent: Intent): IBinder {
TODO("Return the communication channel to the service.")
}
}
AndroidManifest.xml
<service
android:name=".BackGround"
android:enabled="true"
android:exported="true" />
이미지 1와 같이 Service 를 만드면 서비스 구성에 필요한 기본적인 코드들(AndroidManifest.xml 포함)이 작성됩니다.
BackGround.kt
class BackGround : Service() {
private var startId: Int = 0
override fun onBind(p0: Intent?): IBinder? {
TODO("Not yet implemented")
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
this.startId = startId
Log.d("background", "Service ${startId} starts")
Handler(Looper.getMainLooper()).postDelayed({
stopSelf()
}, 3000)
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
Log.d("background", "Service ${startId} stops")
super.onDestroy()
}
}
처음 서비스가 실행되면 onStartCommand() 메소드가 실행됩니다.
onStartCommand()는 startId를 저장하고 딜레이 이후 서비스를 자동 종료하도록 코드를 작성하였습니다.
서비스가 종료될 떈 onDestory() 메소드가 실행됩니다. 실행됐는지 확인하기 위해 로그를 남겨두었습니다.
2. 서비스 실행하기
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val intent = Intent(this, BackGround::class.java)
startService(intent)
}
}
서비스를 실행할 땐 startService 메소드를 사용합니다.
3. 결과
포어그라운드 서비스 구현
서비스가 먼저 실행되고 그 서비스 내에서 startForeground()가 실행되어야 합니다.
1. 권한 명세
AndroidManifest.xml
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
2. Foreground.kt 구현 (서비스 구현)
package com.example.myapplication2
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Intent
import android.os.Build
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.util.Log
import androidx.core.app.NotificationCompat
class ForeGround : Service() {
private var startId: Int = 0
override fun onBind(intent: Intent): IBinder {
TODO("Return the communication channel to the service.")
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
this.startId = startId
Log.d("foreground", "Service ${startId} starts")
val notificationIntent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this,
0,
notificationIntent,
PendingIntent.FLAG_IMMUTABLE
)
createNotificationChannel()
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service ${startId}")
.setSmallIcon(R.mipmap.ic_launcher_round)
.setContentIntent(pendingIntent)
.build()
startForeground(startId, notification)
Handler(Looper.getMainLooper()).postDelayed({
stopSelf()
}, 6000)
return super.onStartCommand(intent, flags, startId)
}
private fun createNotificationChannel(){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
val serviceChannel = NotificationChannel(
CHANNEL_ID,
"Foreground Service Channel ${startId}",
NotificationManager.IMPORTANCE_DEFAULT
)
val manager = getSystemService(NotificationManager::class.java)
manager.createNotificationChannel(serviceChannel)
}
}
override fun onDestroy() {
Log.d("foreground", "Service ${startId} stops")
super.onDestroy()
}
companion object{
val CHANNEL_ID = "FOREGROUND CHANNEL"
}
}
포어그라운드는 서비스 내에서 startForeground() 메소드를 사용해 포어그라운드를 실행합니다.
서비스는 알람을 생성하여 사용자에게 포어그라운드가 실행되고 있음을 알려주고
6초 뒤 자동으로 종료됩니다.
3. 서비스 실행하기
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val intent = Intent(this, ForeGround::class.java)
ContextCompat.startForegroundService(this,intent)
}
}
4. 결과
LogCat
에뮬레이터
'Android' 카테고리의 다른 글
[안드로이드]CameraX(Preview, ImageCapture) (0) | 2023.08.06 |
---|---|
[안드로이드] CustomTextView (0) | 2023.07.30 |
[안드로이드] 콘텐츠 제공자(ContentProvider) 와 콘텐츠 리졸버(ContentResolver) (0) | 2023.07.16 |
[안드로이드]FragmentManager (0) | 2023.07.09 |
[안드로이드]DataStore에 대하여 (0) | 2023.07.02 |