슈퍼드로이드 카페의 안드로이드 강좌가 책으로 나왔습니다.
도서명 : 이것이 안드로이드다.
도서링크 : http://www.yes24.com//24/goods/13950202
================================================================================================
1.2 Service Component 구현 - startService
이제 서비스 형태로 1초에 1씩 증가하는 기능을 구현해 보자.
아래와 같은 서비스 테스트 패키지를 구성한다.
AndroidManifest.xml에 다른 Component와 같이 <application> 항목 아래에
<service> 항목을 선언한다.
2번은 다른 패키지에서 해당 서비스를 활성화 시킬때
사용되는 Action을 등록하는 과정이다.
Activity 강좌에서 배운 것 처럼
Service 역시 같은 방법으로 Action이 사용된다.
아래는 Service 소스이다.
아래 1번과 같이 Service를 상속 받아서 해당 소스를 구현한다.
Service를 상속받게 되면
아래와 같이 2~5번과 같은 함수를 overriding할 수 있다.
Overriding 함수는 모두 Service의 생명 주기에서 호출되는 Callback 함수 들이다.
우선 5번은 별도로 생각해야 함으로 다음에 설명하도록 하고
1번~4번까지를 먼저 보도록 하자.
아래의 그림을 보고 이해하도록 하자.
서비스의 생명주기는 Activity에 비해 아주 간단한다.
Service가 실행되면 가장먼저 0nCreate()가 호출되며,
서비스가 실행되기 직전에 0nStartCommand()함수가 호출된다.
이후 서비스는 running상태가 된다.
만일 서비스가 종료되면 0nDestory()가 호출되는 것이다.
예외적으로 만일 서비스가 running 상태에서
누군가가 서비스를 또 start를 시키게 되면
서비스는 0nCreate()부터 호출되지 않고,
바로 0nStartCommand()가 호출된다는 것을 기억하면 된다.
아주 간단하지 않은가? ㅋㅋㅋ
자 생명주기가 정말 그렇게 동작하는지 확인해 보자.
아래와 같이 각 생명 주기에 로그를 추가하도록 하자.
이제 해당 서비스를 start할 패키지를 하나 작성하자.
해당 패키지는 하나의 Activity로 구성되고
특정 버튼을 눌렀을 경우 Service를 시작하도록 할 것이다.
Layout 구성은 아래와 같다.
Activity 소스는 아래와 같다.
1번과 같이 "Start Count" 버튼을 클릭하게 되면
해당 Service를 실행한다.
Service를 활성화 하는 방법은 Activity를 활성화 할때 사용했던 방법과 거의 유사하다.
먼저 해당 Service의 Action을 Intent에 담아 주고,
startService()라는 함수를 호출함으로써 서비스가 활성화 될 수 있다.
2번은 "Stop Count"버튼을 눌렀을때 동작중인 서비스를 중단시켜 준다.
1번에서 서비스를 시작한 방법과 거의 동일하다.
중지한 Service의 Action을 Intent에 담고,
stopService()함수를 호출함으로써 서비스를 중단 시킨다.
3번에서는 서비스에서 증가중인 현재 값을 읽어 오는 부분을 구현할 것이다.
일단 3번 과정은 잠시 후에 별도로 설명할 것이므로 비워둔다.
자 구현이 끝났다.
Service 패키지를 먼저 설치하고
그 다음 서비스를 활성화 시켜주는 ClientCountTest 패키지를 실행하자.
자 아래와 같이 1번 버튼을 선택한후 로그를 보자.
2번과 같이 0nCreate()가 호출되고 이어 0nStartCommand()가 호출됨을 알 수 있다.
|
참고)
서비스를 실행시켜주는 Client패키지에서 startService()를 통해 서비스를 실행했다. startService 메소드를 실행할때 인자로 startService(intent) 를 넣어 주었다. 바로 이 Intent는 서비스의 생명주기 콜백 함수인 startCommand() 의 인자로 전달 받는다. startCommand의 상세 인자에 대해서는 다음 강좌에서 설명하겠다.
|
이제 아래와 같이 1번을 눌러 서비스를 중단해 보자.
2번과 같이 로그를 보면 0nDestroy()가 정상적으로 호출되었음을 알 수 있다.
자 마지막으로 다시 1번과 같이 서비스를 실행해 보자.
2번과 같이 로그를 보면 당연히 0nCreate()가 호출되고 0nStartCommand()가 호출되었다.
여기까지는 서비스가 실행된 상태이다.
만일 다시 서비스를 실행하면 어떻게 될까?
3번과 같이 다시 서비스를 실행해 보자.
4번의 로그를 보면 0nCreate()부터 호출되지 않고
바로 0nStartCommand()가 호출되었다.
여기서 기억해야 할 사항은
서비스를 실행시 0nCreate()가 무조건 호출되지 않는다는 것을 기억해야 한다.
만이 서비스가 실행될때 마다 처리해야 할 코드를 넣는다면,
당연히 0nStartCommand()에 넣어야 하는 것이다.
별로 어렵지 않은 부분이므로 상세한 설명은 필요없을 것 같다.
위의 테스트 패키지의 소스는 아래를 참조하자.
자 이제 본격적으로 1초에 1씩 증가하는 기능을 서비스에 넣어 보도록 하자.
아래와 같이 서비스 패키지의 CountService.java를 수정하자.
2번을 보면 loop를 돌면서 1씩 증가하고 있다.
너무 쉬운 코드라 설명하지 않아도 될 것 같다.
자 서비스 패키지를 저장하고 바로 설치하자.
해당 서비스를 동작시키기 위해 위에서 작성해 뒀던
ClientCountTest 패키지를 실행하자.
자 아래와 같이 1번 "StartCount" 버튼을 눌러
그렇다면 서비스가 활성화 되면서
1초에 1씩 증가하는 동작이 수행될 것이다.
2번과 같이 DDMS를 실행시켜 서비스 패키지를 가만히 보고 있어 보라.
!!! 약 20초 후에 서비스 Process가 사라질 것이다.
왜일까?
로그를 확인해 보면 3번과 같이 ANR이 발생되었다.
??? ANR은 Activity의 Main Thread에서 5초 이상 긴 작업을 수행했을때 발생되는 것이 아니던가?
^^ 그것은 큰 오해이다.
Activity도 Broadcast Receiver도 Service도 모두 Main Thread에서 동작한다.
이 3가지 Component 모두 Main Thread에서 긴 작업을 하면 모두 ANR이 발생되는 것이다.
하지만 각 Component 별로 ANR이 발생되는 시간은 다르다.
일단 이전에 수정한 아래에 코드를 다시 보자.
0nStartCommand() 내에 loop문이 돌면서 작업이 끝나지 않고 있다.
다시 해당 서비스를 실행해 보고
DDMS를 살펴 보자.
20초전에 빠르게 서비스 Process의 Main Thread를 확인해 보라.
5번을 보면 MainThread에서 0nStartCommand()가 호출되고 있음을 알 수 있다.
잠시 Android Frameworks의 소스를 확인해 보겠다.
Frameworks의 ActivityManagerService.java를 참조해 보면
1번과 같이 SERVICE_TIMEOUT 값을 볼 수 있다.
값을 보면 20초인 것을 알 수 있다. 즉 20초 동안 서비스의 응답이 없을 경우
강제로 ANR을 발생시키는 것이다.
추가적으로
2번과 같이 BroadcastReceiver의 경우 10초이며,
3번과 같이 Activity의 경우 5초인 것을 알 수 있다.
그러면 어떻게 구현해야 할까?
바로 이전 강좌에서 Thread를 사용한 것과 같은 방법으로 해야한다.
!!! 뭘까? 궁금한 점이 생기지 않는가?
분명 Service는 Background에서 돌아가는 Component이고
Thread와 같은 것 아니었는가?
이것이 많은 사람들이 범하는 오해이다.
Service는 Thread가 절대 아니다.
이전 강좌에 설명했듯 Service는 다른 Process에서
서비스의 기능을 사용할 수 있는 구조를 지원하는 것이다.
서비스는 UI가 없으며 Background에서 동작함으로 Thread라고 오해해서는 안된다.
서비스 역시 Main Thread에서 동작함으로
긴 시간이 걸리는 작업은 Activity와 동일하게 Thread에서 처리해야 한다.
이해가 되는가?
이해가 되지 않는 사람은 꼭 댓글을 달아 주었으면 한다.
좀더 이해가 되도록 노력해 보겠다. ^^
자 그럼 서비스 소스를 다시 Thread를 사용해서 구현해 보자.
내용은 이전강좌와 동일한 소스이므로 상세한 설명을 생략하겠다.
자 서비스를 설치하고 서비스를 실행하는 ClientCountTest 패키지를 실행해 보자.
아래의 1번과 같이 "Start Count" 버튼을 눌러 서비스를 활성화 하자.
DDMS를 이용해서 서비스 Process의 Thread들을 살펴보자.
아래 4번을 보면 생성된 "Count Thread" 가 존재하는 것을 알 수 있다.
조더 상세히 5번을 보자.
Thread의 run() 메소드가 실행중인 것을 알 수 있다.
자 해당 기능을 정상적으로 동작시켰다.
이제 Background에서 1초에 1씩 증가하는 서비스가 계속 돌고 있을 것이다.
여기까지 작성된 소스는 아래를 참조하자.
지금까지 배운 내용만으로도 쉽게 서비스를 구현할 수 있다.
만일 특정 기능(서비스)을 동작시키고,
중단 시키는 역할만 필요하다면 지금까지 배운 것만으로도 가능하다.
"뭐야 이 것만 가지고 뭘해!" 라고 생각할 수도 있다.
하지만 정말 특정 서비스를 시작과 종료만으로도 충분한 경우는 있다.
예를 들어 특정 노래를 재생하는 경우 play와 stop이면 충분할 수도 있다.
^^ 어디선가 이 것에 대해서 설명하기 위한 그림을 본적이 있다.
두가지 그림을 그려 놓고
1) " ▶", " ■ " 밖에 없는 MP3 Player 그림과
2) " ▶", " ■ " , "||", "<<", ">>" MP3 Player 그림을 그려 놓고 있었다.
참 적절한 그림이지 않은가?
1)번과 같은 경우는 지금까지 설명한 내용만으로 구성된 것을 말한다. ^^
자 그럼 2)번과 같이 추가적으로 여러 기능을 구현하는 방법을 알아보자.
위에서 1초에 1씩 증가하는 서비스는 구현되었다.
하지만 현재 증가된 값을 얻어오는 부분은 구현되지 않았다.
이를 위해 서비스를 시작과 종료하는 것 이외에 다른 무엇인가를 배워야 한다. ^^
위에 2)번과 같은...
그럼 다음 강좌에서 설명토록 하겠다.
!!! 위의 주제에 해당하는 적당한 예를 댓글로 남겨 주세요. ^^
활용 방안의 예는 다른 개발자들에게 많은 도움이 됩니다.
!!! 카페의 활성화를 위해 추천 버튼을 눌러 주세요.
댓글
댓글 리스트-
답댓글 작성자슈퍼성근 작성자 본인 여부 작성자 작성시간 14.08.08 안녕하세요.
안드로이드에서 - _-; 바뀐것이 없습니다. 혹시 제가 올리 예제 소스로 확인해보셨나요?
start 버튼을 눌렀을 때 onCreate -> onStartCommand -> onDestroy가 죄다 찍혔다는게 이해가가지 않네요.
그말은 서비스가 시작과 동시에 죽어버린 건데요.
간혹 switch~case 문에서 이런 실수를 많이 하게 되는데요.
바로 break;를 빼먹는 경우죠. 저도 간혹 이런 실수를 합니다.
이로인해
case문에서 startService하자마자 break를 빼먹어서
바로 아래의 case문에 stopService가 호출된게 아닌지...
확인해보시겠어요.
어쨌든 startService후 stopService를 하시지 않았다면 onDestory가 호출되는게 이상합니다. -
작성자jeongu park 작성시간 14.08.08 제가 break를 빼먹었네요 ㅠㅠ 제 손으로 직접 따라하면서 치면서해야 공부가 될꺼같아서 직접 쳤늗네 break가 빠져있었어요 ㅠㅠ 감사합니다 ㅎ
-
작성자아잉 작성시간 15.03.02 Android 5.0 에서 변경된 정책때문에 (Service Intent must be explicit)
위와 같이 service start할 경우 아래와 같이 에러가 발생하므로 내용이 업데이트 되면 좋겠습니다.
03-02 15:56:50.858 31375 31375 E AndroidRuntime: Caused by: java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=com.test.service.COUNT }
수고하세요~ -
작성자yeojoy 작성시간 15.03.18 순두부님께서 극찬하신 Service 부분 정주행 중입니다. 혹시 그러셨던 적 없으신가요?
onCreate()은 한번만(강의를 보니 생성한번) 호출되고 onStartCommand()는 여러번 호출되는 현상이요.
물론 Intent를 사용해서 startService를 한번만 호출했습니다. 어딘가에서 잘못 구현한 거 같긴한데... 이전 플젝에서 경험했던거라... 일단 직접 경험해 봐야겠습니다. 역시... 왜 Service에 반했는지 정독을 하니 알겠습니다~ 항상 필요한 부분만 보다보니... -.-)b -
작성자컴공도리 작성시간 15.07.16 안녕하세요. 강의 잘 보고 있습니다. 한가지 궁금한게 있습니다. Activity와 Service의 ANR 발생하는 상황이 다른가요? 그전 강좌에서는 Activity의 경우 Main Thread에서 오래 걸리는 작업을 하더라도 그다음 작업을 해야지 ANR이 발생한다고 하셨는데 Service는 그 다음 작업을 요청하지 않았는데도 ANR이 발생한다고 해서요.