336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

 안드로이드는 저에게 굉장히 생소한 플랫폼입니다. 기존 어플리케이션과는 다른 라이프 사이클, 서로 다른 어플리케이션이 상대방 구성요소를 자유롭게 활용할 수 있는 점, 공통된 자원 접근 경로로서 Uri 의 적극적인 활용 등등... 여러가지 생소한 개념을 포함하고 있습니다.


 그중에서도 Intent 와 Intent Filter 그리고 Intent Routing 관련 기능은 어떻게 이런일이 가능할까? 라고 스스로에게 물어보면, 명쾌히 답할 수 없는 그런 애매한 문제였습니다. 그러다 마침, 참가중인 안드로이드 스터디 모임에서, 관련 내용을 발표하기 위해 Intent 와 Intent Filter 그리고 Intent Routing 에 대해서, 나름대로 정리할 기회가 생겼습니다. 그 내용을 공유해 봅니다.

1. Intent?
 인텐트는 '의도' 라고 번역됩니다. 또한 기능적으로 말하면 '메세지' 라고 할 수 있습니다. 예를 들어 'Intent.ACTION_VIEW' 값을 갖는 인텐트는 무언가를 '본다' 라는 의도를 내포한 '메세지' 라고 할 수 있습니다. 이 메세지는 안드로이드의 어플리케이션 컴포넌트(Activity / Service / BroadcastReceiver)들 사이의 의사 소통 수단 중 하나로 사용됩니다. 


<짜장면을 시키기 위해서는 중국집에 전화를 한 후에 주문을 전달해야 합니다.>

 그런데 여기서 중요한 것은 '메세지' 자체 만으로 어떤 일을 할 수 있는 건 아니라는 점입니다. '메세지' 가 힘을 발휘하는 건, 누군가가 그 '메세지' 를 읽고, 해당 '메세지' 의 내용을 직접 수행할 때 입니다. 짜장면을 배달 시키는 과정을 생각해보면 이해하기 쉽습니다. 예를들어, 내가 자취방에 앉아 아무리 '짜장면 배달해 주세요!' 라고 혼자 소리쳐도  결코 짜장면이 배달되어 오지는 않습니다. 중국집에 전화를 걸어, 내 주소를 말하고, 짜장면을 배달해 달라고 정확한 메세지를 전달하면, 중국집 주방장이 짜장면을 만들고, 그 후에야 짜장면이 오토바이에 실려 집까지 배달 오게 됩니다.

2. Intent Filter?
 인텐트는 어떤 의도를 나타내는 '메세지' 입니다. 메세지가 힘을 갖기 위해서는 누군가에게 전달되야 합니다. 인텐트 필터는 '필터' 라는 말이 의미하는대로, 나는 특정 '의도'를 포함하는 메세지를 받고 싶다라고 정의하는 방법이라고 할 수 있습니다. 예를 살펴보면 훨씬 쉽게 알 수 있습니다. 

<intent-filter android:label="@string/resolve_edit">
 
<action android:name="android.intent.action.VIEW" />
   
<action android:name="android.intent.action.EDIT" />
    <category android:name="android.intent.category.DEFAULT"/>              <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
</intent-filter>
<intent-filter>
   
<action android:name="android.intent.action.INSERT" />
   
<category android:name="android.intent.category.DEFAULT" />
 
<data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
</intent-filter>

 첫번째 인텐트 필터는 VIEW 혹은 EDIT 라는 액션 값을 갖고, DEFAULT 카테고리라는 값을 갖으며, 구글의 Note 타입 데이터를 갖는 메세지를 수신하겠다고 선언하는 인텐트 필터입니다. 또한, 두번째 인텐트 필터는 INSERT 라는 액션 값을 갖고, DEFAULT 라는 카테고리 값을 갖으며, 구글 Note 목록 형식의 데이타 타입을 갖는 인텐트를 수신하겠다는 뜻이지요.

 인텐트는 보통 다섯 가지 구성요소 (Component/Action/Data/Category/Extra) 로 이루어 지고, 각각의 구성요소가 저마다 우선순위가 있고, 여러가지 미리 정의된 값들이 있긴 합니다만 (상세한 내용은 구글 개발자 사이트를 참고 하시면 좋습니다.), 본질적으로는 어떤 '의도' 를 내포하는 '메세지' 에 불과합니다. 또한, 인텐트 필터는 여러가지 메세지중 특정 '의도' 를 갖은 '메세지' 만을 받겠다는 선언에 불과한 것이지요.

 예를 들어, 개인의 휴대폰 번호는 나와 관계된 메세지만을 수신하는 인텐트 필터로 생각 할 수 있겠고, 도미노 피자의 주문 센터는 전국 어느 매장이든 관계 없이 도미노 피자를 주문하고자 하는 메세지를 수신하기 위해 큰 범위의 인텐트 필터를 갖고 있다고 생각할 수 있겠습니다. 

