티스토리 뷰

반응형

안드로이드에서 백그라운드 처리를 할 때 사용합니다.

 

서비스 만들기

New > Service > Service를 눌러 생성을 해줍니다.

 

 

서비스 코드는 아래와 같이 작성하였습니다.

class MyService1 : Service() {

    override fun onBind(intent: Intent): IBinder {
        TODO("Return the communication channel to the service.")
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

        Log.i("MyService1", "서비스 실행")
        ThreadClass().start()

        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.i("MyService1", "서비스 종료")
    }

    inner class ThreadClass : Thread() {

        override fun run() {

            for (i in 0..10) {
                Thread.sleep(1000)
                Log.i("MyService1", "MyService1 : $i")
            }
        }
    }
}

 

Thread를 사용하여 10번 반복하여 1초마다 로그를 출력해주는 코드를 작성하였습니다.

 

 

 

그리고 MainActivity에서는 아래와 같이 작성하였습니다.

start_service.setOnClickListener {
    serviceIntent = Intent(this, MyService1::class.java)
    startService(serviceIntent)
}

stop_service.setOnClickListener {
    stopService(serviceIntent)
}

각각 서비스를 실행하는 코드와 중지하는 코드를 작성하였습니다.

 

 

실행하면 아래와 같이 동작합니다.

 

 

서비스가 종료되더라도 설정한 Thread 작업은 계속 진행됩니다.

 

 

 

Intent Service 사용하기

위의 예제에서는 서비스를 만들고 안에 스레드도 만들었습니다.

Intent Service는 서비스 안에서 스레드를 사용하고자 할 때 사용합니다.

 

 

 

New > Service > Service (IntentService)를 눌러 생성합니다.

 

 

그리고 기본 코드를 제거하고 아래와 같이 세팅을 해줍니다.

 

class MyIntentService : IntentService("MyIntentService") {

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onHandleIntent(intent: Intent?) {

    }
}

 

위 예제에서는 스레드 클래스를 만들었지만 IntentService에서는 onHandleIntent에 스레드로 처리하고 싶은 코드를 작성하면 됩니다. onHandleIntent가 별도의 스레드로 동작하기 때문입니다.

 

 

서비스의 코드는 아래와 같이 작성하였습니다.

class MyIntentService : IntentService("MyIntentService") {

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.i("MyIntentService", "서비스 실행")
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.i("MyIntentService", "서비스 종료")
    }

    override fun onHandleIntent(intent: Intent?) {

        for (i in 0..10) {
            Thread.sleep(1000)
            Log.i("MyIntentService", "MyIntentService : $i")
        }
    }
}

 

서비스 실행 코드는 그대로입니다.

start_intent_service.setOnClickListener {
    serviceIntent = Intent(this, MyIntentService::class.java)
    startService(serviceIntent)
}

 

 

 

아래와 같이 동작하는 것을 확인할 수 있습니다.

 

 

 

Foreground Service 사용하기

서비스는 백그라운드에서 실행되기 때문에 메모리가 부족해지면 안드로이드 운영체제가 서비스를 제거합니다.

제거되는 것을 방지하고자 할 때는 Foreground Service를 사용하면 됩니다.

 

 

New > Service > Service를 눌러 서비스를 하나 생성해줍니다.

 

 

코드는 아래와 같이 작성하였습니다.

 

class MyService2 : Service() {

    override fun onBind(intent: Intent): IBinder {
        TODO("Return the communication channel to the service.")
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

        Log.i("MyService2", "서비스 실행")
        ThreadClass().start()

        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.i("MyService2", "서비스 종료")
    }

    inner class ThreadClass : Thread() {

        override fun run() {

            for (i in 0..10) {
                Thread.sleep(1000)
                Log.i("MyService2", "Foreground 서비스 : $i")
            }
        }
    }
}

 

안드로이드 오레오 아래 버전에서는 startService로 실행을 하면 되는데 오레오 이상부터는 startForegroundService를 사용해야 합니다.

start_foreground_service.setOnClickListener {
    serviceIntent = Intent(this, MyService2::class.java)

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        startForegroundService(serviceIntent)
    } else {
        startService(serviceIntent)
    }
}

 

 

 

실행을 하면 아래와 같이 동작합니다.

 

 

 

실행이 되다가 중간에 서비스가 강제 종료되어 버립니다.

 

startForegroundService를 사용하기 위해서는 notification을 띄어줘야 합니다.

 

아래와 같이 notification을 생성해주었습니다.

class MyService2 : Service() {

    ...

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

        val builder = createNotificationChannel("MyService2", "service")
            .setTicker("MyService2 서비스")
            .setContentTitle("MyService2 서비스 시작")
            .setContentText("MyService2 서비스 동작 중입니다.")
            .setSmallIcon(R.drawable.ic_launcher_foreground)


        startForeground(1, builder.build())

        ...
    }

    private fun createNotificationChannel(id :String, name :String) : NotificationCompat.Builder {

        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){

            val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            val channel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_HIGH)

            manager.createNotificationChannel(channel)

            NotificationCompat.Builder(this, id)
        } else {
            NotificationCompat.Builder(this)
        }
    }
    
    ...
}

 

service에서 startForeground를 설정해줘야 됩니다.

 

여기서 주의할 점은 startForeground에 id값으로 0이 들어가면 안됩니다. [ 0이 들어가면 실행되지 않습니다. ]

 

그리고 foreground를 사용하기 위해서 AndroidManifest.xml에 권한을 추가해줘야 합니다.

 

<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

 

 

이제 실행을 하면 중지 없이 동작을 하는 것을 확인할 수 있습니다.

 

 

 

반응형
댓글