명시적 인텐트(Explicit Intent)
요즘엔 단일 Activity로 앱 개발이 주로 이루어지지만, 초기에는 모두 Activity로 만들어졌었다. 하나의 앱내에서도 Activity를 띄우기 위해선 startActivity()를 사용하는데, 인자로 Intent객체를 넘겨줬다.

Activity를 띄우는 예제 코드를 보면 다음과 같다.
val intent = Intent(this, DisplayMessageActivity::class.java).apply {
putExtra(EXTRA_MESSAGE, message)
}
startActivity(intent)
Intnet 객체를 생성하는 첫번째 인자는 Context이고, 두번째는 불러올 Activity의 클래스 이름이다. “::class.java” 는 무엇일까? ::class 는 Kotlin에서 KClass를 리턴해주는 reflection 문법이다. 안드로이드 Intent는 java class를 인자로 받기 때문에, KClass를 바로 넘길 수 없다. .java는 KClass에서 Java class레퍼런스를 얻어오는 문법이다. Kotlin 공식문서에 다음과 같이 기술하고 있다.
On JVM: a Kotlin class reference is not the same as a Java class reference. To obtain a Java class reference, use the
.java
property on aKClass
instance.
이와같이, 실행할 Activity를 명시해주는 방법이 명시적 인텐트(Explicit Intent)이다. Activity외에도 startService()나 bindService()를 이용해 Service를 실행할 수도 있다.
암시적 인텐트(Implicit Intent)
이와 대조적으로, 실행할 Activity나 Service를 명시하지 않고 약속된 Intent를 보내면, 안드로이드 시스템에서 해당하는 인텐트를 실행할 application을 실행해주는 사용법이 있다. 이것이 암시적 인텐트(Implicit Intent) 라고한다. 암시적 인텐트의 예는 다음과 같이 text message를 보내는 방법이 있다.
// Create the text message with a string.
val sendIntent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, textMessage)
type = "text/plain"
}
// Try to invoke the intent.
try {
startActivity(sendIntent)
} catch (e: ActivityNotFoundException) {
// Define what your app should do if no activity can handle the intent.
}
Intent에 클래스를 명시하지 않지만, “Intent.ACTION_SEND”라는 action을 주고 추가 데이터로 “Intent.EXTRA_TEXT”에 보낼 텍스트 메세지를 추가하고 있다. 그리고 type을 “text/plain”으로 지정하고 있는데, 이 데이터가 어떤 데이터인지 알려주는 mime type을 의미한다. startActivity()에 이 인텐트를 인자로 보내면, 안드로이드 시스템에서 적합한 앱을 실행해 주는 식이다. Intent에 다양한 옵션이 들어간걸 볼 수 있었는데, 차차 알아보자.
인텐트의 작동 방식
이 기묘하기도하고, 같은 application내의 Activity나 Service를 부를 때도 이렇게 사용하는게 의아할 수 있는데, 이게 안드로이드의 설계방식이었다. 안드로이드의 초기 디자인에서 4개의 컴포넌트로 구성이 되게 만들었는데, Activity, Service, Content Provider, Broadcast Receiver이다. 실행단위가 하나의 어플리케이션으로 보기보다, 하나의 컴포넌트로 보이도록 했고, 앱간의 경계없이 이 컴포넌트간 일종의 원격 통신 수단이 Intent인 것이다. 앱간의 경계를 넘어서기 때문에 이러한 구현을 위해 당연히 안드로이드 시스템이 개입하게 된다.
이렇게 인텐트를 보내면 이 인텐트를 시스템이 받고, 어떤걸 실행할지 판단하여 Activity든 Service든 BroadcastReceiver든 실행하게 된다. startActivity()를 통해 다른 Activity를 실행하는 모습을 도식적으로 보면 다음과 같다.

