
안드로이드 개발을 하다 보면 Intent는 정말 자주 만나게 되는 개념이다. 화면 전환할 때도, 서비스 시작할 때도, 브로드캐스트 보낼 때도 사용하니까. 그런데 PendingIntent는 좀 헷갈릴 때가 있었는데, 이번에 정리하면서 확실히 이해하게 되었다.
Intent는 안드로이드 컴포넌트들 간의 소통을 담당하는 메시징 객체다. "이런 작업을 해줘"라고 요청할 때 사용한다고 보면 된다.
Activity 시작: 새로운 화면을 열 때 사용한다.
Intent intent = new Intent(this, DetailActivity.class);
startActivity(intent);
Service 시작: 백그라운드 작업을 시작할 때 사용한다.
Intent serviceIntent = new Intent(this, MusicPlayService.class);
startForegroundService(serviceIntent);
Broadcast 전송: 다른 컴포넌트들에게 이벤트를 알릴 때 사용한다.
Intent broadcastIntent = new Intent("com.myapp.DATA_UPDATED");
sendBroadcast(broadcastIntent);
명시적 Intent는 실행할 컴포넌트를 정확히 지정하는 방식이다. 같은 앱 내에서 특정 Activity를 호출할 때 주로 사용한다.
암시적 Intent는 수행할 작업만 지정하고, 시스템이 적절한 앱을 찾아서 실행하는 방식이다.
// 웹페이지 열기 - 브라우저 앱들 중 하나가 실행됨
Intent webIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.google.com"));
startActivity(webIntent);
암시적 Intent를 사용할 때 시스템은 설치된 앱들의 Intent Filter를 확인해서 적합한 앱을 찾는다. 여러 앱이 가능하면 사용자에게 선택 대화상자를 보여준다.
이때 사용자가 "항상"을 선택하면 기본 앱으로 설정되고, "한 번만"을 선택하면 이번에만 해당 앱이 실행된다.
만약 처리할 수 있는 앱이 없다면? ActivityNotFoundException이 발생한다. 그래서 안전하게 처리하려면 아래와 같은 코드를 사용한다.
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
} else {
// 처리할 수 있는 앱이 없을 때의 대안 처리
Toast.makeText(this, "이 작업을 수행할 수 있는 앱이 없습니다", Toast.LENGTH_SHORT).show();
}
PendingIntent는 "미래에 실행될 Intent"라고 생각하면 된다. 다른 앱이나 시스템 컴포넌트에게 내 앱의 권한으로 Intent를 실행할 수 있는 권한을 위임하는 특별한 객체다.
알림을 예로 들어보자. 카카오톡에서 새 메시지 알림이 왔을 때, 사용자가 알림을 탭하면 채팅방이 열려야 한다.
문제는 알림을 관리하는 건 시스템(NotificationManager)인데, 시스템이 카카오톡의 private Activity에 직접 접근할 수는 없다는 점이다.
해결책이 바로 PendingIntent다:
알림에서 사용:
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("새 메시지")
.setContentText("확인하려면 탭하세요")
.setContentIntent(pendingIntent);
AlarmManager에서 사용:
Intent alarmIntent = new Intent(this, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
this, 0, alarmIntent, PendingIntent.FLAG_IMMUTABLE
);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);

Android 12부터는 PendingIntent 생성 시 반드시 FLAG_IMMUTABLE 또는 FLAG_MUTABLE을 명시해야 한다.
FLAG_IMMUTABLE이 없으면 악의적인 앱이 PendingIntent의 내용을 조작할 수 있다. 예를 들어
FLAG_IMMUTABLE을 사용하면 PendingIntent가 한 번 생성되면 변경할 수 없어서 이런 공격을 방지할 수 있다.
PendingIntent의 핵심은 "권한 위임"이다.
시스템이 내 앱에게 명령을 보내는 게 아니라, 내 앱이 시스템에게 "이런 조건이 되면 내 대신 이걸 실행해줘"라고 위임장을 주는 개념이다.
Intent와 PendingIntent는 안드로이드 앱 개발의 핵심 개념이다. Intent는 즉시 실행되는 작업 요청이고, PendingIntent는 미래의 특정 시점에 실행될 작업을 예약하면서 동시에 권한을 위임하는 메커니즘이다.
특히 PendingIntent를 사용할 때는 보안을 위해 FLAG_IMMUTABLE을 꼭 사용하자. 작은 차이지만 앱의 보안에는 큰 영향을 미칠 수 있다.