AIDL은 무엇일까?
우리는 AIDL 파일 하나 만으로 아주 쉽게 설명서를 생성할 수 있었다.
AIDL (Android Interface Definition Language) 즉 안드로이드에서 사용하는 인터페이스 정의 언어이다. (IDL이라고 하면 아는 사람도 있을 것이다. Android에서 정의한 IDL이 바로 AIDL이다.)
우리가 위와 같이 RPC 통신을 가능하게 하기 위해 특정 파일을 생성하고 거기에 RPC 관련 함수들을 적어 주었다. 해당 파일은 분명 java language는 아니었다. 미리 지정된 쉬운 언어이며, 아주 간단한 내용이었다. 이렇게 작성된 내용은 java 뿐만아니라 다른 언어에서도 공통적으로 파싱되어 사용될 수 있을 것이다. (전문적으로 설명하자면 너무 길다... 이해해 주길 바란다. ^^;) AIDL이란 Android 내부에서 사용되는 RPC Interface 정의 언어인 것이다.
사실 IDL이란 기술은 분산 객체 기술과 관련이 있다. 서로 다른 플랫폼 환경에서 서로 RPC 가 가능하도록 Interface를 만드는데 그 공통된 언어가 IDL이다. 그럼 AIDL이란 Android 내에서만 통일된 Interface를 말한다.
|
지금까지 내용으로 어떻게 RPC 통신이 가능한지 이해가 되었을 것이다.
상세히는 차후 Binder에 대해서 설명할 것이므로 여기까지만
확실히 기억해 두자.
자 지금까지 RPC 통신을 위한 Interface의 생성을 AIDL을 통해
자동으로 작성해 보았다.
이제 해당 Interface를 이용하여
- getCurNumber() 함수
- sum() 함수의
원형을 구현해 보자.
해당 함수는 당연히 Service측에 구현하도록 할 것이다.
서비스 패키지를 아래와 같이 조금 수정하도록 하자.
1번을 보면 드디어 호출하려고 했던 함수 원형을 구현하고 있다.
위에서 설명했던 내용을 기억하는가?
잠시 위에서 설명했던 이미지를 다시 한번 보자.
바로 여기서 원형함수를 구현해 ICountService.Stub() 객체를 생성하는 작업이다.
2번과 같이 두가지 함수를 구현하면 된다.
위에서 계속 설명하였듯이 Stub Class가 Service쪽에 해당한다고 하였다.
3번에서는 onBind()에서 생성된 Stub 객체를 리턴하고 있다.
그리고 Binder Service에서는 생명주기를 별도로 가진다.
4번, 5번, 6번 과 같이 Binder 생명주기에 관련된 콜백함수에 로그를 추가하도록 하자.
아래는 Binder 서비스의 생명주기를 나타낸다.
이전 강좌에서 배운 Service의 생명주기와 많이 다른 것을 알수 있다.
startService에서 호출되었던 함수 0nStartCommand 가 없다.
여기서 다시 한번 말하고 싶은 점은
startService와 bindService는 서로 다르고, 각각의 쓰임새(용도)도 다르다는 것이다.
개인적으로 용도를 정의하자면,
startService는 Background에서 어떤 기능을 동작시키는 작업을 하고
bindService는 동작중인 서비스에 대해서 제어할 수 있는 작업을 할 수 있다.
본 예제와 같이 1초에 1씩 증가하는 서비스를 시작할때는
startService를 통해 서비스 작동을 시작한다.
그 다음 현재 증가된 값을 얻기 위해서는
bindService를 통해 값을 얻어오는 것이다.
만일 본 예제와 같이 Background에서 계속 처리하는 동작이 없다면(1초에 1씩 계속 증가)
꼭 startService를 사용할 필요가 없을 것이다.
단순히 본 예제에 추가한 두가지 수의 합을 구하는 sum(a, b) 함수와 같이 특정 모듈만을 사용할때는
bindService만 사용하면 되는 것이다.
정확한 이해가 되었는가?
그래도 이해가 안되면 댓글을 남겨 주길 바란다.
자 여기까지가 Service 측 구현을 끝이 났다.
위의 생명 주기의 확인은 Client 쪽 구현을 통해 계속 확인해 보자.
startActivity에서 구현했던
ClientCountTestActivity 패키지를 이용해서 추가 구현해 본다.
먼저 서비스 패키지의 함수를 이용하기 위해서는
위에서 설명했던 조립 분해 설명서 Class가 필요하다는 것을 이해하고 있을 것이다.
그럼 먼저 분해 조립 설명서 class를 client 패키지에 추가하는 방법을 살펴보자.
Service 구현에서도 그러했듯이 정말 간단하다.
위와 같이 src폴더에서
1번과 같이 우측 마우스를 누르고
2번과 같이 New를 선택한뒤
3번과 같이 Package를 선택한다.
1번과 같이 서비스 패키지명을 넣어 준다.
그 이유는 간단하다. AIDL 파일은 자동으로 Class 파일로 변환되며,
해당 Class는 서비스 패키지에 구현된 내용이다.
그러므로 서비스와 같은 패키지 명으로 지정해 주어야 하는 것이다.
그 예로 서비스 패키지에서 자동 생성된 class명은
com.test.ServiceCountTest.ICountService 이었다.
그러므로 client 패키지에서도 같은 class명을
참조해서 사용해야 하는 것이다.
1번과 같이 패키지 폴더가 생성되었다.
이제
1번과 같이 서비스 패키지의 aidl 파일을 복사하여
2번과 같이 클라이언트 패키지에서 이전에 생성된 패키지 경로로 복사만 하면 사용 준비는 끝난다.
위와 같이 aidl 파일을 복사하면 자동으로 gen 폴더에
class 파일이 생성될 것이다.
!!! 만일 앞으로 자신이 만든 bind 서비스를 3rd Party 개발자에게
사용하게 하고 싶다면 간단히 aidl 파일만 전달하면 되는 것이다.
물론 aidl의 각 Interface에 대한 설명서도 같이 전달해야 한다.
자 본격적으로 서비스를 이용하는 패키지를 만들어 보자.
아래와 같이 Layout을 변경한다.
이미 아래와 같이 startService와 stopService는 이미 구현된 상태이고 변경 사항은 없다.
이제 bind Service와 Unbind Service를 구현 해 보자.
1번과 같이 bindService 함수를 호출함으로써 서비스 바인딩이 가능하다.
여기서 중요한 것은 전달 인자이다.
2번에 intent는 우리가 사용하려는 서비스 패키지의 Action에 해당한다.
startService에서 이미 설명한 부분이라 넘어가도록 한다.
3번은 ServiceConnection 객체를 넘겨 주어야 한다.
이는 서비스가 연결되었을 경우 호출되는 콜백 함수들을 구현해 주면된다.
bindService를 실행하여 서비스가 연결(binding)되면 0nServiceConnected가 호출되고
서비스가 종료되면 0nServiceDisconnected가 호출된다.
해당 부분은 바로 아래에서 다시 설명토록 하겠다.
4번은 아래에서 별도로 설명토록 하겠다.
5번은 서비스 binding을 종료하는 함수이다.
간단히 unbindService 함수를 호출함으로써 종료된다.
해당 부분은 너무 간단하여 설명을 생략한다.
위의 3번 과정을 좀더 정확히 이해하기 위해서
Client 패키지와 서비스 패키지가 상호 동작하는 과정을 살펴 보겠다.
1번 Client에서 서버로 binding 요청을 하게 되면
2번 Service에서는 0nBind 함수가 호출된다.
여기서 중요한 것은 바로 리턴이다.
3번 자세히 보면 mBinder 객체를 전달한다.
이 객체가 전달되면 이 객체를 이용하여 서비스의 함수들을 호출할 수 있는 것이다. 바로 RPC가 가능하다는 의미가 된다.
4번 Client에서는 ServiceConnection 객체에서 서비스의 binding을 알리는 0nServiceConnected가
호출된다. 이 함수의 인자로는 IBinder 객체를 받게되고,
5번과 같이 서비스를 참조할 수 있는 interface를 가지게 된다.
6번 Client에서 unbindService를 호출하면
7번 Service에서는 onUnbind가 호출된다.
이 과정에서 얻으려는 것이 하나다.
바로 서비스를 참조할 수 있는 Binder 인터페이스 즉 mBinder 이다.
mBinder는 ICountService 이다.
서비스 객체인 CountService의 Interface를 얻고자 한 것이다.
ICountService 라는 이름에서도 알수 있듯이
첫글자 interface를 의미하는 "I"가 붙는다.
이제 우리는 CountService의 객체를 접근할 수 있으며,
RPC 통신이 가능하다.
바로 아래 코드를 보자.
서버의 getCurNumber() 함수를 호출하는 기능이며,
아주 간단히 위에서 mBinder 객체를 이용하여
바로 서버 함수 참조를 한다.
아주 간단하지 않은가?
이렇게 쉽게 RPC 통신이 된 것이다.
정말 그러한지 실행해 보도록 하겠다.
먼저 CountService 패키지를 설치해 두어야 한다.
자 Client 패키지를 실행해 보자.
1번과 같이 Start Service 버튼을 누르면
CountService가 시작될 것이다.
위의 그림과 같이 로그를 보면 CountService의 0nCreate 와 0nStartCommand 가 호출된 것을 알 수 있다.
즉 해당 서비스는 이 시점부터 1초에 1씩 수치를 증가하고 있을 것이다.
2번과 같이 Bind Service 버튼을 누르면
Client와 Service가 바인딩 된다.
로그를 보면 Count Service의 0nBind가 호출된 것을 알 수 있다.
이제 Client에서는 바인더 객체를 얻었을 것이고
Service의 함수를 호출할 수 있게 된 것이다.
3번 버튼을 선택함으로써 RPC 통신으로
Client에서 Service로 현재 시점의 증가된 값을 얻어 온다.
위에서 Toast를 보면 현재까지 증가된 값이 235 라고 출력된 것을 볼 수 있다.
즉 RPC 성공이다.
4번 버튼을 선택함으로써 서버의 Binding을 종료하고
더 이상 서버의 RPC 통신을 중단한다. (서비스 객체 참조 중단, 원격 객체 참조 중단...등의 표현을 많이 한다.)
로그를 보면 Service의 0nUnbind 함수가 호출된 것을 볼 수 있다.
마지막으로
5번 버튼을 선택함으로써 서비스를 중단한다.
로그를 보면 서비스 객체의 0nDestory가 호출 되었다.
지금까지의 Sample Code는 아래를 참조하자
...
<2013년 02월 13일에 갱신된 내용>
T-T
bindService 강좌를 마치고 나서
빠진 내용이 있는 것을 깨닫고 강좌를 추가 합니다.
왜 빠트렸을까요?
아래의 내용을 빠트려서 이미 여기까지 정독하신 분들께 너무 죄송합니다.
과연 무엇이 빠졌을까?
바로 callback 기능이다.
지금까지 client에서 Service로 부터 어떤 함수를 RPC 통신으로
호출하는 것까지 진행하였다.
하지만 Client에서 Service에 Callback interface를 등록한 다음
어떤 조건이 되면 Service가 Callback을 호출해주는 과정을 설명치 않은 것이다. T-T
지금까지의 Sample 코드를 이어서 설명토록 하겠다.
아래를 보자.
위에서 Client에서 Service로 부터 어떤 콜백을 등록한다음
CountService가 1초에 1씩 증가시킬때,
그 증가치가 변경된 경우 콜백으로 알려 주는 과정을 생각해 보도록 하자.
콜백처리 역시 Bind 통신이 이뤄질 것이다.
그러므로 RPC 통신을 위한 AIDL 추가 작성이 간단히 필요하다.
아래는 이전에 작성한 Service 측 코드이며,
이어서 간단히 수정해 보자.
1번과 같이 이전에 작성한 ICountService.aidl을 수정한다.
ICountService.aidl에 수정한 내용은 콜백 Interface를 등록하고 해제하는 함수이다.
(Callback 객체를 전달해 주어야 함으로 당연히 필요한 부분이다.)
2번과 같이 ICountServiceCallback.aidl을 생성한다.
ICountServiceCallback 라는 Interface 는 콜백함수의 원형을 정의한다.
여기까지는 쉽게 이해되리라 생각한다.
다음은 AIDL에 추가된 내용을 구현해 주면 된다.
아래와 같이 CountService.java를 수정해 주자.
1번의 경우 Client측에서 콜백 객체를 전달해 주면 저장해 두는 객체이다.
여기서 RemoteCallbackList라는 객체를 사용하고 있다.
이는 Android에서 제공해 주는 Callback 객체를 저장해 두는 Class이다.
좀더 내려가면 이해가 되니 계속 진행해 보자.
2,3번의 경우 위의 ICountService.aid 에 추가한 콜백 객체를 등록하고 해제하는 함수를 정의한 부분이다.
아주 간단하다.
Client에서 registerCountCallback 함수를 호출하고 그 인자로 Callback 객체를 전달해 주면
RemoteCallbackList 라는 객체에 register 혹은 unregister 를 하고 있다.
위의 과정을 그림으로 이해해 보자.
1번에서 CountService를 사용하는 Client1 이 Callback를 등록요구할때
2번에서 CountService는 Client1의 Callback 객체를 저장해 두어야 할것이다.
3번에서 다른 Client인 CountService를 사용하는 Client2 가 Callback를 등록요구할때
4번에서 CountService는 Client2의 Callback 객체를 저장해 두어야 할것이다.
5,6번은 반복이다.
그렇다면 CountService는 여러 Client의 Callback들을 관리해야 할 것이고,
7번과 같이 그것이 바로 RemoteCallbackList 객체이다.
왜 RemoteCallbackList 객체가 필요한지 이해가 되었는가?
RemoteCallbackList 는 어떤일을 하고 있는지 잠시 살펴 보자.
별로 어렵지 않다.
위에서 1번~5번까지의 멤버함수가 일반적으로 사용된다.
간단히 설명하자면
4번과 5번은 Client에서 등록한 Callback 객체를 등록 및 해제하는 함수를 지원하는 것이다.
( 참고로 RemoteCallbackList 객체는 내부적으로
HashMap 객체를 가지고 결과 HashMap에 리스트를 보관한다.)
1번과 2번은 등록된 Callback들을 호출해 줄때 시작과 끝을 설정할 수 있다.
이 함수는 명시적으로 Callback을 호출할 것임을 알린다.
3번은 1번과 2번 사이에 loop문 안에 사용되며,
콜백이 모두 호출할때까지 등록된 Callback 객체를 얻어오는 역할을 한다.
-0 - 글로 보면 늘 머리 아프고 이해가 잘 안될 것이다.
쉽게 아래의 그림으로 이해해 보자.
순번호를 잘 보면서 이해하자.
1번에서 Client1 이 Callback 등록요청을 한다.
2번에서 CountService는 Client1의 요청을 받아들여
RemoteCallbackList의 register() 함수를 호출하여 Callback을 등록시킨다.
3번 ~ 6번까지 여러 Client 들이 등록을 하는 과정이다.
자 여기까지 register() 함수를 이해했을 것이다.
다음 이제 콜백을 호출할 시점이 되었을 경우
(본 sample 소스에는 1초 후에 1을 증가시켜 값이 변경되었을때가 그 시점이 되겠다.)
7번과 같이 Callback 호출을 시작한다는 함수를 호출한다.
그것이 바로 beginBroadcast() 함수이다.
해당 함수는 총 등록된 Callback의 개수를 리턴하고
우리는 그 수를 받아서 loop문을 돌리게 될 것이다.
자 여기까지 beginBroadcast() 함수를 이해했을 것이다.
다음 등록된 Callback 개수만큼 반복하면서
8번과 같이 Callback 객체를 얻어온다.
바로 콜백 객체를 얻어오는 함수가 getBroadcastItem() 이다.
자 여기까지 getBroadcastItem() 함수를 이해했을 것이다.
9번에서 얻어온 Callback 을 실행한다.
10번에서 모든 Callback 의 실행을 완료하면 finishBroadcast() 함수를 호출한다.
자 여기까지 finishBroadcast() 함수를 이해했을 것이다.
마지막으로 각 Client가 Callback 실행이 필요없게되면
11번과 같이 Callback 해제 요청을 할 것이다.
이때 등록된 Callback을 제거하는 unregister() 함수를 호출하여 제거한다.
자 이제 위에서 설명한 모든 함수를 이해해 보았다.
다시 위에서 추가한 코드를 다시 보도록 해 보자.
위의 과정이 쉽게 이해가 되는가?
빠진 코드가 있다.
바로 콜백을 실행해 주는 부분이다.
아래와 같이 코드를 추가하자.
이미 위에서 그림으로 설명을 했기 때문에 쉽게 코드가 보일 것이다.
대부분 콜백처리는 위와 같은 형태를 가진다.
자 Service 단의 준비는 끝났다.
그럼 Client 패키지를 수정하여 콜백을 등록해 보자.
위의 1번과 같이 Service에서 등록한 AIDL 파일을 Client에 그대로 복사해 두어야 한다.
자 ClientCountTestActivity.java 소스를 약간 수정해 보자.
1번과 같이 콜백 interface를 객체화(구현) 시키는 과정이다.
해당 객체를 등록하면 Service단에서 해당 함수를 호출해 줄 것이다.
2번,3번과 같이 콜백 객체를 등록해제하는 과정이다.
등록하는 과정은 서비스 객체와 binding되었을 때 등록하면 쉽다.
해제하는 과정은 서비스 객체와 unbinding 되었을때 해야하는 것이다.
꼭 등록을 했으면 해제를 해 주어야 하기 때문에
해당 Activity가 종료되때 해제하는 예외 코드를 추가하면 더욱 좋겠다.
자 모든 준비가 끝났다.
두가지 패키지를 설치하고 실행해 보자.
위와 같이
1번과 2번버튼을 누르면 최종 binding 되면서 count가 변경될때마다 callback이 호출될 것이다.
3번의 로그를 통해 확인해 보자.
1초에 한번씩 콜백이 호출되어 로그가 남겨 진다.
이어서 아래를 보자.
1번과 2번 버튼을 누르게 되면 서비스가 unbinding되고 종료되면서
콜백이 더이상 호출되지 않는 것을 볼 수 있다.
여기까지가 Callback 처리 과정을 설명하였다.
그런데 중요한 것 한가지가 있다.
그것을 이해하기 위해 Client 쪽의 Callback 함수에 아래와 같이 추가해 보자.
위와 같이 sleep을 줌으로써 함수의 지연을 주었다.
즉 해당 함수처리는 3초가량 걸릴것이다.
수정된 사항을 반영해 실행해 보자.
1번과 2번 버튼을 눌러 서비스를 시작 및 바인딩한다.
3번을 보면 Service단에서 콜백을 호출하고 (빨강색)
Client단에 콜백이 호출되었다. (귤색)
4번을 보면 거의 4초 후에 또다시 호출된다.
왜 4초일까 1초마다 값이 바뀌기 때문에 1초마다 호출되어야 하는 것이 아닐까?
그렇치 않다.
그 이유는 Client측 콜백함수에서 3초간 지연을 주었기 때문에
Client측의 콜백처리가 완료될때까지 Service는 대기하게 된다.
5번,6번 역시 각각 4초후에 처리가 되는 것을 볼 수 있다.
!!! 그렇다면 client측의 콜백이 느려지면
Service단의 지연이 발생되는 것인가?
그렇다.
!!! 혹시 Service단에서 Client 처리를 기다리지 않고
계속 처리할 방법을 없을까?
물론 있다.
간단한 수정으로 가능하다.
아래와 같이 Client와 Service 패키지에 존재하는
ICountServiceCallback.aidl을 약간 수정해 볼 것이다.
아래와 같이 수정해 보자.
수정도 아니다. 함수 앞에 "oneway" 라는 예약어를 하나 써 주면 된다.
두가지 패키지 모두 저장하고 실행해 보자.
3번~6번까지의 로그를 살펴 보자.
Service는 Client 콜백 처리를 기다려 주지 않고 1초마다 한번씩 호출하는 것을
확인 하였다.
지금까지의 Sample Code는 아래를 참조하자
다음 강좌는 서비스의 생존에 대해서 추가 설명을 하도록 하겠다.
!!! 위의 주제에 해당하는 적당한 예를 댓글로 남겨 주세요. ^^
활용 방안의 예는 다른 개발자들에게 많은 도움이 됩니다.
!!! 카페의 활성화를 위해 추천 버튼을 눌러 주세요.
gen에 에러가...- _-a;;
프로젝트 clean한번 해보시겠어요..
그런 경험이 없어서. T-T
Messenger를 사용하면 AIDL 없이 (사실 없는 것은 아니죠. 프레임웍에 존재할뿐)
바운드 서비스를 사용할 수 있습니다.
하지만 마치 함수를 사용하듯이 할 수는 없죠.
핸들러를 사용하니까요.
어쨌든 장점은 AIDL 배포 없이 사용할 수 있다는 장점은 있습니다.
안녕하세요. 성근님 꿀같은 강의 잘 보면서 공부중입니당!!
지금 서비스부분 보면서 질문 좀 드릴게 생겨서 댓글다네요~~
마지막 oneway 예약어를 쓰는 부분에서 헤매고 있는데요..!!
oneway를 쓰면 값이 바뀔때마다(1초마다) 로그가 찍혀야 하는데.. 그러질 않네요~~
다른 부분은 문제없이 진행되었는데...어떤문제가 있는건가요~??
감이 잡히질 않아서 어느 부분의 코드를 올려야 할 지 몰라 질문만 남깁니다~~ㅠㅠ
먼저 긴 글 읽어주셔서 감사드립니다.
제일 마지막 예제 소스에서 확인해보았습니다.
그런데 정상적으로 잘 동작을 하고 있네요.
^^ 다시 한번 확인해주세요.
@슈퍼성근 아....제가 성근님 강의 보면서 예제를 직접 쓰고 있거든요~~
다시한번 성근님 예제랑 비교하면서 차근차근 살펴봐야겠네요~ 답변감사드립니다~~
@콩다래 아 정말 좋은 방법입니다. T-T
직접 구현해보지 않으면 와닿지 않거든요.
예제 소스는 바로 이를 위해 올린것 입니다.
훌륭하십니다.
아 정말 좋은 내용입니다. bindservice를 이해하려고 인터넷도 보고 책도 찾아봤지만 여기만큼 정말 자세하게 설명되어있는 곳이 없네요. 하지만 ㅠㅠ 너무 길어 ㅠㅠ 길어도 너무 길어 ㅠㅠ 1편 2편으로 나눴으면더 좋았을 꺼라는 생각이 드네요 ㅎ
그래도 정말 유익한 자료입니다!
너~~~무 길어 죄송해요. T-T
사실 거기엔 좀 사연이 있어요.
대게 어떤 주제의 기술이든 독립적으로 존재하는 것이 아니라
관련된 기반 기술이 있고 그것들과 어우러져 파생됩니다.
예를 들어 직렬화에서 프리미티브타입 ->Serializable -> Parcel -> Parcelable -> Bundle...
식으로 말이죠.
따라서 이어져 설명하는게 중요하다고 생각했거든요.
그래서 하나의 게시글로 최대한 설명하려고 했는데.
너무 무식한 발상이었던것 같아요.
굳히 게시글을 하나로 할 필요까지는...
그렇다고 모든 글들이 하나로되어 있는것도 아니고 ㅎㅎㅎ
정체성을 잃어버렸죠.
다음 글들은 잘 나눠놓을께요.
좋은 의견 감사드려요.
@슈퍼성근 세세한 것까지 신경써 주시고 ㅎㅎ 감사합니다~ 그래도 좋은 자료임은 틀림없이니 슈퍼성근님 강추!! 책도 추천!
한가지 여쭤볼께 있어서 남겨봅니다.
서비스돌리는 app 과 그 서비스를 이용하는 외부 app 이 있씁니다.
서비스와 외부앱간의 통신은 성공적으로 이뤄진 상태입니다.
제가 생각했던 것은 서비스app 액티비티에서도 bind를 하고 외부app 액티비티에서 bind를 할 경우
서로 콜백을 다르게 할줄 알았는데... 서비스app 에서 콜백할때 외부 app 에서도 콜백이 동시에 이뤄지고
이와 반대로 외부app에서 콜백하면 서비스app에서 콜백을 합니다.
제 생각에는 같은 콜백함수를 이용하면서 그러는거 같은데 따로 분리하려면 콜백함수를 여러개 생성해서 따로해야 하나요? 이부분을 정확히 모르겠네요;;;
죄송합니다.
질문하신 내용을 이해하지 못했습니다. ^^;
요지가 여러 클라이언트에서 동시에 특정 서비스 함수를 호출하면
같은 콜백으로 반환된다는 말씀이신가요?
RemoteCallbackList 내용을 보시면 콜백을 등록하는 것은
클라이언트고, 서비스는 클라이언트가 등록한 콜백객체를
호출해주고 있습니다.
죄송합니다만 좀더 자세히
질문 게시판에 올려주시면
정확한 답을 드릴수 있을것 같네요.
안녕하세요. 강좌 잘 보고 있습니다.
한가지 궁금한 점이 있는데요.
Unbind Service를 선택시에 Toast도 발생하지 않고 두번 누르면 앱이 종료되는 현상이 발생합니다.
Service 프로세스에서는 Unbind 로그는 찍히는데 onServiceDisconnected는 호출되지 않네요..
어떤 문제일지 의견 주시면 감사하겠습니다.
안녕하세요. ^^ 제가 올린 Sample 앱이 그러한가요?
아니면 직접 따라하신 앱이 그러한가요?
사용하신 예제소스를 아려주시겠어요.
그리고 onServiceDisconnected가 호출되지 않는 것은 바로
bindService의 세번째 인자인 BIND_AUTO_CREATE 값 때문입니다.
간략히 말씀드리자면 BIND_AUTO_CREATE 로 설정시
startService로 서비스가 동작중이 아니더라도
바운드서비스를 사용할 수 있도록 하는 값입니다.
이렇게 바인딩되면 서비스 연결을 유지하기 때문에(unbind를 하더라도 말이죠)
onServiceDisconnected가 호출되지 않습니다.
혹시 책을 가지고 계시다면 18장 서비스편에 상세히 설명하고 있습니다.
아직 인터넷 강좌는 최신화가 덜되어 죄송합니다.(정리되면 최신화에 힘써야겠어요.)
@슈퍼성근 답변 감사드립니다.
제가 따라한 앱에 문제인지 알고 샘플앱을 다운 받아서 확인해도 동일한 현상이었습니다.
onServiceDisconnected 관련한 내용 답변 감사드립니다. 큰 도움 되었습니다. ^^
안녕하세요. 늘 잘보고 있습니다.
서비스에 callback을 등록하는 예제에서 질문이 있어 문의드립니다.
onCountChanged()을 실제 구현하고 있는쪽은 ClientCountTest 패키지인데,
왜 ServiceCountTest패키지에 aidl이 포함이 되어 있나요?
제가 이해한 바로는 실제 구현하는 패키지에 포함되어야한다고 생각하고 있었는데^^;아닌가요?
안녕하세요.
잠시 헷갈리셨나봐요. ^^
둘다 AIDL이 존재해야 합니다.
AIDL의 역할은 서로 다른 프로세스간 함수.. 즉 데이터를 전달하는 목적입니다.
여기서 서비스단에서 클라이언트 단의 callback을 호출하려면
aidl을 통해야하거든요.
이렇게 생각하시면 쉬울것 같습니다.
클라이언트에서 일반적으로 서비스 함수를 호출하는데
콜백은 반대가 되죠. 즉 서비스가 클라이언트 함수를 호출합니다.
그리고 프로세스가 다른 RPC는 AIDL을 이용하는 것이니
당연히 두곳 모두 필요합니다.
이 부분은 위쪽 분해/조립 등의 과정을 다시 상기하시면 좋겠네요.
수고하세요.
@슈퍼성근
제가 이해가 안되어서 질문은 다르게 해보겠습니다.
aidl시 작성시 내가 노출 즉, 외부에서 RPC를 허용하는 함수를 작성하게 되는데 서비스에서 노출시킨 함수는 이해가 되는데, 하기와 같이 클라이언트측에서 노출한 함수작성시 패키지명이 서비스 패키지로 되어있습니다.
제가 생각할땐 package com.test.ClientCountTest; 로 작성되어야 하지 않은가 싶어 질문드린거였습니다.
package com.test.ServiceCountTest;
interface ICountServiceCallback
{
oneway void onCountChanged( int changedCount );
}
@아라한 외부에 제공되는 함수 그리고 그 콜백 함수는 서비스에서 정의하는 것입니다.
서비스 입장에서 보면
특정 함수를 제공하고 그 결과를 콜백으로(비동기) 알려주는 것까지 제공하는 것이죠.
다만 콜백(인터페이스) 구현은 클라이언트가 해서 그렇게 보일 순 있지만
콜백 조차 서비스의 설계입니다.
음 이렇게 생각하면 쉬울것 같습니다.
View에 클릭 리스너를 구현할때
클릭 리스너 구현은 뷰를 사용하는 곳에서 하죠.
하지만 클릭리스너 인터페이스 자체는 뷰에 있죠.
여기서 뷰의 클릭리스너를 서비스로 보시고
클릭리스너를 구현하는 부분을 클라이언트라고 보시면 되겠네요.
@아라한 참고로 서비스 입장에서는 클라이언트 앱이 무엇인지 알 수 없어요.
그래서 미리 클라이언트 패키지명의 AIDL 자체를 만들수 조차 없죠. ^^
이해가 되지 않으시면 모두 저의 부족한 설명 때문입니다.
부담가지지 마시고 얼마든지 재질문하셔도 됩니다.
수고하세요.
감사합니다. 늦은 시간에 이렇게까지 피드백이 바로 올줄은 몰랐네요. ^^ 추가로 별외질문 드리겠습니다.제가 웹쪽지식이 별로 없는데 요즘은 하이브리드앱 기본이라 어떻게 접근하는게 효율적일지 모르겠습니다.별도로 jsp나 jquery,jdbc를 익혀야하나요? 필요하다면 책추천부탁드립니다.
^^ 죄송합니다만 이 질문에 대해서는 노코맨트할께요.
하이브리드 앱/웹앱... 등에 대한 개인 의견은 말씀드리기 좀 곤란해서요.
이해부탁드립니다.
강의해주신 내용들 모두 실무에서 유용하게 도움이 되고 있으며, 크게 감사 드립니다.
콜백 기능의 UseCase 관련하여 몇가지 설계상의 어려운 점에 대해 아래와 같이 문의 드립니다.
(Q1) 임의의 Client1, Client2, Client3이 특정 서비스 모듈에서 제공하는 CallBack API를 등록했습니다. 이때, 해당 서비스 모듈에서 이벤트가 발생하면, 상기의 설명하신 예제에서는 CallBack API를 등록한 모든 Client 들에게 Broadcasting 되고 있습니다. 그런데, 서비스의 컨디션의 의해 어떤 경우는 Client1에만, 또 어떤 경우에는 Client2, 또 어떤 경우에는 Client1과 Client2에 notify 하고자 하는데요. 범용적인 방법으로 설계해야 하는데 어떤 방법이 좋을까요? 잘 모르겠네요.
a. 각각의 조건에 해당하는 CallBack API를 설계한다.
b. Client에서 CallBack API를 등록할 때, Client 정보를 서비스에 제공한다.
c. 기타 등등
(Q2) 임의의 Client1이 특정 서비스 모듈에서 제공하는 CallBack API를 등록하려 합니다.
그런데, 이 Client1은 Background 실행 중에도 콜백을 호출받을 필요가 있습니다.
a. 이 Client1 또한 서비스 모듈로 설계되어야만 하나요?
b. (a의 답변이 yes인 경우) 서비스 모듈로 설계되어야만 한다면, 상기 예제에서 설명주셨던 Activity 대신에 서비스 컴포넌트에서 CallBack API를 등록하면 되나요? 아니면 CallBack 기능을 제거하고, 양방향으로 서비스 바인딩을 걸어 주어야 하나요?
좋은 질문이십니다.
첫번째 질문에 답합니다.
RemoteCallbackList 클래스는 내부적으로 cookie 값을 추가할 수 있습니다.
콜백을 등록할 때 RemoteCallbackList.register(콜백, 바로 요값)
즉 두번째 인자인데요.
이 값에 구분자를 넣어주시면 됩니다.
그럼 RemoteCallbackList.beginBroadcast()
과 finishBroadcast() 사이에서
getBroadcastCookie()라는 함수로 구분자를 받아올 수 있고
이 값을 통해 원하는 대상 콜백만 호출하시면 됩니다.
물론 구분자에는 패키지명 혹은 대상을 구분할 값을 클라이언트에게 받아야겠죠.
일반적으로는 패키지명이 가장 많이 사용됩니다.
두번째 질문에 답합니다.
당연히 Service형태여야 합니다.
Service를 써야하는 경우는 단순합니다.
화면을 가진 액티비티가 아닌 백그라운드 작업을 해야할 때입니다.
액티비티에서 작업스레드로 처리하고, 액티비티가 종료된 후에도 돌아야 한다면
이는 서비스 밖에 방법이 없습니다.
b의 답.
당연히 서비스에서 다른 서비스의 콜백을 등록하셔야 겠죠.
콜백이 호출되는 곳이 어딘지 생각하시면 쉽습니다.
액티비티에서 콜백을 등록하면 등록된 콜백은 액티비티가 종료하면
받을 수 없습니다.
즉 액티비티의 Context를 사용할 수 없게되죠. 액티비티가 종료되면
액티비티의 Context도 사라지니까요.
마지막으로 힌트 한가지 드립니다.
서비스에서 일련의 작업들을 스케쥴링하기가 쉽지 않습니다.
Main Thread의 Handler를 이용하여 sendMessage로
처리할 작업들을 큐에 담아 순차적으로 처리하면 편하지만
서비스는 Main thread에서 작업하면 안됩니다.
(이는 위에서 설명드렸죠.)
이를 위해 HandlerThread라는 녀석이 있습니다.
요 녀석은 작업 스레드를 만들어줄 뿐만 아니라
Main Thread에 있는 looper, messageQueue 구조와 동일합니다.
그래서 서비스 내에서는 HandlerThread를 기반으로 처리하는 것이 매우 편합니다.
혹시 책이 있으시다면 HandlerThread를 참고해주셨으면 좋겠네요.
수고하세요.
명쾌한 답변,,, 감사 감사 무한 감사 드립니다.
안녕하세요 잘 보고 있습니다. 질문드릴게 있어요
callback 이용할 때 하나씩 하지 않고 클래스를 하나 만들어서 dto형식으로 한번에 넘기려고 했는데
aidl에서 import를 하고 쓰려고 해도 클래스를 자꾸 찾을 수 없다고 뜨네요
다른 곳에서 사용한 그대로 임포트를해도 철자 틀린것도 아닌데 자꾸 해당 클래스를 찾을 수 없다는
오류만 띄워주니 미치겠습니다.
같은 패키지 안에 넣고 임포트 안하고 사용하려고 하면 알 수 없는 클래스라고 뜨고..
어떻게 해결해야할 지 조언좀 얻을 수 있을까요?
안녕하세요. 늦게 봤네요. T-T
아마도 제 생각에는 AIDL의 경로가 잘못된 것이 아닌가 생각됩니다.
AIDL을 제공하는 서비스측과
AIDL을 사용하는 클라이언트의 .aidl 파일을
패키지 경로까지 같아야 합니다.
늦었지만 지금도 문제가 된다면 Sample을 질문게시판에 올려주시면
쉽게 도움드릴수 있겠네요.
수고하세요.
궁금한것이 있습니다. 여기서 aidl 파일에 콜백을 따로 만들어주는 이유가 무엇인가요? 위 예제코드를 보면. getCurNumber()메소드를 1초마다 호출시키면 그것도 콜백이 되지 않나요? IServiceCallback을 만들어주는 이유가 궁금합니다.
안녕하세요.
getCurNumber은 함수의 리턴으로 받는 방법입니다.
그런데 이 경우는 getCurNumber 처리가 빠르지만
굉장히 느린 함수고 언제 작업이 끝날지 모른다고 하면,
무작정 해당 함수의 리턴을 기다릴수 없습니다.
제가 하려는 말은 바로 비동기 처리가 필요한 경우입니다.
이때 바로 콜백 리스너를 등록하는 것입니다.
즉 서비스에서 작업이 끝나면 해당 리스너를 호출해주는 것이죠.
많은 앱을 개발하다보면 콜백 리스너는 매우 많이 사용됩니다.
(비동기 처리가 대부분 필요하니까요.)
꼭 이 장의 서비스의 기능이 아닌것이죠.
단지 서비스도 콜백 리스너를 지원한다 입니다.
수고하세요.
안녕하세요. 궁금한 것이 있는데요. 서비스 관련 첫번째 강의를 보면
"그렇다 Service는 서로 다른 Process에서도 해당 기능을 사용할 수 있도록 하는 구조를 제공한다."
서비스를 이용하면 다른 프로세스 상에서 실행되는 서비스(내 함수)도 쉽게 접근 가능하다고 생각했는데,
여기서는 AIDL을 써서 접근을 해야한다고 설명하셨습니다.
무슨 차이인지 궁금합니다..
성근님 제가 궁금해하던부분이 여기있었네요 aidl 콜백.. 많이 배우고 갑니다
추가적으로 server 역활을 하는 service가 갑자기 kill되고, 그 kill된 service를 client에서 알지못하는 경우가 종종 생기는데요 그럴경우 oncallbackdied의 설명도 추가 해주셨으면 좋겠습니다~
아~ 죄송, 책에는 써져있네요~
안녕하세요. 질문드릴사항이 있습니다.
RemoteCallbackList에 등록된 client 중 일부를 선택해서 callback을 할 수 있나요?
예를들어 server에 client1, client2, client3이 등록되어 있을 시
특정 상황에서 client2로만 callback해주고 싶습니다.
getbroadcastitem으로 client(app)을 식별해서 callback하는
방법은 어떻게 해야할지 감이 안오네요.
안녕하세요.
이는 cookie를 이용하시면 됩니다.
위의 질문과 제가 단 답변으로 보면 그 내용이 있습니다.
수고하세요.
callback 할 대상이 2개 이상일 경우는 어떻게 하나요? 각각 aidl을 만들어야하는가요?
네 따로 만드셔도 되고,
아니면 콜백 인터페이스 클래스에
여러개의 핸들러 함수를 등록해서 사용하셔도 되겠네요.
예를 들어
interfce ICountServiceCallback
{
void onCountChanged();
void on1()
void on2()
...
원하는 만큼 타입 추가
}
수고하세요.
처음이라 완벽하게 숙지는 못했지만 두고두고 봐서 완벽하게 숙지할 생각입니다.
정말 알찰내용이네요!! 자료 감사합니다!!!!
감사합니다
같은 이름의 서비스를 다른 액티비티에서 bindservice 두번 이상 하게되면 bindservice 할때마다 다른 process에서 돌아가나요? 아니면 하나만 계속 도나요?
서비스는 여러개가 실행되지 않습니다.
동일한 앱이든 다른 앱이든 여러번 바인드 하더라도 기존 실핼된 서비스에 하나에 바인딩됩니다.
수고하세요 ^^
@슈퍼성근 안녕하세요~ 답변 감사합니다. 한가지 문의사항있습니다.
서비스를 bindservice로 구현했는데 이 서비스가 background에서 돌아가길 원합니다.
즉 앱이 destory될때 unbind를 하지 않고싶은겁니다. (unbind는 특정 버튼을 눌렀을 경우에만)
일반적으로 bind서비스를 unbind하지 않으면 앱이 destroy될때
"has leaked ServiceConnection that was originally bound here" 라는 에러를 호출하더군요.
검색을 해도 잘 안나오고..
어느분은
startservice 후에 bindservice하면된다고 하는데 그렇게 해도 앱이 destory되면 똑같은 에러가 나오더군요.
@슈퍼성근 어떤 라이브러리에서는 bind후에 unbind하지 않고 앱을 destory해도 위와같은 에러가 나오지 않더라구요.
어떻게하면되는지 궁금합니다.
설명 끝판왕 이네요! 잘 보고 잘 이해하고 갑니다. :)