반응형
Android Intent Flag 완벽 가이드 - 화면 전환 제어 방법
Android Intent Flag는 앱의 화면 전환과 태스크 관리를 제어하는 핵심 요소입니다. 올바른 Intent Flag 사용은 사용자 경험을 향상시키고, 메모리 효율성을 높이며, 앱의 안정성을 개선합니다. 이 가이드에서는 Intent의 기본 개념부터 주요 Flag의 실전 활용법, 최신 Android 버전의 변경사항까지 모든 것을 상세히 다룹니다.
목차
1. Intent와 Intent Flag 기본 개념
2. 주요 Intent Flag 종류와 활용
3. 실전 활용 시나리오
4. 문제 해결과 최신 버전 대응
5. 자주 묻는 질문 (FAQ)
#1. Intent와 Intent Flag 기본 개념
Android 앱 개발에서 Intent는 컴포넌트 간 통신을 담당하는 메시징 객체입니다. 액티비티, 서비스, 브로드캐스트 리시버 등 다양한 컴포넌트 간에 데이터를 전달하거나 특정 작업을 요청할 때 필수적으로 사용됩니다.
1) Intent의 두 가지 타입
Intent는 사용 목적에 따라 명시적 인텐트와 암시적 인텐트로 구분됩니다.
(1) 명시적 인텐트 (Explicit Intent)
특정 컴포넌트를 직접 지정하여 호출하는 방식입니다. 앱 내부의 특정 액티비티로 이동할 때 주로 사용합니다.
// 명시적 인텐트 예제
Intent explicitIntent = new Intent(this, SecondActivity.class);
startActivity(explicitIntent);
// 데이터 전달
Intent intent = new Intent(this, DetailActivity.class);
intent.putExtra("user_id", 12345);
intent.putExtra("user_name", "홍길동");
startActivity(intent);
Intent explicitIntent = new Intent(this, SecondActivity.class);
startActivity(explicitIntent);
// 데이터 전달
Intent intent = new Intent(this, DetailActivity.class);
intent.putExtra("user_id", 12345);
intent.putExtra("user_name", "홍길동");
startActivity(intent);
(2) 암시적 인텐트 (Implicit Intent)
특정 작업을 수행할 수 있는 컴포넌트를 시스템이 찾아서 호출하는 방식입니다. 웹 브라우저 열기, 전화 걸기 등 다른 앱의 기능을 활용할 때 사용합니다.
// 웹 브라우저 열기
Intent implicitIntent = new Intent(Intent.ACTION_VIEW,
Uri.parse("https://www.example.com"));
startActivity(implicitIntent);
// 전화 걸기 화면 열기
Intent callIntent = new Intent(Intent.ACTION_DIAL,
Uri.parse("tel:01012345678"));
startActivity(callIntent);
Intent implicitIntent = new Intent(Intent.ACTION_VIEW,
Uri.parse("https://www.example.com"));
startActivity(implicitIntent);
// 전화 걸기 화면 열기
Intent callIntent = new Intent(Intent.ACTION_DIAL,
Uri.parse("tel:01012345678"));
startActivity(callIntent);
. . . . .
2) Intent Flag의 역할과 중요성
Intent Flag는 인텐트가 어떻게 처리되어야 하는지를 Android 시스템에 알려주는 지시자입니다. 액티비티의 실행 방식, 태스크 스택 관리, 백 스택 동작 등을 정밀하게 제어할 수 있습니다.
| 관점 | Intent Flag의 역할 | 효과 |
|---|---|---|
| 사용자 경험 | 자연스러운 네비게이션 흐름 제공 | 직관적인 화면 전환 |
| 메모리 관리 | 불필요한 액티비티 인스턴스 생성 방지 | 메모리 효율성 향상 |
| 데이터 일관성 | 중복 인스턴스로 인한 데이터 불일치 해결 | 안정적인 상태 관리 |
| 보안 | 적절한 액티비티 실행 모드로 데이터 보호 | 민감 정보 노출 방지 |
// Intent Flag 설정 기본 형식
Intent intent = new Intent(this, TargetActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
// 또는 addFlags() 사용
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
Intent intent = new Intent(this, TargetActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
// 또는 addFlags() 사용
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
. . . . .
3) 태스크와 백 스택 이해하기
Intent Flag를 제대로 활용하려면 태스크(Task)와 백 스택(Back Stack) 개념을 이해해야 합니다.
(1) 태스크 (Task)
태스크는 사용자가 특정 작업을 수행할 때 상호작용하는 액티비티의 모음입니다. 앱을 실행하면 기본적으로 새로운 태스크가 생성되며, 이 태스크 내에서 액티비티들이 스택 형태로 쌓입니다.
(2) 백 스택 (Back Stack)
백 스택은 액티비티가 시작된 순서대로 쌓이는 LIFO(Last In First Out) 구조입니다. 사용자가 백 버튼을 누르면 최상위 액티비티가 제거되고 이전 액티비티로 돌아갑니다.
① Activity A 시작 → [A]
② Activity B 시작 → [A, B]
③ Activity C 시작 → [A, B, C]
④ 백 버튼 클릭 → [A, B] (C 제거)
#2. 주요 Intent Flag 종류와 활용
Android에서 제공하는 다양한 Intent Flag 중 실무에서 자주 사용되는 핵심 Flag들을 살펴보겠습니다.
1) FLAG_ACTIVITY_NEW_TASK
새로운 태스크에서 액티비티를 시작합니다. 액티비티가 아닌 컨텍스트(서비스, 브로드캐스트 리시버)에서 액티비티를 시작할 때 필수적으로 사용해야 합니다.
(1) 사용 시나리오
① 서비스나 브로드캐스트 리시버에서 액티비티 시작
② 알림(Notification)에서 앱 실행
③ 다른 앱에서 내 앱의 액티비티 실행
// 서비스에서 액티비티 시작
Intent intent = new Intent(context, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
// 알림에서 액티비티 시작
Intent notificationIntent = new Intent(this, NotificationActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(
this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
Intent intent = new Intent(context, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
// 알림에서 액티비티 시작
Intent notificationIntent = new Intent(this, NotificationActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(
this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
. . . . .
2) FLAG_ACTIVITY_CLEAR_TOP
대상 액티비티가 이미 스택에 존재하면 그 위의 모든 액티비티를 제거하고 해당 액티비티를 최상위로 가져옵니다. 홈 화면으로 돌아가거나 특정 지점으로 네비게이션할 때 매우 유용합니다.
(1) 동작 원리
// 현재 스택: [Home, List, Detail, Edit]
Intent intent = new Intent(this, ListActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
// 결과 스택: [Home, List] (Detail과 Edit 제거됨)
Intent intent = new Intent(this, ListActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
// 결과 스택: [Home, List] (Detail과 Edit 제거됨)
(2) 실전 활용 코드
// 홈 화면으로 돌아가기
private void navigateToHome() {
Intent intent = new Intent(this, HomeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
// 특정 액티비티로 돌아가기 (중간 화면들 제거)
private void backToCart() {
Intent intent = new Intent(this, CartActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
}
private void navigateToHome() {
Intent intent = new Intent(this, HomeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
// 특정 액티비티로 돌아가기 (중간 화면들 제거)
private void backToCart() {
Intent intent = new Intent(this, CartActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
}
. . . . .
3) FLAG_ACTIVITY_SINGLE_TOP
이미 최상위에 있는 액티비티와 동일한 액티비티를 시작하려 할 때, 새 인스턴스를 생성하지 않고 기존 인스턴스의 onNewIntent() 메서드를 호출합니다.
// Intent Flag 설정
Intent intent = new Intent(this, CurrentActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra("data", "새로운 데이터");
startActivity(intent);
// 액티비티에서 새 Intent 처리
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
String data = intent.getStringExtra("data");
// 새로운 데이터로 UI 업데이트
updateUI(data);
}
Intent intent = new Intent(this, CurrentActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra("data", "새로운 데이터");
startActivity(intent);
// 액티비티에서 새 Intent 처리
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
String data = intent.getStringExtra("data");
// 새로운 데이터로 UI 업데이트
updateUI(data);
}
. . . . .
4) FLAG_ACTIVITY_CLEAR_TASK
FLAG_ACTIVITY_NEW_TASK와 함께 사용되며, 새 액티비티가 시작되기 전에 기존 태스크의 모든 액티비티를 제거합니다. 로그아웃이나 앱 재시작 시 유용합니다.
// 로그아웃 후 로그인 화면으로
private void logout() {
Intent intent = new Intent(this, LoginActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
finish();
}
// 앱 초기화 후 메인 화면으로
private void resetApp() {
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
private void logout() {
Intent intent = new Intent(this, LoginActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
finish();
}
// 앱 초기화 후 메인 화면으로
private void resetApp() {
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
. . . . .
5) FLAG_ACTIVITY_NO_HISTORY
이 Flag로 시작된 액티비티는 사용자가 떠나면 백 스택에 남지 않습니다. 스플래시 화면이나 일회성 안내 화면에 적합합니다.
// 스플래시 화면 구현
Intent intent = new Intent(this, SplashActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(intent);
// 일회성 튜토리얼 화면
Intent tutorialIntent = new Intent(this, TutorialActivity.class);
tutorialIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(tutorialIntent);
Intent intent = new Intent(this, SplashActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(intent);
// 일회성 튜토리얼 화면
Intent tutorialIntent = new Intent(this, TutorialActivity.class);
tutorialIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(tutorialIntent);
. . . . .
6) 주요 Flag 비교표
| Flag | 주요 기능 | 사용 시나리오 |
|---|---|---|
| NEW_TASK | 새 태스크에서 시작 | 서비스/알림에서 액티비티 시작 |
| CLEAR_TOP | 상위 액티비티 모두 제거 | 홈 화면으로 돌아가기 |
| SINGLE_TOP | 최상위 중복 방지 | 검색 결과 업데이트 |
| CLEAR_TASK | 태스크 완전 초기화 | 로그아웃, 앱 재시작 |
| NO_HISTORY | 백 스택에 남지 않음 | 스플래시, 튜토리얼 화면 |
| REORDER_TO_FRONT | 기존 액티비티를 앞으로 | 특정 화면 빠른 접근 |
#3. 실전 활용 시나리오
실제 앱 개발에서 자주 마주치는 상황별로 Intent Flag를 어떻게 활용하는지 살펴보겠습니다.
1) 로그인 플로우 구현
사용자가 로그인에 성공한 후 이전의 모든 화면을 제거하고 메인 화면으로 이동하는 패턴입니다.
// 로그인 성공 처리
private void onLoginSuccess() {
// 사용자 정보 저장
saveUserInfo();
// 메인 화면으로 이동 (백 스택 초기화)
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
finish();
}
// 로그아웃 처리
private void logout() {
// 사용자 정보 삭제
clearUserInfo();
// 로그인 화면으로 이동 (모든 화면 제거)
Intent intent = new Intent(this, LoginActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
private void onLoginSuccess() {
// 사용자 정보 저장
saveUserInfo();
// 메인 화면으로 이동 (백 스택 초기화)
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
finish();
}
// 로그아웃 처리
private void logout() {
// 사용자 정보 삭제
clearUserInfo();
// 로그인 화면으로 이동 (모든 화면 제거)
Intent intent = new Intent(this, LoginActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
. . . . .
2) 푸시 알림 처리
푸시 알림을 통해 앱의 특정 화면으로 직접 이동할 때 사용하는 패턴입니다.
// FCM 알림 메시지 처리
private void handleNotification(RemoteMessage message) {
String notificationType = message.getData().get("type");
String targetId = message.getData().get("target_id");
// 타겟 액티비티 Intent 생성
Intent intent = new Intent(this, DetailActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra("notification_type", notificationType);
intent.putExtra("target_id", targetId);
// PendingIntent 생성 (Android 12 이상 대응)
PendingIntent pendingIntent;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
pendingIntent = PendingIntent.getActivity(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
} else {
pendingIntent = PendingIntent.getActivity(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
// 알림 표시
showNotification(pendingIntent);
}
private void handleNotification(RemoteMessage message) {
String notificationType = message.getData().get("type");
String targetId = message.getData().get("target_id");
// 타겟 액티비티 Intent 생성
Intent intent = new Intent(this, DetailActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra("notification_type", notificationType);
intent.putExtra("target_id", targetId);
// PendingIntent 생성 (Android 12 이상 대응)
PendingIntent pendingIntent;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
pendingIntent = PendingIntent.getActivity(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
} else {
pendingIntent = PendingIntent.getActivity(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
// 알림 표시
showNotification(pendingIntent);
}
. . . . .
3) 복잡한 네비게이션 흐름
쇼핑몰 앱에서 장바구니 → 결제 → 결제 완료 후 메인 화면으로 돌아가는 시나리오입니다.
// 결제 완료 후 메인 화면으로
private void onPaymentSuccess() {
// 결제 완료 화면 표시
showPaymentCompleteDialog();
// 3초 후 메인 화면으로 이동
new Handler().postDelayed(() -> {
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
finish();
}, 3000);
}
// 장바구니로 돌아가기 (중간 화면 제거)
private void backToCart() {
Intent intent = new Intent(this, CartActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
private void onPaymentSuccess() {
// 결제 완료 화면 표시
showPaymentCompleteDialog();
// 3초 후 메인 화면으로 이동
new Handler().postDelayed(() -> {
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
finish();
}, 3000);
}
// 장바구니로 돌아가기 (중간 화면 제거)
private void backToCart() {
Intent intent = new Intent(this, CartActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
. . . . .
4) 딥링크 처리
외부 링크를 통해 앱의 특정 화면으로 진입할 때의 처리 방법입니다.
// AndroidManifest.xml에서 딥링크 설정
// <intent-filter>
// <action android:name="android.intent.action.VIEW" />
// <data android:scheme="myapp" android:host="product" />
// </intent-filter>
// 딥링크로 진입한 경우 처리
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
Uri data = intent.getData();
if (data != null && "myapp".equals(data.getScheme())) {
// myapp://product/123 형식 파싱
String productId = data.getLastPathSegment();
// 상품 상세 화면으로 이동
Intent productIntent = new Intent(this, ProductDetailActivity.class);
productIntent.putExtra("product_id", productId);
productIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(productIntent);
}
}
// <intent-filter>
// <action android:name="android.intent.action.VIEW" />
// <data android:scheme="myapp" android:host="product" />
// </intent-filter>
// 딥링크로 진입한 경우 처리
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
Uri data = intent.getData();
if (data != null && "myapp".equals(data.getScheme())) {
// myapp://product/123 형식 파싱
String productId = data.getLastPathSegment();
// 상품 상세 화면으로 이동
Intent productIntent = new Intent(this, ProductDetailActivity.class);
productIntent.putExtra("product_id", productId);
productIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(productIntent);
}
}
#4. 문제 해결과 최신 버전 대응
Intent Flag 사용 시 자주 발생하는 문제와 해결 방법, 그리고 최신 Android 버전의 변경사항을 알아보겠습니다.
1) 자주 발생하는 문제와 해결
(1) 액티비티 중복 생성 문제
원인: FLAG_ACTIVITY_SINGLE_TOP이나 CLEAR_TOP을 사용하지 않아 동일한 액티비티가 여러 번 생성됩니다.
// 문제 상황: 백 버튼을 누를 때마다 같은 화면이 반복됨
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent); // 매번 새 인스턴스 생성
// 해결 방법 1: SINGLE_TOP 사용
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
// 해결 방법 2: CLEAR_TOP 사용
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent); // 매번 새 인스턴스 생성
// 해결 방법 1: SINGLE_TOP 사용
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
// 해결 방법 2: CLEAR_TOP 사용
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
(2) 백 버튼 동작 이상
원인: 태스크 스택이 의도한 대로 관리되지 않아 백 버튼 동작이 예상과 다릅니다.
// AndroidManifest.xml에서 launchMode 설정
// <activity
// android:name=".MainActivity"
// android:launchMode="singleTop" />
// 백 버튼 커스터마이징 (Android 12 이하)
@Override
public void onBackPressed() {
Intent intent = new Intent(this, HomeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
// 백 버튼 처리 (Android 13 이상)
private final OnBackPressedCallback callback = new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
Intent intent = new Intent(MainActivity.this, HomeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getOnBackPressedDispatcher().addCallback(this, callback);
}
// <activity
// android:name=".MainActivity"
// android:launchMode="singleTop" />
// 백 버튼 커스터마이징 (Android 12 이하)
@Override
public void onBackPressed() {
Intent intent = new Intent(this, HomeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
// 백 버튼 처리 (Android 13 이상)
private final OnBackPressedCallback callback = new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
Intent intent = new Intent(MainActivity.this, HomeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getOnBackPressedDispatcher().addCallback(this, callback);
}
(3) 상태 복원 문제
원인: 앱이 백그라운드에서 시스템에 의해 종료된 후 복원될 때 데이터가 손실됩니다.
// 상태 저장
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("user_name", userName);
outState.putInt("scroll_position", scrollPosition);
outState.putSerializable("data_list", dataList);
}
// 상태 복원
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null) {
userName = savedInstanceState.getString("user_name");
scrollPosition = savedInstanceState.getInt("scroll_position");
dataList = (ArrayList) savedInstanceState.getSerializable("data_list");
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("user_name", userName);
outState.putInt("scroll_position", scrollPosition);
outState.putSerializable("data_list", dataList);
}
// 상태 복원
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null) {
userName = savedInstanceState.getString("user_name");
scrollPosition = savedInstanceState.getInt("scroll_position");
dataList = (ArrayList) savedInstanceState.getSerializable("data_list");
}
}
. . . . .
2) 최신 Android 버전 변경사항
(1) Android 10 (API 29) 이상
백그라운드에서 액티비티 시작 제한이 도입되었습니다. FLAG_ACTIVITY_NEW_TASK를 사용해도 특정 조건에서는 액티비티 시작이 차단됩니다.
// Android 10 이상 대응 코드
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// 알림을 통해 사용자 상호작용 유도
showNotificationWithAction();
} else {
// 기존 방식으로 액티비티 시작
Intent intent = new Intent(this, TargetActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// 알림을 통해 사용자 상호작용 유도
showNotificationWithAction();
} else {
// 기존 방식으로 액티비티 시작
Intent intent = new Intent(this, TargetActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
(2) Android 11 (API 30) 이상
패키지 가시성 제한으로 다른 앱의 액티비티 시작 시 추가 설정이 필요합니다.
// AndroidManifest.xml에 queries 추가
// <manifest>
// <queries>
// <package android:name="com.example.targetapp" />
// <intent>
// <action android:name="android.intent.action.VIEW" />
// <data android:scheme="https" />
// </intent>
// </queries>
// </manifest>
// <manifest>
// <queries>
// <package android:name="com.example.targetapp" />
// <intent>
// <action android:name="android.intent.action.VIEW" />
// <data android:scheme="https" />
// </intent>
// </queries>
// </manifest>
(3) Android 12 (API 31) 이상
PendingIntent 생성 시 FLAG_IMMUTABLE 또는 FLAG_MUTABLE을 명시적으로 지정해야 합니다.
// Android 12 이상 PendingIntent 생성
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// FLAG_IMMUTABLE 필수 지정
pendingIntent = PendingIntent.getActivity(
this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
} else {
pendingIntent = PendingIntent.getActivity(
this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT
);
}
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// FLAG_IMMUTABLE 필수 지정
pendingIntent = PendingIntent.getActivity(
this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
} else {
pendingIntent = PendingIntent.getActivity(
this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT
);
}
#5. 자주 묻는 질문 (FAQ)
1) Q: Intent Flag는 여러 개를 동시에 사용할 수 있나요?
A: 네, 비트 OR 연산자(|)를 사용하여 여러 Flag를 조합할 수 있습니다. 예를 들어
FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK처럼 사용하면 새 태스크를 생성하면서 기존 태스크를 모두 제거합니다. 단, Flag 조합에 따라 예상치 못한 동작이 발생할 수 있으므로 테스트를 통해 검증해야 합니다.
. . . . .
2) Q: FLAG_ACTIVITY_CLEAR_TOP과 FLAG_ACTIVITY_CLEAR_TASK의 차이는?
A: CLEAR_TOP은 대상 액티비티 위의 액티비티만 제거하지만, CLEAR_TASK는 태스크 전체를 제거합니다. CLEAR_TOP은 단독으로 사용 가능하지만, CLEAR_TASK는 반드시 NEW_TASK와 함께 사용해야 합니다. 로그아웃 시에는 CLEAR_TASK를, 홈으로 돌아갈 때는 CLEAR_TOP을 사용하는 것이 일반적입니다.
. . . . .
3) Q: launchMode와 Intent Flag의 차이는 무엇인가요?
A: launchMode는 AndroidManifest.xml에서 정적으로 설정하는 액티비티 실행 모드이고, Intent Flag는 코드에서 동적으로 설정합니다. launchMode는 singleTop, singleTask, singleInstance 등이 있으며, Intent Flag보다 우선순위가 낮습니다. 따라서 Intent Flag가 launchMode 설정을 덮어쓸 수 있습니다.
. . . . .
4) Q: 서비스에서 액티비티를 시작할 때 주의할 점은?
A: 서비스는 액티비티 컨텍스트가 아니므로 반드시 FLAG_ACTIVITY_NEW_TASK를 사용해야 합니다. 이를 누락하면
AndroidRuntimeException이 발생합니다. Android 10 이상에서는 백그라운드에서의 액티비티 시작이 제한되므로, 대신 알림을 사용하여 사용자의 명시적인 액션을 유도하는 것이 권장됩니다.
. . . . .
5) Q: onNewIntent()는 언제 호출되나요?
A: FLAG_ACTIVITY_SINGLE_TOP을 사용하거나 launchMode가 singleTop/singleTask일 때, 이미 최상위에 있는 액티비티와 동일한 액티비티를 시작하면 새 인스턴스를 만들지 않고 onNewIntent()가 호출됩니다. 이때
setIntent(intent)를 호출하여 새 Intent로 업데이트하는 것을 잊지 말아야 합니다.
. . . . .
6) Q: 딥링크로 앱 진입 시 백 스택은 어떻게 관리하나요?
A: 딥링크로 앱에 진입하면 해당 액티비티만 스택에 생성되므로, 백 버튼을 누르면 앱이 종료됩니다. 이를 방지하려면 TaskStackBuilder를 사용하여 인위적으로 백 스택을 생성하거나, 딥링크 액티비티에서 부모 액티비티를 명시하여 자연스러운 네비게이션을 구현해야 합니다.
. . . . .
7) Q: 알림에서 앱을 실행할 때 권장하는 Flag 조합은?
A: 일반적으로
FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP 조합을 권장합니다. 이렇게 하면 앱이 이미 실행 중이면 기존 태스크를 재사용하고, 실행 중이 아니면 새 태스크를 생성합니다. SINGLE_TOP을 추가하면 대상 액티비티가 이미 최상위에 있을 때 중복 생성을 방지할 수 있습니다.
. . . . .
8) Q: Android 13 이상에서 백 버튼 처리가 변경되었나요?
A: 네, Android 13(API 33)부터는 예측 가능한 백 제스처(Predictive Back Gesture)가 도입되었습니다.
onBackPressed()는 deprecated되었으며, OnBackPressedCallback을 사용해야 합니다. 이를 통해 사용자가 백 제스처를 시작할 때 미리보기를 제공할 수 있습니다.
. . . . .
9) Q: FLAG_ACTIVITY_NO_HISTORY는 어떤 경우에 사용하나요?
A: 일회성 화면에 사용합니다. 스플래시 화면, 튜토리얼, 광고 화면 등 사용자가 한 번 보고 지나가는 화면에 적용하면 백 버튼으로 다시 돌아가지 않아 사용자 경험이 개선됩니다. 단, 이 Flag를 사용하면 액티비티가 백 스택에 전혀 남지 않으므로 신중히 사용해야 합니다.
. . . . .
10) Q: Intent Flag 사용 시 메모리 누수를 방지하려면?
A: Intent Flag 자체는 메모리 누수를 직접 발생시키지 않지만, 액티비티 중복 생성을 방지하여 간접적으로 메모리를 절약할 수 있습니다. SINGLE_TOP이나 CLEAR_TOP을 적절히 사용하고, 더 이상 필요 없는 액티비티는
finish()로 명시적으로 종료하세요. 또한 액티비티 내에서 Context 참조를 장기간 유지하지 않도록 주의해야 합니다.
마무리
Android Intent Flag는 앱의 화면 전환과 네비게이션을 제어하는 강력한 도구입니다. 적절한 Flag 사용은 사용자 경험을 크게 향상시키고, 메모리 효율성을 높이며, 앱의 안정성을 개선합니다.
이 가이드에서 다룬 핵심 내용을 요약하면 다음과 같습니다. NEW_TASK는 서비스나 알림에서 액티비티를 시작할 때 필수이며, CLEAR_TOP은 특정 화면으로 돌아갈 때 중간 액티비티들을 제거합니다. SINGLE_TOP은 중복 생성을 방지하고, CLEAR_TASK는 로그아웃 시 전체 스택을 초기화할 때 사용합니다.
실전 개발 시 권장사항은 다음과 같습니다. 앱의 네비게이션 패턴을 미리 설계하고 적절한 Flag 조합을 계획하세요. 복잡한 네비게이션 흐름은 반드시 테스트를 통해 검증해야 합니다. 최신 Android 버전의 변경사항(백그라운드 시작 제한, PendingIntent FLAG_IMMUTABLE 등)을 항상 고려하고, 사용자 경험을 최우선으로 생각하며 Flag를 선택하세요.
Intent Flag를 마스터하면 Android 앱 개발의 핵심 역량을 갖추게 됩니다. 이 가이드가 여러분의 개발 여정에 실질적인 도움이 되기를 바랍니다.
긴 글 읽어주셔서 감사합니다.
끝.
끝.
반응형
'Development > Android' 카테고리의 다른 글
| [Android] Android Context 완벽 이해 - 종류별 사용법과 메모리 누수 해결 (0) | 2020.04.08 |
|---|---|
| [Android] Android Activity 생명주기와 실전 구현 방법 (기본) (0) | 2020.04.08 |
| [Android] Android ArrayList 객체를 Intent로 전달하는 3가지 방법 (3) | 2020.04.08 |
| [Android] Android Activity 오류 해결 및 성능 최적화 방법 (0) | 2020.04.08 |
| [Android] Android Picasso vs Glide 이미지 라이브러리 비교와 선택 기준 (0) | 2019.10.01 |