- 액티비티가 인텐트를 생성해서 startActivity()를 통해 보낸다.
- 안드로이드 시스템이 intent filter를 통해 intent에 매칭되는 모든 앱들을 찾는다.
- 매칭되는 Activity를 찾으면, 해당 Activity를 실행하고 Intent를 넘겨준다.
명시적 인텐트(Explicit Intent)에 대해선 실행할 대상을 명확하게 기술하기 때문에, 위에서 말하는 Intent filter를 통해 intent에 매칭되는걸 찾는 과정이 필요가 없다. 그렇다면, 암시적 인텐트(Implicit Intent)는 이 과정이 어떻게 진행되는걸까? 인텐트에는 부가적인 정보를 추가할 수가 있는데, 인텐트를 받는 앱 쪽에서 어떠한 인텐트를 받을지 manifest 파일에 intent-filter 정보를 추가하여 결정한다. 시스템에서 이 manifest파일을 보고 보내는 인텐트에 해당되면, 앱을 실행하게 된다. 이런 과정을 보기 위해, 우선 Intent의 구성을 알아보고 보내기 위해 어떻게 만드는지 알아보자.
Building an Intent
인텐트에는 여러가지 정보가 들어간다. 실행할 대상도 특정해야하고, 전달할 데이터 내용도 필요하고. 명시적인 경우엔 컴포넌트 이름을 직접 지정하지만, 암시적인 경우엔 인텐트를 전달할 대상을 필터링할 정보들도 필요하다. 처음 접할 때는 복잡하고 난해하게 느껴졌지만, 왜 필요한지 생각하면 그리 어려운 내용들은 아니다. 수행할 대상특정(Component name 또는 Class name), 암시적 인텐트의 경우 대상특정을 위한 필터링 정보(Action, Type, Category), 수행에 필요한 데이터(Data, Extra), 수행하는 Activity를 띄우는 방법(Flags)식으로 생각하면 된다.
- Component
명시적 인텐트(Explit Intent)의 경우에 사용된다. 암시적 인텐트에는 사용되지 않는다. setComponent(ComponentName)을 이용하거나 setClass(Context, Class)를 사용해 지정한다. 대부분 클래스를 사용하는데, Intent 생성자에 인자로 넘겨준다. - Action
어떤 동작을 수행할지 말해주는 문자열이다. ACTION_VIEW, ACTION_SEND등과 같이 기본적인 것들은 Intent 클래스에 정의 되어있다. 문자열이기 때문에, 사용자가 정의해서 사용할 수도 있다. 어떤 Action이냐에 따라 데이터가 달라질텐데, 이는 해당 action에 대한 문서를 참고하자. - Data & Type
Data는 Action을 수행할 대상 데이터에 대한 Uri 객체다. 예를 들어 ACTION_EDIT 액션이라면, 편집할 대상 문서 파일에 대한 레퍼런스 Uri를 넘겨주게 된다. Type은 데이터가 어떤 형태인지 알려주는 mime type이다.
Data와 Type이 항상 같이 쓰이는 것은 아니며, Data만으로 충분할 때는 type이 생략되기도 하고, type만 사용되기도 한다. 저 위에서 봤던 ACTION_SEND를 보면, 특별히 Uri로 지정할 파일이 없기 때문에, EXTRA_TEXT에 문자열이 들어가고 type만 “text/plain”으로 지정된걸 볼 수 있다. - Category
인텐트를 다루게 될 대상 컴포넌트에 추가되는 정보이다. 대부분 필요로 하는 항목은 아니지만, 특정 목적으로 사용된다. 예를 들면 다음과 같은 것이 있다.
– CATEGORY_LAUNCHER : 앱 실행시, 최초 실행되는 Activity. 시스템 런처에서 리스트로 표시된다.
– CATEGORY_BROWSABLE : 웹 브라우저에서 실행될 수 있는 Aitivity. 예를들어, 웹페이지에서 이메일 링크를 클릭했을 때, 실행되는 Activity는 이 카테고리를 필요로 한다. - Extras
key-value 형태의 Map 형식으로 추가적인 데이터를 전달한다. 데이터 내용은 Action에 따라 달라지며, 사용자 정의 행태에 따라서 임의로 사용도 가능하다. Bundle처럼 결국, 받는쪽에서 어떻게 풀어내는지 알아야 하는 거니까. putExtra()함수를 통해 Intent에 추가된다. 모든 데이터를 Bundle로 묶어서 putExtras()를 이용하는 것도 가능하다. 표준으로 사용되는 Extra 데이터는 Intent 클래스에 EXTRA_* 형태로 정의해 놓았다. 위에서 text message를 보내는 경우, EXTRA_TEXT가 이에 해당한다.
Bundle과는 달리, Parcelable, Serializable 데이터는 런타임 예외가 발생하므로 쓰지 말라고 되어있다.
Caution: Do not useParcelable
orSerializable
data when sending an intent that you expect another app to receive. If an app attempts to access data in aBundle
object but does not have access to the parceled or serialized class, the system raises aRuntimeException
. - Flags
Activity를 어떻게 띄울지, 띄운 Activity의 순서는 어떻게 처리할지 등등을 결정하도록 해주는 부분이다. setFlags(), addFlags() 를 통해 설정한다. 보통 Activity 순서 스택을 처리하기 위해 사용한다. 살펴보면, 다음과 같은 것들이 있다. 각각 비트플래그이므로, 혼용해서 사용이 가능하다.
먼저, Back stack과 task라는 용어를 간단히 설명하겠다. Activity는 기본적으로 실행하는 순서대로 쌓이게 된다. 또한, 동일한 Activity를 추가로 실행하면, 같은 Activity도 여러개 쌓일 수도 있다. 가령 A Activity가 B Activity를 시작하면 A위에 B가 올라가고, B에서 또 B Activity를 시작하면 그위에 B가 레이어처럼 또 올라간다. 이런식으로 Activity의 실행 순서대로 쌓이는게 Back stack이고(Back key 눌렀을 때 스택의 pop처럼 동작), 이 Activity들의 묶음을 하나의 task라고 한다. 이렇게 A->B->B로 쌓인경우, back key를 눌렀을 때, 역순으로 B->B->A로 위에서 하나씩 사라지게 된다.
만약, “FLAG_ACTIVITY_SINGLE_TOP” 플래그를 주게되면, task내에 같은 Activity가 있을경우, 해당 Activity를 끌어올려 top에 올려놓게 된다. 이경우 back key를 누르면 B->A순으로 동작한다.

