[Android] Intent, Intent Filter, PendingIntent 정리

본 내용은 'Manifest Android Interview'를 읽고 개인적으로 개념 복기를 위한 설명, 헷갈리거나 어려웠던 점 등을 책 외부의 공식문서등의 자료로 정리한 내용입니다.

 

안드로이드의 Intent는 컴포넌트 사이에서 작업을 요청하거나 데이터를 전달할 때 사용하는 메시지 객체입니다. 이번 글에서는 명시적 인텐트, 암시적 인텐트, 인텐트 필터, PendingIntent의 차이를 중심으로 정리합니다.


1. 개념 정리

Intent

Intent는 수행할 작업을 표현하는 객체입니다.

Activity를 실행하거나, Broadcast를 보내거나, Service를 시작할 때 사용할 수 있습니다. 또한 putExtra()를 통해 컴포넌트 사이에 데이터를 전달할 수 있습니다.

val intent = Intent(this, DetailActivity::class.java)
intent.putExtra("postId", 10)
startActivity(intent)

Intent 객체를 만든다고 바로 실행되는 것은 아닙니다. 실제 실행은 startActivity(), sendBroadcast(), startService() 같은 메서드를 호출할 때 발생합니다.


명시적 인텐트

명시적 인텐트는 실행할 컴포넌트를 직접 지정하는 인텐트입니다.

대상 Activity나 Service를 정확히 알고 있을 때 사용합니다. 주로 앱 내부 화면 이동에 사용합니다.

val intent = Intent(this, DetailActivity::class.java)
startActivity(intent)

이 코드는 DetailActivity를 직접 지정합니다. 시스템이 어떤 앱을 열지 찾을 필요가 없습니다.

사용 상황은 다음과 같습니다.

내 앱의 MainActivity에서 DetailActivity로 이동
내 앱의 특정 Service 실행
내 앱 내부 컴포넌트 간 데이터 전달

암시적 인텐트

암시적 인텐트는 실행할 컴포넌트를 직접 지정하지 않고, 수행할 작업만 선언하는 인텐트입니다.

예를 들어 “이 URL을 보여줘”, “이 텍스트를 공유해줘”처럼 요청하면 Android 시스템이 이 요청을 처리할 수 있는 앱을 찾습니다.

val intent = Intent(
    Intent.ACTION_VIEW,
    Uri.parse("https://www.example.com")
)

startActivity(intent)

Intent.ACTION_VIEW는 “보여줘”에 가까운 action입니다. URL, 지도, 이미지, PDF 등을 열 때 사용할 수 있습니다.

val intent = Intent(Intent.ACTION_SEND).apply {
    type = "text/plain"
    putExtra(Intent.EXTRA_TEXT, "공유할 텍스트")
}

startActivity(Intent.createChooser(intent, "공유할 앱 선택"))

Intent.ACTION_SEND는 “보내기” 또는 “공유하기”에 가까운 action입니다. 텍스트나 이미지를 다른 앱으로 공유할 때 사용합니다.

암시적 인텐트는 다음 요소를 조합해서 요청을 표현합니다.

action   = 어떤 작업을 할지
data     = 어떤 데이터를 대상으로 할지
type     = 데이터 형식이 무엇인지
extras   = 추가로 전달할 값

자주 쓰는 Intent action

ACTION_VIEW
보여주기. 웹페이지, 지도, 이미지, PDF 열기 등에 사용합니다.

ACTION_SEND
공유하기. 텍스트나 이미지를 다른 앱으로 보낼 때 사용합니다.

ACTION_SENDTO
특정 대상에게 보내기. 이메일, SMS처럼 목적지가 명확한 전송에 사용합니다.

ACTION_DIAL
전화 다이얼 화면 열기. 바로 전화하지 않고 번호가 입력된 화면을 엽니다.

ACTION_PICK
사용자가 하나를 선택하게 하기. 연락처나 이미지 선택 등에 사용합니다.

