본문 바로가기

어플리케이션 구성/인텐트(Intent)

내가 누군지 말해줘! - 인텐트 필터 (Intent Filter)


암시적 인텐트(Implicit Intent)를 처리하려면 인텐트 객체에 들어 있는 여러 정보를 이용해야 한다는 것을 앞에서 알아보았습니다. 인텐트 객체 내의 정보들을 통해 호출해야 할 컴포넌트의 정보를 알았으니, 그 다음엔 이 정보를 바탕으로 각 컴포넌트별로 비교를 해보며 적절한 컴포넌트를 찾아야 합니다.

이렇게 인텐트 객체 내의 여러 정보들을 바탕으로 가장 적절한 컴포넌트를 찾는 과정을 인텐트 해석(Intent Resolving)이라 합니다.  이 과정에서 인텐트 객체의 정보와 각 컴포넌트의 정보를 비교하게 되며, 이을 위해 각 컴포넌트는 자신이 받을 수 있는 인텐트의 종류를 메니페스트 파일에 정의합니다. 이를 인텐트 필터(Intent Filter)라 합니다,
 
안드로이드 시스템 내부에서는 수많은 어플리케이션들에 의해 수많은 인텐트들이 발생합니다. 이 중에서 자신에게 필요한 인텐트만을 받기 위해 인텐트 필터가 있는 것이죠.



하지만, 인텐트 필터가 없다고 해서 인텐트를 받을 수 없는 것은 아닙니다. 인텐트 필터는 인텐트 해석 과정이 필요한 암시적 인텐트(Implicit Intent)를 받을 때만 필요할 뿐, 호출 대상 컴포넌트가 명시되어있는 명시적 인텐트(Explicit Intent)는 이러한 인텐트 필터가 없어도 대상 컴포넌트를 호출할 수 있습니다.

- 어플리케이션간 경계가 없는 안드로이드

명시적 인텐트를 사용하면 굳이 인텐트 필터를 정의할 필요도 없고, 간단히 컴포넌트를 호출할 수 있는데 왜 암시적 인텐트가 있는 것일까요? 그 이유는 바로 "명시적" 인텐트의 이름에서도 알 수 있듯이, 명시적 인텐트는 호출 대상 컴포넌트의 이름을 정확히 알아야 하기 때문입니다.

안드로이드가 다른 시스템과 가장 다른 점이 바로 "어플리케이션 간에 경계가 없다"는 것입니다. 이는, 서로 다른 어플리케이션 내의 컴포넌트라 할지라도 사용자가 사용하는 입장에서 볼 때에는 마치 하나의 어플리케이션처럼 동작하는 것이죠. 

 


위의 영상에서는 바코드 스캐너와 책 관리 어플리케이션이 예로 나옵니다. 바코드 스캐너 어플리케이션은 책 관리 어플리케이션 내에 포함되어 있지 않지만, 마치 책 관리 어플리케이션의 일부인 것처럼 사용하는 것을 확인할 수 있습니다. 이런 식으로, 자신이 필요하다면 다른 사람이 만든 어플리케이션 내의 컴포넌트를 사용할 수 있으므로 개발 시간을 줄여줄 뿐 아니라, 똑같은 걸 다시 만들 필요가 없으니 효율도 증가하겠죠?

- 왜 암시적 인텐트를 사용하는가?

자, 이제 본격적인 문제는 여기서부터입니다. 자신이 만든 컴포넌트라면 컴포넌트의 이름을 정확하게 알 수 있겠지요. 하지만, 다른 사람이 만든 컴포넌트의 이름을 알기란 어렵습니다. 즉, 명시적 인텐트를 사용하여 해당 컴포넌트를 호출할 수가 없죠. 따라서 암시적 인텐트를 통해 컴포넌트를 호출하게 됩니다.

암시적 인텐트를 사용하는 이유는 이 뿐만이 아닙니다. 안드로이드가 다른 시스템과 다른 것 중 또 하나는 바로 Native 어플리케이션 (Home, Dialer 등...)까지도 사용자가 별도로 제작한 것으로 대체할 수 있다는 것입니다.

Native 어플리케이션을 호출할 때 만약 명시적 인텐트를 사용한다면 사용자가 만든 어플리케이션으로 Native 어플리케이션을 대체하는 것은 불가능하겠지요? 하지만, 암시적 인텐트를 사용하여 호출 대상 컴포넌트가 처리해야 할 작업만을 명시한다면 특정 작업의 처리를 꼭 하나의 어플리케이션만 하는 것이 아니라, 여러 가지의 어플리케이션을 통해 할 수 있게 됩니다. 마치 우리가 그림 파일을 편집할 때 포토샵을 쓰는 사람도 있고, 페인트샵을 쓰는 사람도 있는 것과 마찬가지죠.

- 인텐트 필터의 구성요소

인텐트 필터는 인텐트 객체 내의 정보들을 바탕으로 인텐트를 필터링하기에 인텐트 객체 내의 정보들을 바탕으로 자신이 받을 수 있는 정보들을 정의하며. 이 정보들 중 인텐트 필터에서 주로 필터링하는 항목은 action, data (데이터 주소 유형 및 데이터 타입), category입니다.

