[Android] Service 사용법

2014. 12. 27. 17:07Basic/etc

반응형

안드로이드/Android Service 사용법




안녕하세요. 오늘 배워볼 내용은 안드로이드 Application을 구성하는 4가지 컴포넌트 중에 하나인 Service에 대해 공부해 보겠습니다. 일단 안드로이드 Application을 구성하는 4가지 컴포넌트 들은 Application을 만들 때 사용하는 핵심 부품 4가지 라고 생각하시면 됩니다.


보통 우리가 컴퓨터 본체를 조립하게 되면, 부품들을 크게 메인보드, CPU, RAM, 하드디스크 등으로 분류 할 수 있는데요, 마찬가지로 안드로이드 Application을 컴퓨터 본체라고 생각하시면 그 컴퓨터를 조립 할 때 동작을 할 수 있게 하는 4가지 부품(컴포넌트)이라고 생각하시면 됩니다. 


물론 안드로이드 Application은 Activity 하나만으로 구성 할 수 있기 때문에, 모든 부품이 다 있어야 동작하는 컴퓨터 본체와는 다르지만, 개념적으로 Application을 만들 때 사용하는 4가지 부품이라고만 생각하시면 됩니다.

<안드로이드 Application을 구성하는 4가지 컴포넌트>



자 그러면, Service에 대해 알아보겠습니다. 아래의 그림에서 빨간색 중요 표시가 되어 있는 컴포넌트에 대한 설명입니다.



 1. Service란 무엇인가?

Service는 안드로이드 Application을 구성하는 4가지 컴포넌트 중에 하나이며, Activity처럼 사용자와 상호작용 하는 컴포넌트가 아니고, Background(화면뒷단)에서 동작하는 컴포넌트를 말합니다.



 2. Service는 왜 필요한가?

자 그러면, Service는 왜 필요한 걸까요? 네 그렇습니다. Activity 화면에서 동작뿐만 아니라, Activity가 종료되어 있는 상태에서도 동작하기 위해서 만들어진 컴포넌트 입니다.

MP3 플레이어 같은 기능을 활용할 때 말이죠. MP3 플레이어는 화면이 종료된 상태에서도 계속 노래를 재생해야 하기 때문에 입니다.

만약 Service가 실행되고 있는 상태라면, 안드로이드 OS 에서는 해당 Process를 왠만하면 죽이지 않도록 방지하고 관리하게 됩니다. 그렇기 때문에 메모리 부족이나, 특별한 경우를 제외하고는 Background 동작을 수행하도록 설계 되었습니다.



 3. Service 사용방법

Service의 종류에는 2가지가 있습니다. 1번째는 startService()를 이용한 방법이고, 2번째 방법은 bindService()를 이용하는 방법 입니다. 2가지의 차이점에 대해서는 나중에 얘기를 해보도록 하구요. 이번 포스팅 에서는 1번째 방법만 설명 하도록 하겠습니다.


Service 실행 : startService(Intent Serivce) 메서드 호출.


Service 중지 : stopService(Intent Serivce) 메서드 호출.



 4. Service 사용시 주의사항

 4.1. Service의 ANR 발생

Android는 Linux 기반의 프로그램 입니다. 프레임웍 단에는 Linux로 구현되어 있습니다. 메모리 관리 또한 Linux Kernel에서 해주게 되는데요.





하나의 프로세스를 자세히 살펴보면, 아래와 같은 구조를 가지고 있습니다.


여기서 주의 해야 할 점은, 모든 컴포넌트들이 Main Thread 안에 실행된다는 점 입니다. Main Thread는 앞서 Thread의 예제에서 살펴봤듯이, UI 작업을 처리해주는 Thread 입니다. Service 역시 Main Thread에서 관리하는 녀석이므로, Thread 작업이 필요한 경우, 작업Thread를 생성해서 관리해줘야 한다는 점 입니다.


아니면 ANR이 발생하여, 종료시켜 버리게 됩니다. 그렇기 때문에 Service사용시에도 Thread작업이 필요할 경우에는, 별도의 작업Thread를 만들어서 사용해야 합니다. 이점 유의해 주시기 바랍니다.



 4.2. Service의 실행 중에 startService() 호출

Service 실행중에 startService() 메서드를 호출하게 되면 어떤 현상이 발생 할까요? 일반적으로 Service가 무한정 생성될거 같은데요, Service 실행도중에 startService() 메서드를 실행하게 되면 Service의 onStartCommand() 메서드를 호출 하게 합니다.