ACTION_GET_CONTENT
파일이나 콘텐츠를 가져오기. 이미지나 문서 선택에 사용합니다.

예를 들어 전화 앱을 열 때는 다음처럼 작성할 수 있습니다.

val intent = Intent(Intent.ACTION_DIAL).apply {
    data = Uri.parse("tel:01012345678")
}

startActivity(intent)

인텐트 필터

인텐트 필터는 특정 컴포넌트가 어떤 암시적 인텐트를 받을 수 있는지 Manifest에 선언하는 조건표입니다.

암시적 인텐트를 보내는 쪽은 “이 작업을 해줘”라고 요청하고, 받는 쪽은 “나는 이런 요청을 처리할 수 있음”을 인텐트 필터로 등록합니다.

<activity
    android:name=".ShareActivity"
    android:exported="true">

    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="text/plain" />
    </intent-filter>

</activity>

이 설정은 다음 뜻입니다.

ShareActivity는
ACTION_SEND 요청을 받을 수 있고,
text/plain 타입의 데이터를 처리할 수 있습니다.

Android 시스템은 암시적 인텐트가 발생하면 설치된 앱들의 인텐트 필터들을 읽어 비교합니다. 주로 action, data, category가 매칭 기준입니다.

보내는 앱
ACTION_SEND + text/plain

Android 시스템
설치된 앱들의 intent-filter 검색

받는 앱
조건이 맞는 Activity 후보로 표시

category.DEFAULT는 Activity가 일반적인 암시적 인텐트를 받을 때 자주 필요합니다.

링크를 앱에서 열고 싶다면 ACTION_VIEW, BROWSABLE, data 조건을 사용합니다.

<activity
    android:name=".PostActivity"
    android:exported="true">

    <intent-filter>
        <action android:name="android.intent.action.VIEW" />

        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data
            android:scheme="https"
            android:host="www.example.com" />
    </intent-filter>

</activity>

이 설정은 https://www.example.com 링크를 눌렀을 때 PostActivity가 후보가 될 수 있다는 뜻입니다.


PendingIntent

PendingIntent는 Intent를 즉시 실행하지 않고, 나중에 다른 주체가 실행할 수 있도록 시스템에 맡기는 객체입니다.

일반 Intent가 “지금 이 작업을 실행해줘”에 가깝다면, PendingIntent는 “나중에 이 작업을 실행할 수 있는 티켓”에 가깝습니다.

Intent
지금 실행할 요청서

PendingIntent
나중에 시스템이나 다른 앱이 실행할 수 있는 요청 티켓

대표적인 사용처는 알림입니다.

val intent = Intent(this, DetailActivity::class.java).apply {
    putExtra("postId", 10)
}

val pendingIntent = PendingIntent.getActivity(
    this,
    0,
    intent,
    PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)

알림에 이 PendingIntent를 넣으면, 사용자가 알림을 눌렀을 때 시스템이 이 티켓을 사용해 DetailActivity를 실행합니다.

val notification = NotificationCompat.Builder(this, CHANNEL_ID)
    .setSmallIcon(R.drawable.ic_notification)
    .setContentTitle("새 글")
    .setContentText("눌러서 확인")
    .setContentIntent(pendingIntent)
    .setAutoCancel(true)
    .build()

PendingIntent는 생성 목적에 따라 다음 메서드를 사용합니다.

getActivity()
나중에 Activity를 실행합니다.

getBroadcast()
나중에 BroadcastReceiver로 보냅니다.

getService()
나중에 Service를 시작합니다.

getForegroundService()
나중에 ForegroundService를 시작합니다.

PendingIntent 플래그

PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE는 두 옵션을 동시에 적용한다는 뜻입니다.

Kotlin의 or는 플래그를 조합할 때 사용합니다.

PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE

FLAG_UPDATE_CURRENT는 같은 PendingIntent가 이미 있으면 기존 것을 유지하되 extra 값을 새 값으로 갱신합니다.