3. Intent Routing?
  인텐트는 '의도를 갖는 메세지' 인텐트 필터는 '특정 의도를 수신하겠다는 선언' 이라고 한다면, 인텐트와 인텐트 필터를 서로 연결 해주는 과정이 필요합니다. 그 것이 바로 인텐트 라우팅 과정이라고 할 수 있습니다. 

  즉, 인텐트 라우팅이란, 특정 인텐트가 주어지면, 해당 인텐트 - 의도 에 가장 잘 부합하는 인텐트 필터를 갖은 단 하나의 컴포넌트를 찾아주는 일입니다. 이때, 메세지 자체에 해당 메세지를 전달해야하는 수신처(컴포넌트 이름)가 명확하게 명시되어 있다면, 별다른 고민없이 해당 컴포넌트에 인텐트를 전달하면 됩니다. 이런 경우를 명시적 인텐트(Explicit Intent)라고 합니다. 만일 그렇지 않을 경우, 추가적으로 주어진 단서(Action/Data/Category)를 기반으로 가장 적합한 하나의 컴포넌트를 찾아내야 합니다. 이른바 암시적 인텐트(Implicit Intent) 입니다.


<기본적으로 인텐트는 반드시 하나의 컴포넌트에게만 전달되어야 한다> 

 이 과정은 몇 가지 규칙에 의해 이루어 지는데, (예를 들어, 인텐트에 액션 값이 명시되어 있다면, 인텐트 필터에 해당 액션 값이 명시되어 있어야 한다. 등등) 모든 규칙을 적용한 후에도, 해당 '의도'를 정확하게 만족시키는 컴포넌트가 여러개일 경우, 사용자에게 여러 후보 중 하나만 고르라고 위와 같은 UI 가 나타납니다. 

4. Behind the Scenes
 인텐트에 관련되어 가장 놀라운 점은, 특정 인텐트를 전달하고자 할 때, 해당 인텐트를 전달받아야 하는 대상 컴포넌트가 생명주기 상 어떤 단계에 있건 관계없이 (컴포넌트가 작동중인 상태이던 그렇지 않던...) 해당 인텐트를 수신할 수 있다는 점입니다. 

 예를 들어, 개발자가 'ACTION_VIEW' 라는 액션 값을 갖고, 'image/jpeg' 라는 타입을 갖는 인텐트를 하나 생성해서, startActivity() 함수를 통해 해당 인텐트를 전달 한다면, 이미지 뷰어 엑티비티가 화면에 나타납니다. 해당 엑티비티가 이미 실행중이던, 그렇지 않던 관계 없이 말이지요. 비유하자면, 휴대폰이 꺼져있어도 만일 누군가가 나에게 전화를 걸면 휴대폰이 자동으로 켜지는 것과 비슷한 일입니다. 

 이러한 일은 어떻게 가능할까요?

 답은 어쩌면 뻔하면서도 단순합니다. 안드로이드 어플리케이션 프레임워크단에서 세 가지 중요한 일을 수행해 주고 있기 때문입니다. 첫째로 각각의 컴포넌트들이 어떤 메세지를 수신하고자 하는지 그 내용(인텐트 필터) 를 모두 모아서 하나의 주소록처럼 관리하는 일을 수행합니다. 두 번째는, 각 컴포넌트들이 전송하는 인텐트를 우선 수신한 후, 보관된 주소록을 바탕으로 대상 컴포넌트를 찾는 역할을 수행합니다. 마지막으로, 인텐트를 수신해야할 해당 컴포넌트의 상황에 따라 적절한 일을 수행합니다. (예를 들어 작동중이 아닌 컴포넌트는 새롭게 시작시키고, 이미 백그라운드에서 작동중인 컴포넌트인 경우 포그라운드로 옮겨 주는 등...) 