Service 주기는 onCreate() -> onStartCommand() 순으로 이뤄 지는데요, Service가 실행되고 있는 도중에 다시한번  startService()  메서드가 호출되면 onStartCommand() 주기 부터 실행하게 됩니다. 마치 Activity의 onResume() 처럼 말이죠. 그렇기 때문에 중요한 작업에 대해서는 onCreate() 보다는 onStartCommand() 메서드에 구현을 해줘야 합니다.



안드로이드 2.0 이하 버전의 Service의 주기는 onCreate() -> onStart() 구조 였지만, 2.0 이상 부터는 Service의 문제점과 기능을 보완하여 onStart()메서드 대신 onStartCommand() 메서드 사용을 권장하고 있습니다. 현재 onStart() 메서드를 사용해도 상관은 없지만 구글에서 권장하는 onStartCommand() 메서드를 사용하는게 실제 사용시 좀 더 다양한 기능을 사용 할 수 있습니다.



[기존 Service Life cycle]


[2.0 이후의 Service Life cycle]




 5. Service onStartCommand() 사용방법

Service는 기본적으로 프로세스에 의해 종료가 되더라도 다시 살아나는 구조를 가지고 있습니다. Service의 종료메서드인 stopService() 메서드를 호출 하기 전까지는 말이죠.


프로세스에 의해 종료된 Service는 onCreate() -> onStartCommand() 순으로 주기를 타게 됩니다. startService() 메서드를 호출하지 않았기 때문에 정상적으로 동작 하는 것이지요.


onStartCommand() 메서드는 3가지 리턴 타입을 갖게 되는데요.

 
 START_STICKY : Service가 강제 종료되었을 경우 시스템이 다시 Service를 재시작 시켜 주지만 intent 값을 null로 초기화 시켜서 재시작 합니다. 

 Service 실행시 startService(Intent service) 메서드를 호출 하는데 onStartCommand(Intent intent, int flags, int startId) 메서드에 intent로 value를 넘겨 줄 수 있습니다. 기존에 intent에 value값이 설정이 되있다고 하더라도 Service 재시작시 intent 값이 null로 초기화 되서 재시작 됩니다. 

 START_NOT_STICKY : 이 Flag를 리턴해 주시면, 강제로 종료 된 Service가 재시작 하지 않습니다. 시스템에 의해 강제 종료되어도 괸찮은 작업을 진행 할 때 사용해 주시면 됩니다. 

 START_REDELIVER_INTENT : START_STICKY와 마찬가지로 Service가 종료 되었을 경우 시스템이 다시 Service를 재시작 시켜 주지만 intent 값을 그대로 유지 시켜 줍니다. startService() 메서드 호출시 Intent value값을 사용한 경우라면 해당 Flag를 사용해서 리턴값을 설정해 주면 됩니다.




일단 기본적인 리턴방법에 대해 알아봤습니다. 그런데 보통 Service Class를 상속받고 onStartCommand() 메서드를 오버라이드 하면 아래와 같은 형태의 메서드가 기본 형태인데요. 위에서 알아본 3가지 리턴방식과는 다른 모양 입니다.



음 이건 몰까요? 이것 역시 START_STICKY 과 동일한 리턴 타입 입니다. START_STICKY 플래그(FLAG)의 값은 상수 1이고 super.onStartCommand(intent, flags, startId) 역시 상수 1을 리턴 합니다. 그렇기 때문에 START_STICKY 와 동일게 동작 합니다.




 6. Service의 종류 

Service의 종류가 2가지가 있다고 위에서 잠깐 언급했었는데요. 2가지 방식의 Service를 살펴 보자면 첫번째로 지금 까지 설명했던 1번 startService() 메서드에 의한 실행방법이 있습니다. 이 Service는 현재 실행중인 프로세스안에서의 동작을 하게 되는 Service 입니다.


이 처럼 하나의 프로세스 안에서 해당 패키지 내의 다른 컴포넌트들과의 유기적으로 통신하는 어플리케이션의 역할을 하고 있습니다.



2번째 bindService()로 실행하는 방법은


프로세스 내에서 다른컴포넌트들과 서로 유기적으로 통신을 하며, 다른 프로세스(어플리케이션)와도 Data 공유 및 통신을 하게 되는 Service를 실행하는 방법입니다.


