안드로이드 시스템·Compose 용어 사전
컴포넌트암시적 인텐트 · 명시적 인텐트 · PendingIntent

Intent

컴포넌트를 잇는 메시지 객체. 명시적은 대상을 지정하고 암시적은 능력을 요청한다.

컴포넌트를 잇는 메시지 객체.

// 명시적 — 대상 클래스를 지정한다
startActivity(Intent(this, DetailActivity::class.java).putExtra("id", 42L))

// 암시적 — "이런 일을 할 수 있는 앱" 을 요청한다
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://example.com")))
startActivity(Intent(Intent.ACTION_SEND).apply {
    type = "text/plain"; putExtra(Intent.EXTRA_TEXT, "공유할 내용")
})

암시적 인텐트가 실패하는 경우

val intent = Intent(Intent.ACTION_VIEW, uri)
if (intent.resolveActivity(packageManager) != null) startActivity(intent)
else showError("이 작업을 처리할 앱이 없습니다")

// 또는
runCatching { startActivity(intent) }
    .onFailure { /* ActivityNotFoundException */ }

처리할 앱이 없으면 ActivityNotFoundException 으로 크래시한다

  • 암시적 인텐트는 항상 방어한다

Android 11+ 에서는 resolveActivity 가 보이려면 AndroidManifest 에 <queries> 로 대상을 선언해야 한다 (패키지 가시성 제한)

결과를 받아오기

// startActivityForResult 는 폐기됐다
private val picker = registerForActivityResult(
    ActivityResultContracts.GetContent()
) { uri: Uri? -> uri?.let { load(it) } }

button.setOnClickListener { picker.launch("image/*") }

왜 바뀌었나 requestCode 정수를 직접 관리해야 했고 충돌이 잦았다 프로세스 죽음 후 결과 복원이 깨졌다

  • ActivityResult API 는 등록을 onCreate 전에 하도록 강제해 복원을 보장한다

PendingIntent — 남에게 맡기는 인텐트

val pi = PendingIntent.getActivity(
    ctx, 0, Intent(ctx, DetailActivity::class.java),
    PendingIntent.FLAG_IMMUTABLE            // ★ Android 12+ 필수
)

알림 시스템 · AlarmManager 처럼 '다른 프로세스가 내 대신 실행' 해야 할 때 쓴다

FLAG_IMMUTABLE 을 안 주면 Android 12+ 에서 IllegalArgumentException 이다

  • 가변 PendingIntent 는 받은 쪽이 내용을 바꿀 수 있어 보안 구멍이 된다

면접 함정

  • "Intent extra에 큰 객체를 담아 넘긴다" → Binder 트랜잭션 한도(약 1MB)에 걸려 TransactionTooLargeException이 난다. id만 넘기고 다시 조회한다.
  • "암시적 인텐트로 내 앱의 Service를 부른다" → Android 5.0부터 금지다. 명시적이어야 한다.

Intent 필터가 매칭되는 규칙

암시적 인텐트는 세 가지로 매칭된다
  action    무엇을 하려는가 (ACTION_VIEW · ACTION_SEND)
  category  어떤 맥락인가 (DEFAULT 는 암시적 인텐트에 사실상 필수)
  data      URI 스킴 · 호스트 · 경로 · MIME 타입

셋이 모두 맞아야 후보가 된다

  • "딥링크가 안 열려요" 의 절반은 CATEGORY_BROWSABLE 누락이다

인텐트로 다른 앱을 여는 흔한 예

// 전화 걸기 화면 (권한 불필요 — ACTION_CALL 은 권한이 필요하다)
Intent(Intent.ACTION_DIAL, Uri.parse("tel:021234567"))

// 앱 설정 화면
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
       Uri.fromParts("package", packageName, null))

// 지도
Intent(Intent.ACTION_VIEW, Uri.parse("geo:37.5,127.0?q=서울역"))

// 공유 시트를 강제로 띄운다
startActivity(Intent.createChooser(sendIntent, "공유"))

보안 — 받은 인텐트를 믿지 않는다

// exported 컴포넌트는 아무 앱이나 부를 수 있다
override fun onCreate(s: Bundle?) {
    val id = intent.getLongExtra("id", -1)
    require(id > 0) { "잘못된 진입" }        // 검증한다
    val redirect = intent.getStringExtra("next")
    // ❌ 검증 없이 이 값으로 다른 컴포넌트를 부르면 인텐트 리다이렉션 취약점이다
}

Android 12+ 는 intent-filter 가 있는 컴포넌트에 android:exported 명시를 강제한다 필요 없으면 exported="false" 로 닫는 것이 기본이다

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 4대 컴포넌트 — Intent·Service·Receiver·Provider