ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Android Component - Service
    Android/Component 2019. 7. 24. 23:59

    Service는 장시간 background 작업 이라고 정의 할 수 있다.

    백그라운드에서 실행되면서 ContentProvider를 업데이트 시키거나 Intent를 발행 혹은 알림을 만들어 내는 역할을 한다.

     

    눈에 보이는 인터페이스 없이 다른 서비스, 액티비티 그리고 브로드케이스트 리시버 수신자를 포함한 다른 어플리케이션 컴포넌트로 부터 시작 중지를 한다.

     

    시작된 서비스는 비활성 액티비티 나 눈에 보이지 않는 액티비티 보다 높은 우선 순위를 받아 런타임의 리소스 관리에 의해 종료될 가능성을 낮춘다.


    컴포넌트의 우선순위는 아래의 순서와 같다.
        (1) 활성 상태 액티비티
        (2) pause 상태 액티비티
        (3) 움직이고 있는 서비스
        (4) 비활성 상태 액티비티
        (5) 시작 했지만 아무짓도 안하는 서비스

    규칙적으로 뭔가를 업데이트 하지만 사용자 상호작용이 거의 드물거나 산헐적으로 필요한 어플리케이션은 서비스로 구현하기 좋은 후보이다. 예를 들면 아래와 같다. 
                - MP3 플레이어
                - RSS/Atom 피드 수집 서비스
                - 메신저 채팅 서비스

    서비스 사용시 주의 사항

    서비스는 눈에 보이지 않는 상태에서 수행하게 됨으로 액티비티 보다 작성시 더 많은 주의를 기울여야 한다.

    만약 자원을 너무 심하게 사용하거나 방비하면 시스템에 문제가 생기거나 베터리 사용시간이 급격하게 줄어들 수 있다.
    또한 실제 서비스를 구현할때의 자바 코드 스타일도 영향이 있다.

    코드 스타일의 영향을 줄이려면 객체 생성 줄이기, 비용이 적은 for문 임시변수 사용 줄이기 등등...

     

    구글의 추천은 서비스를 사용하지 않고서 개발이 가능하다면 최대한 사용하지 마라! 라고 하였었다.


    1. 서비스 구동방법

     

    서비스 구동방법은 NormalService와 BindService가 있다.

    두개의 가장 큰 차이는 라이프사이클에 있다.

     

    1-1. StartService

    실행
    val intent = Intent(this,Service::class.java) 
    startService(intent) 
    
    클래스 선언
    class MyService : Service() { 
        onCreate() 
        onStartCommand() 
        // 여기 까지가 서비스가 실행된 상태 
        onDestroy() 
    } 
    
    종료
    stopService(intent)

    StartService는 유일하게 종료 이벤트를 날릴 수 있는것이 서비스이기 때문에 아래의 코드를 통해 아무 곳에서나 서비스를 종료 할 수 있다.

     

    라이프사이클 : onCreate -> onStartCommand -> onDestroys

     

    1-2. BindService

    실행
    val intent = Intent(this,Service::class.java) 
    bindService(intent) 
    
    종료
    unbindService(intent) 
    
    클래스 구현
    class MyService : Service() { 
        onCreate() 
        onBind() 
        // 여기 까지가 서비스가 실행된 상태 
        unBind() 
        onDestroy() 
    } 

    BindService 를 사용하게 될 경우 아래와 같이 라이프 사이클이 바뀐다.
    라이브사이클 : onCreate -> onBind:IBinder -> unBind -> onDestroy

    바인드 서비스의 경우 특이하게 리턴값을 가지고 있는데 결과 값을 바인딩 받기 위해서 사용이 된다.

    보통 결과값은 Inner class로 Binder를 상속받아 사용한다.
    inner class B : Binder

    리턴값을 받을려면 ServiceConnection 인터페이스를 구현한 객체를 넣어주면 된다.
    bindService에 의한 리턴값으로는 받을 수 없다.

     

    1-2-1. BindService 구현하기

    class MyBindService : Service() {
    
        fun getMessage() = "MyBindService Data"
    
        override fun onBind(intent: Intent): IBinder {
            Log.d("TAG", "onBind")
            return MyBinder()
        }
    
        override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
            Log.d("TAG", "서비스 시작")
            return super.onStartCommand(intent, flags, startId)
        }
    
        override fun onDestroy() {
            Log.d("TAG", "서비스 종료")
            super.onDestroy()
        }
    
        inner class MyBinder : Binder() {
            fun getService(): MyBindService {
                return this@MyBindService
            }
        }
    
    }

    1-2-2. ServiceConnection으로 데이터 받아오기

    class MainActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            val intent = Intent(this, MyBroadcastReceiver::class.java)
    
            val serviceConnection = object : ServiceConnection {
                override fun onServiceDisconnected(componentName: ComponentName) {
                }
    
                override fun onServiceConnected(componentName: ComponentName, iBinder: IBinder) {
                    val binder = iBinder as MyBindService.MyBinder
                    binder.getService().getMessage()
                }
    
            }
    
        }
    
    }


    그러면 여기서 궁금증! Intent의 Service의 객체가 Binding된 객체를 가지고

     

    Q1. 만약 서비스를 중복으로 실행 시키면 어떻게 될 것인가?
                    

    val intent = Intent(this,Service::class.java) 
    startService(intent) 
    startService(intent) 

     

    액티비티의 경우 절대적으로 싱글턴이 생성되지 않기 때문에 아래와 같이 생성되며 테스크 관리가 필요해진다.

    결과 : Intent -> Activity(A#1) [new] -> Intent -> Activity(A#2) [new]

    서비스의 경우 무조건 싱글턴으로 작동하고 객체는 생성되지 않고 onStartCommand 함수만 재실행 된다.
    그리고 onStartCommand는 반복 호출 가능성이 있음으로 쓰레드 사용시 유의하면서 사용하여야 한다. 


    쓰레드가 반복 가능성이 없다 : onCreate 
    쓰레드가 반복 가능성이 있다 : onStartCommand 

     

    결과 : Intent -> Service(A#1) -> Service(A#1)

    Q2. 그러면 startService, bindService 무엇을 사용 하여야 하는가? 

    만약 서비스 내에 데이터가 실시간으로 발생할 경우 서비스를 구동시킨 곳에 데이터를 넘길 필요가 없다면 편하다! 

     

    2-1. 각 Service의 정보와 장단점


    (1) bindService 
    상대방이 나에게 카톡을 보냄 : Network -> Service -> Activity 
    내가 그 카톡에 답장을 보냄 : Activity -> Service -> Network 

    장점

    데이터를 주고 받을때 객체가 생성 소멸이 되지 않는다. 

     

    단점

    메인 컴포넌트 사상에 위배 된다. 데이터 결합이 일어나기 떄문이다.
    액티비티에서 백버튼을 누르면 Destroy된다. 즉, 액티비티 라이프 사이클을 따라서 죽는다. 
    이를 방지하기 위해서는 코드의 양이 늘어난다. 

    (2) startService 
    액티비티의 Inner Class로 브로드케스트 리시버를 만든다.
    서비스에서 데이터를 날릴때 브로드케스트 인텐트를 발생!  
    인텐트의 Extra 를 통해 서비스에서 브로드케스트로 보낸다.

    상대방이 나에게 카톡을 보냄 : Network -> Service -> Receiver -> Activity 
    내가 그 카톡에 답장을 보냄 : Activity -> Receiver -> Service -> Network 

    장점

    메인 컴포넌트 사상에 위배되지 않고 데이터 결합이 일어나지 않는다. 

     

    단점

    데이터를 주고 받을때 마다 객체가 생성 소멸이 된다. 

    2-2. 선정 기준
    - 서비스가 발생한 데이터를 다른 컴포넌트에 넘기지 않는다면 그냥 서비스 
    - 서비스가 발생한 데이터를 다른 컴포넌트에 넘기거나 서로 교환을 한다면 아래의 기준에 따라 나눔 

    (1) 데이터 상호작용 빈번도 [ 빈번도 기준 : 초당 수십번 ] 

    빈번도가 낮으면 startService 
    빈번도가 높으면 bindService 

    (2) 서비스 또는 이용자 중심 

    서비스 중심 : startService ( 그냥 필요 하든 말든 넘기고 보자 ) 
    이용자 중심 : bindService ( 필요한 순간에만 주고 받게 하자 ) 

    (3) 보안성 

    보안성 취약 : startService = 공개된 Intent 를 사용함으로 보안상 취약 
    보안성 강화 : bindService = 앱만의 API를 사용함으로 보안이 높다


    IntentService 

     

    IntentService 는 백그라운드에서 수행되며 로직 수행이 끝나면 자동 종료되는 특수 Service이다.

    일반 Service처럼 Intent에 의해 수행되기는 하지만 stopService 명령이 없어도 Service의 로직 수행이 끝나면 
    자동 종료되는 특징을 가지고 있다. 

    일반 Service와 다른점은 onHandleIntent가 필수이며 이 함수에 백그라운드에서 수행할 로직을 정의하게 된다. 
    이 함수의 로직이 수행이 완료되면 자동종료 

    class A : IntentService {  
        onCreate()  
        onStartCommand()  
        onHandleIntent() // Developer Main Algorithm  
        onDestroy()  
    }  

    동시 다발성으로 중복 실행을 시킬 경우 순차적으로 실행한다. 

    - Service와 동일하게 싱글턴이다
    - 첫번째 실행자가 시작되고 있을때 그 뒤의 실행자가 있을 경우 블러킹 한다 
    - 첫번째 실행자가 종료되었을때 그 뒤의 실행자가 있을 경우 종료하지 않고 뒤의 실행자를 실행한다. 

    ex) work1 -> work2 -> work3 -> Destroy 

    어디에 사용할까?

    - 특정 업무를 수행 후 자동 종료됨으로 장시간 동안 화면과 상관없이 지속적으로 살아 있는 서비스를 구현해야 하는 곳에서는 일반 서비스로 구현. 
    - 일정 정보의 시간 동안 순차적으로 수행되는 곳에서 사용하면 됨으로 AsyncTask의 대체용으로도 사용이 가능 

    장시간     = Service 
    특정시간   = IntentService

    'Android > Component' 카테고리의 다른 글

    Android Notification  (0) 2019.07.25
    Android JobSchduler  (0) 2019.07.25
    Android Background Limits  (0) 2019.07.25
    Android Component - Broadcast Service  (0) 2019.07.24

    댓글

Designed by Tistory.