<인텐트 메카니즘에서 핵심 역할을 수행하는 것은 ActivityManager 와 PackageManager 이다>


 안드로이드 플랫폼에서 첫번째 일을 수행하는 것은 PackageManagerService 이고, 두번째와 세번째 일을 수행하는 것은 ActivityManagerService 입니다. 또한 이 두개의 서비스는 시스템 서비스로, 안드로이드 플랫폼이 시작되면 항상 실행되는, 시스템 프로세스 내에서 상주하고 있습니다. 

 PackageManager 는 최초 실행 시, 안드로이드플랫상에서 어플리케이션이 설치되는 특정 경로들(system/app, data/app 등등)을 검색해, 설치된 APK 파일들을 모두 모은 후, 해당 패키지 파일에 포함되어 있는 메니페스트 파일의 내용을 파싱합니다. 특정 패키지에 들어있는 Activity / Service / BroadcastReceiver 들의 각종 권한 정보, 인텐트 필터정보 등등을 모두 수집하여 메모리 상에 관리합니다. 또한, 새롭게 패키지가 추가되거나 삭제될 때 발생하는 Broadcast Intent 를 수신하며, 관리하고 있는 주소록을 업데이트하는 일도 동시에 수행합니다.

 ActivityManger 는 한층 더 다양한 일을 수행합니다. 안드로이드 어플리케이션 컴포넌트가 인텐트를 전달하기 위해 사용하는 API 는 크게 startActivity / startService / sendBroadcast 세 가지로 나누어 집니다. 그리고, 이러한 API 가 호출될 때, 최종적으로 호출되는 API 는 ActivityManagerService 단의 API 들입니다. 세가지 API 모두 IBinder 를 통해 Remote Server 의 API 를 호출하는 샘입니다. 다시말해, 안드로이드 플랫폼 상에서 발생하는 모든 인텐트는 우선적으로 ActivityManagerService 에 전달되는 것이지요. 

 ActivityManagerService 는 인텐트를 수신 받으면, PackageManagerService 에 해당 인텐트에 가장 잘 부합하는 컴포넌트가 무엇인지 물어 봅니다. 그리고 그 결과를 바탕으로, 인텐트를 타켓 컴포넌트에게 전달해 줍니다. ActivityManagerService 는 말 그대로, 작동중인 컴포넌트들의 상태 정보 추가로, 활성화된 패키지들의 상태 정보들을 모두 관리하고 있기 때문에, 타겟 컴포넌트의 현재 상태를 확인 할 수 있습니다. 때문에, 특정 컴포넌트가 잠들어 있는 상태라면 해당 컴포넌트를 되살린 후에, 인텐트를 전달하게 됩니다. (DHL 도 울고 갈만큼 투철한 서비스 정신입니다.)


<전화한통으로 원하는 메세지를 상대방에게 확실하게 전달하는 방법>

 즉, 개념적으로 살펴 보자면, 모든 '인텐트' 는 특정 컴포넌트에게 직접 전달되는게 아니고, 택배회사와 같은 ActivityManager 와 PackageManager 를 통해, 위탁 전달된다고 생각 할 수 있습니다. 

블로그 이미지

By훈트

,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


<Intent 의 변신. PendingIntent 와 IntentSender>

 안드로이드 인텐트 관련해서, 두 가지 신기한 클래스가 제공됩니다. 이름만 들어서는 그 쓸모가 무엇인지 애매한, PendingIntent 와 IntentSender 가 바로 그 주인공입니다. 개발자 사이트에 서술된 내용을 살펴보아도 두 가지가 하는일이 정확히 무엇인지, 그리고 특히, 둘 사이의 차이점이 무엇인지 좀 헷갈립니다. 두 가지 클래스는 어떤 역할을 수행하고, 어떤 차이점이 있으며, 안드로이드 어플리케이션을 개발할 때, 어떻게 유용하게 사용될 수 있는지 간단하게 정리해 보았습니다. 


1.PendingIntent

 PendingIntent 은 비교적 이해하기 쉽습니다. 커뮤니케이션에는 세 가지 기본 요소가 있습니다. 메세지, 송신자, 수신자. 인텐트는 메세지 입니다. 수신자는 해당 인텐트를 수신하기 위한 인텐트 필터를 갖고 있는 컴포넌트 입니다. 송신자는 인텐트를 보내기 위한 API (startActivity/startService 등)를 호출한 컴포넌트입니다. 


<부디 나쁜데 쓰지 말고 내가 시킨것만 잘 하시오...>

 PendingIntent 는 인텐트를 전송하고자 하는 '송신자'가 인텐트를 하나 생성한 후, 별 도의 컴포넌트에게 '이 인텐트를 나중에 나 대신 보내 주렴.' 하고 전달하고자 할 때 사용되는 클래스입니다. 즉, 내가 친구에게 은행 통장에서 돈을 대신 뽑아달라고 부탁하며, 뽑을 돈의 액수를 알려주고 (인텐트), 내 카드를 빌려주는 것과 비슷한 개념이라고 생각 할 수 있습니다. 당연히, PendingIntent 를 사용할 때는 내가 맡긴 카드가 악용되지 않도록, '권한' 문제에 관해서 신경을 기울일 필요가 있으며, 이와 동시에 안드로이드 플랫폼 상에서도 PendingIntent 의 권한 문제를 제어할 수 있는 API는 물론이고, 다양한 FLAG를 제공해 주고 있습니다. 

