posted by By훈트 2010.08.17 17:04

 안드로이드는 저에게 굉장히 생소한 플랫폼입니다. 기존 어플리케이션과는 다른 라이프 사이클, 서로 다른 어플리케이션이 상대방 구성요소를 자유롭게 활용할 수 있는 점, 공통된 자원 접근 경로로서 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 를 통해, 위탁 전달된다고 생각 할 수 있습니다. 

신고
크리에이티브 커먼즈 라이선스
Creative Commons License
posted by By훈트 2010.08.17 17:01


<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 쌍둥이 동생? 정도로 가볍게 넘어가셔도 좋을 듯 하네요.



신고
크리에이티브 커먼즈 라이선스
Creative Commons License
posted by By훈트 2010.08.17 13:42
[훈트 2010-08-17 화요일]

구글의 안드로이드OS를 탑제한 스마트폰인 안드로이드폰.
경쟁사인 애플의 아이폰과 상반되는 여러가지 특징 중 다음 특징에 대해 이야기하고자 한다.
  • 다양한 회사제품
  • 다양한 디자인 및 해상도
스터디 위주로 안드로이드 프로젝트를 하던중 치과 솔루션 업체에 취업을 하게 되었다. 
이번에 취업된 회사에서 C#기반으로 제작된 치과솔루션 프로그램을 안드로이드폰과 곧 추가될 안드로이드 OS를 탑제한 타블렛을 겨냥해 안드로이드 버젼으로 개발하는 프로젝트를 진행하게 되었다.
테스트 데모버젼을 만들기위해 그동안 스터디 위주의 학습에선 간과했던 안드로이드폰의 다양한 해상도에 고민을 하게됬다.

현재 국내에 출시되어 있는 핸드폰 기기와 출시예정인 폰중 인기 폰들만 해도 10여종에 이른다. 최고의 인기를 얻고있는 
  • 삼성 - 갤럭시A, 갤럭시S
  • 팬텍 - 시리우스, 이자르, 베가
  • HTC - 디자이어, 넥서스원
  • 모토로라 - 드로이드, 모토글램, 드로이드x

위에 폰 외에도 여러가지 출시및 출시예정된 폰들이 있는데 이들은 각각 지원하는 해상도가 다르다.
WVGA, HVGA, FWVGA 등 지원 해상도에 따라서 가로와 세로의 비율 등이 차이가 나게되어 어플 개발시 UI설계에 고민이된다.

해상도 UI에  대해서 알고 싶다면 안드로이드펍의 운영을 맡고있는 회색님의 글의 첨부 PDF파일을 참고하자.


위의 해상도관련 PDF를 보면 안드로이드의 UI 를 위해 XML코드 작성시 px 단위가 아닌 dip 라는 단위의 사용을 권장하고있다.

예를들어 로고화면을 메인에 ImageView로 넣고싶다고 가정하자. 
일단 테스트로 필자가 가지고있는 WVGA(480x800)해상도를 지원하는 팬텍의 시리우스에 테스트를 목적으로 작성한다고 하자.
가로 480px 세로 800px 이므로 로고의 크기를 가로 360px 세로 300px의 크기로 만들어 화면에 상단에 배치하고 싶다.

위에 링크의 PDF파일을 읽었다면 실제 xml 작성시 이 ImageView에 가로와 세로 속성을 dip단위로 작성해야 하는데..
문제는 여기서 생긴다, 그렇다면 이 px와 dip의 단위는 보통 1:1로 매핑되지 않기때문에 변환 작업을 거쳐야한다. 그렇기 때문에
로고 이미지를 제작할때 이미지의 px 크기에 맞춰 xml에 dip크기를 주기가 쉽지않다. 
물론 HVGA의 경우에는 dpi(dots per inch)의 크기가 기본 px과 1:1로 매핑되는 160dpi 이기 때문에 이미지 px의 크기 그대로 xml의 dip 크기로 작성이 가능하지만,
갤럭시S나 시리우스가 지원하는 WVGA의 경우는 240dpi를 지원하기 때문에 이미지의 px을 그대로 xml의 dip로 가져올수 없다.
쉽게 말해서 360x300의 px단위의 이미지를 xml에 ImageView의 크기를 width: 360dip, height: 300dip 로 가져올수 없다.
dpi를 고려하여 변환을 거쳐 width: 240dip, height: 200dip 으로 설정해야 이미지가 일그러지거나 깨지지 않고 그대로 보여줄수 있게 된다.

이런 작업을 위해서 다양한 해상도의 기기별로 변환식에 px을 대입해 해당 매치되는 dip값을 얻어야 하는데 다양한 해상도 별로 모두 하기에 상당히 번거로운 과정이다. 그래서 점심시간을이용해서 간단하게 엑셀로 안드로이드 폰 DB 라는 이름으로 작성을 해보았다.


엑셀을 처음 사용해보는거라 많이 부족하지만 필요한것 위주로 만들어보았다.
왼쪽 파란색 타이틀은 기본 폰 정보이고 
오른쪽 보라색 타이틀은 xml에서 사용하기위한 변환 수치 정보이다.

입력 dip, 입력 px에 수치를 적으면 결과px, 결과dip에 왼쪽에 기기의 해상도 정보에 맞춰 변환되어 나온다.
기기및 해상도를 추가하고싶다면 한 행의 셀을 복사해서 제일 아래 추가하고
  • 스크린타입
  • 스크린크기(px) - 가로, 세로
  • 스크린 밀도(dpi)
위의 3가지 수치 정보만 바꿔주고 
선택사항으로 기타정보인 제조사,기종,통신사,스크린타입만 바꾸어주면 
원하는 기기를 등록하여 활용할수 있다.

해당 Exel 파일은 따로 이 글에 첨부하였다.


안드로이드 화면사이즈 DB.xlsx 파일은 만약 저장이 알집형태로 저장된다면 뒤에 알집 확장자를 지우면된다.
안드로이드 화면사이즈 DB.xlsx.zip 으로 저장된다면 이름바꾸기에서 .zip을 지운후 열면된다


Ps. 엑셀파일 작성이 처음이라 작성및 데이터입력까지 1시간넘게걸렸다. 거기에 글쓰는데 점심시간 30분..
힘내라고 코맨트 달아주면 앞으로 힘내서 더 좋은정보 공유하겠습니다 ㅋ



신고
크리에이티브 커먼즈 라이선스
Creative Commons License

티스토리 툴바