기존 티켓이 있음
postId = 10

같은 조건으로 다시 생성
postId = 20

FLAG_UPDATE_CURRENT 사용
기존 티켓의 extra가 postId = 20으로 갱신

FLAG_IMMUTABLE은 PendingIntent를 받은 외부 주체가 내부 Intent를 수정하지 못하게 합니다. 일반적인 알림 클릭, 알람, 위젯 클릭에서는 기본적으로 FLAG_IMMUTABLE을 사용하는 것이 안전합니다.

중요한 점은 IMMUTABLE이 원래 만든 앱도 절대 못바꾼다는 뜻은 아니라는 점입니다. 외부 실행자가 못 바꾸게 막는 의미에 가깝습니다. 원래 앱은 FLAG_UPDATE_CURRENT를 통해 새 extra로 갱신할 수 있습니다.

자주 쓰는 플래그는 다음과 같습니다.

FLAG_UPDATE_CURRENT
기존 PendingIntent가 있으면 새 extra로 갱신합니다.

FLAG_IMMUTABLE
외부에서 PendingIntent 내부 Intent를 수정하지 못하게 합니다.

FLAG_MUTABLE
외부 실행자가 일부 값을 채워 넣을 수 있게 허용합니다. 알림 바로 답장처럼 시스템이 입력값을 넣어야 하는 경우에 사용합니다.

FLAG_CANCEL_CURRENT
기존 PendingIntent를 취소하고 새로 만듭니다.

FLAG_ONE_SHOT
한 번만 사용할 수 있는 PendingIntent를 만듭니다.

FLAG_NO_CREATE
기존 PendingIntent가 있는지만 확인하고, 없으면 새로 만들지 않습니다.

실무에서 알림 클릭용으로는 보통 다음 조합을 자주 사용합니다.

val pendingIntent = PendingIntent.getActivity(
    this,
    postId,
    intent,
    PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)

이 코드는 다음 의미입니다.

같은 PendingIntent가 있으면 최신 extra로 갱신하고,
외부에서는 내용을 바꾸지 못하게 합니다.

2. 복기 노트

Intent는 요청서입니다
Intent는 Activity, Service, BroadcastReceiver에게 작업을 요청하는 객체입니다. 다만 Intent 객체를 만드는 것과 실행하는 것은 다릅니다. 실제 실행은 startActivity() 같은 메서드를 호출할 때 발생합니다.

 

명시적 인텐트는 대상을 아는 경우(주로 내 앱)입니다
내 앱 안에서 DetailActivity를 여는 것처럼 실행할 컴포넌트를 정확히 알면 명시적 인텐트를 사용합니다.

 

암시적 인텐트는 작업만 아는 경우입니다
URL 열기, 공유하기, 전화 앱 열기처럼 어떤 앱이 처리할지 직접 정하지 않을 때 사용합니다. 이때 시스템이 설치된 앱들의 인텐트 필터를 보고 후보를 찾습니다.

 

ACTION_VIEW와 ACTION_SEND는 암시적 인텐트가 요청할 action입니다
ACTION_VIEW는 “보여줘”, ACTION_SEND는 “공유해줘”에 가깝습니다. action은 인텐트가 수행하려는 작업의 종류입니다.

 

인텐트 필터는 암시적 인텐트를 받는 쪽의 조건표입니다
암시적 인텐트가 요청서라면, 인텐트 필터는 “우리 앱은 이런 요청을 받을 수 있음”이라고 Manifest에 등록한 조건표입니다. 주요 기준은 action, data, category입니다.

 

PendingIntent는 실행 티켓입니다
PendingIntent는 일반 Intent처럼 즉시 실행하는 요청이 아니라, 나중에 시스템이나 다른 앱이 실행할 수 있도록 맡기는 티켓에 가깝습니다. 알림 클릭, 알람, 위젯 클릭처럼 앱 코드가 즉시 실행되지 않는 상황에서 필요합니다.