int FLAG_CANCEL_CURRENT 이전에 생성한 PendingIntent 는 취소하고, 새롭게 하나를 만듭니다. (친구에게 예전에 빌려준 카드를 정지 시키고 새롭게 하나 신청합니다.)
int FLAG_NO_CREATE 현재 생성된 PendingIntent 를 반환 합니다. (친구 보고 내가 빌려준 카드를 당장 가져와 보라고 요청합니다. 이 후에, 해당 카드를 회수 할 수도 있습니다.)
int FLAG_ONE_SHOT 이 플래그를 이용해 생성된 PendingIntent 는 단 한번 밖에 사용될 수 없습니다. (일회용 카드)
int FLAG_UPDATE_CURRENT 만일 이미 생성된 PendingIntent 가 존재 한다면, 해당 Intent 의 내용을 변경합니다. (친구에게 전화해서, 인출할 돈의 액수를 다시 알려줍니다.)

 따라서, 저는 의미적으로 'PendingIntent' 를 '위임된 인텐트' 라고 해석할 수 있지 않을까 생각합니다.  PendingIntent 는 여러가지로 유용하게 사용될 수 있는데(여러가지 이유로 특정 패키지만 사용가능한 컴포넌트를 다른 컴포넌트와 상호 작용시킬 필요가 있는 경우 등....)그 중에서도, 안드로이드 화면 상단에 위치한 'Notification Bar' 와 상호 작용하는 어플리케이션을 작성할 때 널리 사용됩니다.

 예를 들어, 인터넷 상에서 음악을 다운로드 받는 어플리케이션이 있습니다. 음악 다운로드가 진행 중일 때, 'Notification Bar' 에 진척 상황이 나타나도록 구현하였습니다. 그리고 다운로드가 완료된 후에 사용자가 해당 알림 정보를 클릭하면, 다운로드 완료된 음악이 재생됩니다. 얼핏 생각해보면 'NotificationBar' 가 특정 아이콘이 클릭되었을 때, 어떻게 알고 음악 재생 Activity 를 띄우는지 신기하게 여겨질 수도 있습니다. 이 과정은 우리가 'Notification Manager' 에게 특정 아이콘을 등록함과 동시에 해당 아이콘이 클릭되는 순간에 전달되어야 PendingIntent 를 넘겨 주기 때문에 가능합니다. 

public void setLatestEventInfo (Context context, CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent)


 위와 같이, Notification 클래스에는 해당 Notification 이 수행할 PendingIntent 값을 설정할 수 있도록 되어 있습니다. 즉, PendingIntent 를 잘 사용하면, 다른 컴포넌트들과 상호 작용할 때, 실재로 수행할 일 자체를 PendingIntent 를 통해 추상화 할 수 있음으로(음악을 재생해라... 라는 구체적인 명령 대신, 전달받은 PendingIntent 를 수행하라 식으로...), 보다 더 확장성있는 어플리케이션 컴포넌트를 구성할 수 있습니다.

2.IntentSender
 IntentSender 는 좀 애매합니다. 무엇보다도 IntentSender 가 사용되는 꼭 맞는 샘플 코드를 찾기가 어렵더군요. IntentSender 를 생성하는 방법도 조금 색다른데, PendingIntent 를 우선 생성한 뒤, getIntentSender() API 를 호출해야합니다. 처음에는 IntentSender 는 좀 더 추상화된 PendingIntent 가 아닐까 생각했습니다. (두 클래스 간의 직접적인 상속관계가 있는 것은 아니지만...)

 하지만, 여러가지 자료를 좀 뒤져보고 (내용은 거의 없지만...) 결정적으로 안드로이드 프레임워크 소스를 살펴본 결과 허무한 결론을 내리고 말았습니다. IntentSender 는 PendingIntent 의 쌍둥이 클래스 입니다. 말하자면, PendingIntent 에 비하면 조금 못난... 쌍둥이 동생이라고 할 수 있습니다. 위임 받은 인텐트를 전달 할 수 있다는 점에서는 PendingIntent 와 정확하게 동일하지만, PendingIntent 와 비교하면 사용하기가 좀 어렵고 결정적으로 '권한' 을 관리하기 위한 API 를 제공해 주지도 않습니다. 

<멸종 위기 클래스... IntentSender. 다음 버전에는 없어지지 않을까요?>

 이 녀석 도데체 어디에 쓰이는 걸까요? 결론은 쓰이는 곳이 없다;;; 입니다. 안드로이드 풀소스를 뒤져봐도 사용되는 곳은 전무합니다. 심지어 IntentSenderTest 에서도 사용되는 것은 PendingIntent 뿐입니다. 혹시라도 저처럼 IntentSender 가 뭐에 쓰이는 물건인고... 하고 궁금해 하시는 분이 있다면, 아 그 못난 PendingIntent 쌍둥이 동생? 정도로 가볍게 넘어가셔도 좋을 듯 하네요.



블로그 이미지

By훈트

,