--------Service의 추가 정보-----


- Service 는 특별히 명시하지 않는 한 Main Process 의 Main Thread 에서 돌기 때문에 Service 내에서 thread 관리를 해주어야 함.

- Service 는 할 일이 끝나면, stopSelf() 나 stopService() 를 통해 종료를 해주어야 한다. ( bind 시에는 고려대상이 아니다. )

- 안드로이드 시스템에 메모리가 부족할 경우, Service 를 죽일 수 있다. 현재 user focus 를 가진 activity 가 이 service 와 연결되어 있다면, 죽을 확률은 현저히 떨어지고, 만약 service 가  run in foreground 로 설정되어 있다면, 죽을 확률은 거의 없다고 보면 된다.

- 시스템이 메모리 등의 이유로 service 를 kill 시켰다면, resource 가 available 해졌을 때 자동으로 service 가 재시작된다.( 물론 이동작은 onStartCommand 의 return value 에 따라 다르다. )
 
IntentService 는 Service 의 subclass 로 일반 Service 와 달리 worker thread 를 이용하여 reqeust 를 handle 한다. 이것은 동시에 여러 요청을 처리할 필요가 없을 때 best option이 된다. IntentService는 각 요청마다 onHandleIntent() 가 들어온다.

- IntentService 는 request 를 queue 를 해줘서 worker thread 에서 일을 처리는 하돼, 한번에 하나씩만 처리하도록 한다. 만약에 모든 request 가 다 처리되면 알아서 종료되기 때문에 stopSelf() 를 호출해줄 필요가 없다. 나머지도 기본구현이 되어있기 때문에 결국 신경써줘야 하는건 onHandleIntent() 뿐이다.

- onStartCommand() 는 3가지의 return type 이 있다.

 START_NOT_STICKY 

system 이 service 를 죽였을 때, pending intent 가 없는 한 service 를 다시 실행시키지 않는다. 가장 safe 한 return type 이다.
 

 START_STICKY 

system 이 onStartCommand()가 return 한 후 service 를 죽였다면, service 를 다시 create 하면서 onStartCommand() 를 부른다. 하지만 last intent 를 다시 전달하지는 않는다. pending intent 가 없다면 null intent 가 전달된다. MediaPlayer 와 같이 자기 일을 묵묵히 하고 request 가 간혹 들어오는 type 의 service 에 더욱 적합하다.
 

 START_REDELIVER_INTENT 

system 이 service 를 죽였을 때 서비스를 다시 만들고 onStartCommand() 를 부르는데, 이 녀석은 last intent 를 전달받는다. 그 다음에 pending intent 가 있다면 추후에 전달된다. downloading 같은 즉각적인 resume 이 필요한 service 에 적합하다.

 

- Service 의 처리 결과를 받고 싶다면(bind 없이), client 에서 getBroadcast() 를 통해 braodcast PendingIntent 를 만든 다음에 service 구동 intent 에 함께 싣어보내고 service에서 해당 pending intent의 broadcast 를 통해 결과를 전달하는 구현을 해준다. ( PendingIntent 는 parcelable 로 전달한다.)

- startService 는 여러번 불릴 수 있지만, stopSelf() 나 stopService() 는 한번만 호출되도 service 를 stop 시킬 수 있다. 이 경우 multi request 를 handle 할경우 문제가 될 수 있다. 그래서 stopSelf( int reqeustID ) 가 제공되며, 가장 최근에 받은 request의 requestID 과 stopSelf에 전달되는 requestID 가 일치할 때만, 이 call 이 유효하게 된다.

- Foreground service 의 경우 반드시 noficiation 을 제공해야 한다. Foreground service 는 보통 Music Player 와 같이 interactive 한 service 에 사용한다. foreground 는 startForeground() 로 시작시킨다. 이 때 Notification ID 와 notification을 함께 전달한다. parameter로 stopForeground() 로 stop 시키는데 parameter 로 notification 도 함께 제거할지를 boolean 값으로 넘기게 된다.



출처 : http://arabiannight.tistory.com/247

반응형

'Basic > etc' 카테고리의 다른 글

[Android] 무결성을 위한 앱 설계시 고려할 사항들  (0) 2014.12.27
[Android] ANR 이란?  (0) 2014.12.27
클래스와 객체  (0) 2014.11.10
final, static  (0) 2014.11.10
객체의 생성, 생성자  (0) 2014.11.10