이러한 인텐트 필터의 내용들을 어플리케이션이 실행되기 전에 안드로이드 시스템에서 알고 있어야 다른 어플리케이션에서 해당 어플리케이션의 컴포넌트를 필요로 하는 인텐트를 발생시켜도 해당 컴포넌트를 실행시킬 수 있습니다. 때문에, 이러한 인텐트 필터의 내용들은 메니페스트 파일인 AndroidManfest.xml 파일의 각 컴포넌트 태그 내에 정의됩니다.

        <activity android:name=".LifeCycleTester"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

지금부터는 인텐트 필터 내의 각 속성들에 대해 하나씩 알아보도록 하겠습니다.

1. action (액션) 필터

인텐트 객체 내의 action을 검사하여 인텐트 필터에 정의된 액션과 일치하는지 여부를 검사합니다. 이 검사를 통과하려면 인텐트 객체 내의 액션이 인텐트 필터에 정의된 액션과 일치해야 합니다. 단, 인텐트에 액션이 아예 정의되어있지 않은 경우에는 액션 필터를 통과할 수 있습니다.


위에서 보면 인텐트 객체에 정의된 액션의 이름과 인텐트 필터에 정의된 액션의 이름이 조금 다른데, 사실 표현 방식만 다르지 결국은 똑같은 값입니다. Intent 클래스의 API를 보면, Intent 객체 내의 상수로 정의된 ACTION_VIEW 의 실제 값은 android.intent.action.VIEW 인 것을 확인할 수 있습니다.


2. category (카테고리) 필터

인텐트 객체 내의 category 항목을 검사하여 인텐트 필터에 정의된 카테고리와 일치하는지 여부를 검사합니다. 액션 검사에서는 인텐트 객체 내에 아예 액션이 정의되어있지 않은 경우 액션 검사를 통과할 수 있었던 것에 반해 카테고리 검사는 인텐트 객체에 정의된 카테고리가 인텐트 필터에 정의된 카테고리들과 일치해야 합니다.

예를 들면, 인텐트 필터에서 CATEGORY_DEFAULT와 com.androidhuman.TEST_CATEGORY가 정의되어 있을 경우 카테고리로 CATEGORY_DEFAULT를 가지는 인텐트 객체, com.androidhuman.TEST_CATEGORY를 가지는 객체, 혹은 둘 다 가지고 있는 인텐트 객체가 카테고리 검사를 통과할 수 있습니다. 하지만, CATEGORY_DEFAULT는 가지고 있는데 com.androidhuman.NEW_CATEGORY를 가지는 인텐트 객체의 경우 인텐트 필터에서 NEW_CATEGORY가 정의되어있지 않으므로 카테고리 검사를 통과할 수 없습니다.

이 때문에, 암시적 인텐트를 만들 때 카테고리를 추가하지 않을 경우 안드로이드에서 자동으로 CATEGORY_DEFAULT(android.intent.category.DEFAULT)를 추가해주게 됩니다. 때문에 카테고리를 특별히 추가해주지 않은 인텐트를 받을 수 있게 하려면 카테고리 필터에 android.intent.category.DEFAULT 를 추가해줘야 합니다. 그렇지 않으면 어떠한 암시적 인텐트도 받을 수 없게 됩니다.



3. data (데이터) 필터

인텐트 객체 내의 data 항목 및 type을 검사하여 인텐트 필터에 정의된 값과 비교하여 일치 여부를 검사합니다.
데이터 검사는 크게 데이터의 주소(URI)를 검사하는 부분과 데이터의 유형(type, MIME type)을 검사하는 부분으로 나누어집니다.

데이터의 주소를 검사하는 부분은 데이터의 주소를 세분화하여 검사할 수 있도록 되어있습니다. URI(Uniform Resource Identifier)는 다음과 같은 구조로 구성되어있습니다.

scheme://host:port/path

만약, http://google.com 을 각 요소별로 나누어본다면 schemehttp, host google.com 이 되겠죠? 이와 마찬가지로 안드로이드 시스템에서 사용하는 주소들도 각각의 요소들로 나눌 수 있습니다.
예를 들어 content://com.androidhuman.provider.SimpleMemo/memos/1 같은 주소의 경우 schemecontent, hostcom.androidhuman.provider.SimpleMemo, pathmemos/1 이 되겠지요??
데이터의 유형을 필터링하는 것은 type(mimeType)을 이용합니다. 보통 아래와 같이 정의됩니다.

<data android:mimeType = "video/mpeg" android:scheme = "http">
<data android:mimeType = "audio/*" android:scheme = "http">

mimeType은 위와 같은 형식으로 정의되며, 큰 범주/큰 범주의 하위 범주 형식으로 정의됩니다. (video/mpeg의 경우 큰 범주가 video, video의 하위 범주가 mpeg) 하위 범주에는 와일드카드 문자(*) 를 사용하여 해당 범주 내의 모든 형식을 허용할 수도 있습니다.

이런 방식으로 첫번째 <data> 필터를 해석하면 "http 스키마를 가진 mpeg 형식의 비디오 데이터"를 가진 인텐트를 허용하는 것임을 알 수 있고, 두번째 필터는 "http 스키마를 가진 모든 오디오 데이터"를 가진 인텐트를 허용하는 것임을 알 수 있습니다.


지금까지 인텐트 객체 및 인텐트 필터의 각 필터들 및 인텐트 필터의 통과 조건들에 대해 알아보았습니다. 다음 강좌에서는 NotePad 예제에 정의되어있는 인텐트 필터들을 보고 해석하면서 오늘 배운 내용들을 다시 한번 정리해보도록 하겠습니다. :)