안드로이드 개발자의 창고
[39일차 Android] Service 본문
출처 : 안드로이드 앱스쿨 2기 윤재성 강사님 수업 PPT
📖 Service
- 안드로이드 4대 구성 요소 중 하나로 백그라운드 처리를 위해 제공되는 요소
- Activity는 화면을 가지고 있어 화면이 보이는 동안 동작하지만 Service는 화면을 가지고 있지 않아 보이지 않는 동안에도 동작하는 것을 의미한다.
Forground Service
- Service는 백그라운드에서 운영되는 실행 요소로써 메모리가 부족해지거나 절전 모드가 되는 등 다양한 상황에서 안드로이드 OS에 의해 제거 될 수 있다.
- 이를 방지하고자 할 때는 Foreground Service로 만들어 사용하면 된다.
- Foreground Service외의 서비스는 안드로이드 OS에 의해 모두 제거될 수 있다.
- Foreground Service의 목적은 현재 단말기에서 Service를 통해 백그라운드에서 작업 중이라는 것을 사용자에게 알리는 목적으로 사용한다.
예제 코드
TestService.kt
val NOTIFICATION_CHANNEL1_ID = "Service"
val NOTIFICATION_CHANNEL1_NAME = "Service"
var isRunning = false
var value = 0
// Activity가 서비스에 접속하면 전달될 바인더 객체
val binder = LocalBinder()
// 외부에서 서비스에 접속하면 자동으로 호출되는 메서드
// 여기에서는 바인더 객체를 반환한다.
override fun onBind(intent: Intent): IBinder {
return binder
}
// 서비스가 가동되면 자동으로 호출되는 메서드
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
addNotificationChannel(NOTIFICATION_CHANNEL1_ID, NOTIFICATION_CHANNEL1_NAME)
// 안드로이드 버전 확인
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
val builder = getNotificationBuilder(NOTIFICATION_CHANNEL1_ID)
builder.setSmallIcon(android.R.drawable.ic_menu_search)
builder.setContentTitle("서비스 가동")
builder.setContentText("서비스가 가동 중입니다")
val notification = builder.build()
startForeground(10, notification)
}
isRunning = true
thread {
while(isRunning == true){
SystemClock.sleep(500)
val now = System.currentTimeMillis()
Log.d("now", "now : $now")
value++
}
}
return super.onStartCommand(intent, flags, startId)
}
- 안드로이드 버전 8.0 이상인 에뮬레이터에서 확인할 수 있도록 Notification을 발생시킨다.
- Thread를 발생시켜 현재 시간을 계속 LogCat에 출력한다.
// 서비스가 중지되면 호출되는 메서드
override fun onDestroy() {
super.onDestroy()
// 쓰래드의 while문 중단을 위해
isRunning = false
}
- 위에서 발생시킨 Thread의 while문을 멈추기 위해 isRunning의 값을 false로 설정해준다.
// Notification Channel을 등록하는 메서드
fun addNotificationChannel(id:String, name:String){
// 안드로이드 8.0 이상일 때만 동작하게 한다.
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
// 알림 메시지를 관리하는 객체를 추출한다.
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
// id를 통해 NotificationChannel 객체를 추출한다.
// 채널이 등록된 적이 없다면 null을 반환한다.
val channel = notificationManager.getNotificationChannel(id)
// 채널이 등록된 적이 없다면...
if(channel == null){
// 채널 객체를 생성한다.
val newChannel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_HIGH)
// 단말기에 LED 램프가 있다면 램프를 사용하도록 설정한다.
newChannel.enableLights(true)
// LED 램프의 색상을 설정한다.
newChannel.lightColor = Color.RED
// 진동을 사용할 것인가?
newChannel.enableVibration(true)
// 채널을 등록한다.
notificationManager.createNotificationChannel(newChannel)
}
}
}
// Notification 메시지 관리 객체를 생성하는 메서드
// Notification 채널 id를 받는다.
fun getNotificationBuilder(id:String) : NotificationCompat.Builder{
// 안드로이드 8.0 이상이라면
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
val builder = NotificationCompat.Builder(this, id)
return builder
} else {
val builder = NotificationCompat.Builder(this)
return builder
}
}
// 접속하는 Activity에서 서비스를 추출히기 위해 사용하는 객체
inner class LocalBinder : Binder(){
public fun getService() : TestService{
return this@TestService
}
}
// 변수의 값을 반환하는 메서드
public fun getNumber():Int{
return value
}
MainActivity.kt
lateinit var activityMainBinding: ActivityMainBinding
// 서비스를 가동시키기 위해 사용할 인텐트
lateinit var serviceIntent2:Intent
// 동작중인 서비스 객체를 담을 변수
var ipcService:TestService? = null
// 서비스 접속을 관리하는 객체
lateinit var serviceConnectionClass:ServiceConnectionClass
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(activityMainBinding.root)
activityMainBinding.run{
button.setOnClickListener {
// 현재 서비스가 실행중인지 파악한다.
val chk = isServiceRunning("com.test.android54_service.TestService")
serviceIntent2 = Intent(this@MainActivity, TestService::class.java)
// 서비스가 가동중이 아니라면 서비스를 가동한다.
if(chk == false) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(serviceIntent2)
} else {
startService(serviceIntent2)
}
}
// 서비스에 접속한다.
serviceConnectionClass = ServiceConnectionClass()
bindService(serviceIntent2, serviceConnectionClass, BIND_AUTO_CREATE)
}
button2.setOnClickListener {
stopService(serviceIntent2)
}
button3.setOnClickListener {
// 서비스에서 데이터를 가져온다.
val value = ipcService?.getNumber()
activityMainBinding.textView.text = "value : $value"
}
}
}
- button
- 서비스가 가동 중인지를 확인하여 중복으로 가동되지 않도록 한다.
- 이를 확인하지 않는다면 서비스가 가동될 때마다 TestSerrvice의 onStartCommand() 메서드가 계속 실행되므로 Notification과 Thread가 추가로 실행된다.
- 서비스가 가동 중인 것을 확인한 후 서비스에 접속한다.
- button2
- 서비스를 중지시킨다.
- TestService의 onDestroy()가 실행되어 Thread와 서비스가 중지된다.
- button3
- 서비스에 접근하여 서비스에서 데이터를 가져온다.
- TestService의 Thread에서 LogCat에 출력하고 있는 현재 시간의 개수(value)를 가져온다.
// 서비스가 가동중인지 확인하는 메서드
fun isServiceRunning(name:String) : Boolean{
// 서비스 관리자를 추출한다.
val activityManager = getSystemService(ACTIVITY_SERVICE) as ActivityManager
// 현재 실행중인 모든 서비스를 가져온다.
val serviceList = activityManager.getRunningServices(Int.MAX_VALUE)
// 가져온 서비스의 수 만큼 반복한다.
for(serviceInfo in serviceList){
// 현재의 서비스의 클래스 이름이 매개변수로 전달된 클래스 이름과 동일한지...
if(serviceInfo.service.className == name){
return true
}
}
return false
}
- button에서 현재 서비스가 실행 중인지 파악할 때 사용하는 메서드
- 모든 서비스를 가져와 넘겨준 서비스의 이름과 같은게 있다면 true를 반환하고 없으면 false를 반환한다.
- 즉 넘겨준 이름의 서비스가 실행 중이면 true, 실행 중이지 않으면 false를 반환한다.
// Activity의 서비스 접속을 관리하는 클래스
inner class ServiceConnectionClass : ServiceConnection{
// 서비스 접속이 성공했을 경우
// 두 번째 매개변수 : 서비스의 onBind 메서드가 반환하는 객체
override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) {
// 서비스를 추출한다.
val binder = p1 as TestService.LocalBinder
ipcService = binder?.getService()
}
// 서비스 접속이 해제 되었을 때
override fun onServiceDisconnected(p0: ComponentName?) {
ipcService = null
}
}
- button에서 서비스에 접속할 때 사용되는 클래스
- 서비스 접속에 성공하면 TestService의 onBind() 메서드를 통해 binder 객체를 받아 서비스를 추출한다.
- 서비스 접속이 해제되면 null을 반환한다.
- 이 값은 button의 bindService에 담겨진다.
override fun onDestroy() {
super.onDestroy()
// 서비스가 접속 중이라면 접속을 해제한다.
if(::serviceConnectionClass.isInitialized == true){
unbindService(serviceConnectionClass)
}
}
'Computer > Android' 카테고리의 다른 글
[41일차 Android] ActionBar (0) | 2023.07.01 |
---|---|
[39일차 Android] Fargment (0) | 2023.07.01 |
[39일차 Android] Broadcast Receiver (0) | 2023.06.27 |
[39일차 Android] Thread, runOnUiThraad (0) | 2023.06.26 |
[38일차 Android] 다양한 Notification (0) | 2023.06.25 |