티켓을 발부해서 주면 제 3자(시스템의 알림 등)가 티켓을 행사해서 내 앱의 인텐트를 실행할 수 있습니다.

 

UPDATE_CURRENT와 IMMUTABLE은 역할이 다릅니다
UPDATE_CURRENT는 만든 쪽이 기존 PendingIntent의 extra를 최신 값으로 바꾸기 위한 옵션입니다. IMMUTABLE은 받은 쪽이 내부 Intent를 바꾸지 못하게 막는 보안 옵션입니다.


3. 면접 예상 노트

Q. Intent란 무엇인가요?

Intent는 Android 컴포넌트 사이에서 작업을 요청하거나 데이터를 전달하는 메시지 객체입니다. Activity 실행, Service 시작, Broadcast 전송 등에 사용합니다.

Q. 명시적 인텐트와 암시적 인텐트의 차이는 무엇인가요?

명시적 인텐트는 실행할 컴포넌트를 직접 지정합니다. 앱 내부 화면 이동처럼 대상이 명확할 때 사용합니다.

암시적 인텐트는 컴포넌트를 직접 지정하지 않고 action, data, type 등을 통해 작업만 선언합니다. URL 열기나 공유하기처럼 다른 앱이 처리할 수 있는 작업에 사용합니다.

Q. 암시적 인텐트는 어떤 기준으로 처리 앱을 찾나요?

Android 시스템은 설치된 앱의 Manifest에 선언된 intent-filter와 인텐트의 action, data, category를 비교해서 처리 가능한 컴포넌트를 찾습니다.

Q. 적합한 앱이 없으면 어떻게 되나요?

처리 가능한 컴포넌트가 없는데 startActivity()를 호출하면 예외가 발생할 수 있습니다. 따라서 암시적 인텐트는 실행 전에 처리 가능한 앱이 있는지 확인하거나 예외 처리를 고려해야 합니다.

Q. Intent Filter란 무엇인가요?

Intent Filter는 Activity, Service, BroadcastReceiver가 어떤 암시적 인텐트를 받을 수 있는지 Manifest에 선언하는 조건입니다. 받는 쪽의 “처리 가능 목록”에 가깝습니다.

Q. PendingIntent는 일반 Intent와 무엇이 다른가요?

일반 Intent는 앱이 직접 실행하는 요청입니다. PendingIntent는 미리 정의한 Intent를 나중에 시스템이나 다른 앱이 내 앱을 대신해 실행할 수 있도록 만든 토큰입니다.

Q. PendingIntent가 필요한 대표 상황은 무엇인가요?

알림 클릭, AlarmManager 예약, 홈 화면 위젯 클릭처럼 앱이 직접 그 순간에 실행을 제어하지 않는 상황입니다. 시스템이 나중에 PendingIntent를 사용해 미리 정한 작업을 실행합니다.

Q. FLAG_UPDATE_CURRENT는 언제 사용하나요?

같은 PendingIntent가 이미 있을 때 기존 객체를 유지하면서 extra만 최신 값으로 갱신하고 싶을 때 사용합니다. 예를 들어 알림마다 postId를 다르게 전달해야 할 때 사용할 수 있습니다.

Q. FLAG_IMMUTABLE은 왜 사용하나요?

PendingIntent를 받은 외부 주체가 내부 Intent를 수정하지 못하게 하기 위해 사용합니다. 보안상 일반적인 경우에는 FLAG_IMMUTABLE을 기본으로 사용하는 것이 안전합니다.

Q. FLAG_MUTABLE은 언제 사용하나요?

알림의 바로 답장처럼 시스템이 PendingIntent 내부에 사용자 입력값을 채워 넣어야 하는 경우에 사용합니다. 그 외의 일반적인 상황에서는 FLAG_IMMUTABLE을 우선 고려합니다.