만약, “FLAG_ACTIVITY_NEW_TASK”를 사용하게되면, 새로운 task를 생성해서 Activity가 추가되므로, 기존 Back stack과는 별도로 동작하게 된다. 좀 더 자세한 내용은 공식 문서 Tasks and the back stack을 참조.
사용되는 Flag들은 매우 많다. 이 Flag들을 적절히 조합해서 사용해야 하는데, 이에 대해선 별도의 포스팅을 작성해야할 것으로 보인다. 사용되는 경우에 각각 설명할 것이므로, 여기선 이정도로 넘어가자.
인텐트의 속성들은 알았으니, 보내는 코드를 살펴보자.
val downloadIntent = Intent(this, DownloadService::class.java).apply {
data = Uri.parse(fileUrl)
}
startService(downloadIntent)
위 코드는 서비스를 명시적으로 실행하는 방법이다. 인텐트의 생성자로 클래스 이름을 넘겨주고, data 속성에 파일의 uri를 지정하여 startService()로 인텐트를 전달하고 있다.
// Create the text message with a string.
val sendIntent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, textMessage)
type = "text/plain"
}
// Try to invoke the intent.
try {
startActivity(sendIntent)
} catch (e: ActivityNotFoundException) {
// Define what your app should do if no activity can handle the intent.
}
앞에서 봤던 예제와 동일한데, 다시 살펴보자. 암시적 인텐트 예제로, Intent()생성자에는 아무런 인자를 넘겨주지 않고, action, type, extra값을 설정하고 있다. 이렇게 만든 인텐트를 startActivity()를 통해 넘겨준다.
Receiving an implicit intent
인텐트를 받기 위해선, 시스템에 어떤 인텐트를 받을지 알려줘야한다. 그러기위해선 AndroidManifest.xml에 받을 컴포넌트 태그 안에서 <intent-filter> 항목을 작성하면 된다. 예제를 보자.
<activity android:name="ShareActivity" android:exported="false">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
위 예제는 “ACTION_SEND” 에 대한 인텐트를 받는 intent filter를 작성한 것이다. <intent-filter>안에 <action> 이름으로 “ACTION_SEND”에 대한 문자열을 기술하고, <data>에서는 mime type을 기술하고 있다. <category>는 “CATEGORY_DEFAULT”로 정의된 문자열이 들어갔다. 이렇게 하면, action이 “ACTION_SEND” 로 되어있고, data type이 “text/plain”인 인텐트만 받아들이게 된다. 암묵적 인텐트(Implicit Intent)의 경우에는 category를 “CATEGORY_DEFAULT”로 지정해야한다고 한다.
<intent-filter>를 살펴보면 이와같이 세가지로 이루어져있다.
- <action> : 받아들일 인텐트의 action을 지정
- <data> : 받아들일 인텐트 데이터의 타입을 지정. scheme, host, port, path, pathPattern, pathPrefix, pathSuffix, pathAdvancedPattern, mimeType 을 각각 지정할 수 있다.
- <category> : 받아들일 인텐트의 category를 지정. implicit intent의 경우, CATEGORY_DEFAULT로 지정해야한다.
중요한 부분이 남아있는데, 바로 <activity>태그 안에 있는 android:exported 속성이다. 이름 그대로, 해당 컴포넌트를 외부 앱이 실행할 수 있느냐를 결정하는 속성으로 true/false 값을 준다. intent-filter를 사용한다면, 이 값을 반드시 지정해야 한다. 꼭 필요한 경우가 아니면 보안을 위해 false로 지정해준다.
계속 이어서…
개략적인 인텐트에 대한 내용을 살펴봤다. 다음에는 Pending Intent에 대해 다뤄야 하며, 기본적인 Common Intents도 몇개 다루겠다. 너무 길어져서 일단 여기까지.