Activity Back Stack
Activity는 어떤 작업 중심으로 디자인됩니다. 예를들어 전화앱에서 번호를 누르는 Activity는 단순히 사용자로부터 번호를 입력받는 화면만 제공합니다. 번호를 입력하고 통화버튼을 누르면 다른 Activity가 실행되는데 그 Activity는 통화 기능만 제공합니다. 이처럼 앱에는 여러 Activity가 존재합니다.
전화통화가 끝나면 Activity는 종료되고 다시 번호를 입력받는 Activity로 돌아갈 것입니다. 대부분의 사람들이 이런 UX를 예상할 것입니다. Activity Back Stack은 이런 개발자의 UX를 구현하는데 도움을 줍니다. Back Stack은 Activity의 작업을 관리하는 자료구조로 마치 Stack(후입선출)처럼 동작합니다.
그림 1은 Back Stack에 대해서 쉽게 이해할 수 있게 도와줍니다. 처음 앱을 시작하면서 Activity1이 실행되었고 Stack의 맨 위에 쌓였습니다. 그리고 Activity2가 실행되었을 때 Activity1 위로 Activity2가 Stack에 쌓이게 되었습니다. 다시 Activity3가 실행되면서 Activity3가 Stack에서 가장 맨 위에 쌓이게 되었습니다.
여기서 뒤로가기 버튼을 눌러 Activity3를 종료하게 되면 Activity3는 Pop되어 Stack에서 삭제되고 그 밑에 있는 Activity2가 실행되었습니다. 만약 여기서 뒤로가기 버튼을 눌러 Activity2를 종료하면 Activity1이 실행됩니다.
시작 Activity
Launcher에서 앱을 실행하면 항상 고정적으로 실행되는 시작 Activity가 실행됩니다. 만약 어떤 Activity를 시작 Activity로 설정하고 싶다면 AndroidManifest.xml의
<Activity>
Tag 하위에 <intent-filter>
를 등록하기만 하면 됩니다. 이전 튜토리얼 Project의 AndroidManifest.xml을 열어보겠습니다.
AndroidManifest.xml을 보면 두개의 Activity가 등록되어있습니다. 여기서 MainActivity의 <activity>
TAG 아래에만 <intent-filter>
가 정의되어있습니다. Android는 이 Intent filter를 보고 시작 Activity를 구분합니다. 그렇기 때문에 앱을 실행하면 항상 MainActivity가 시작 Activity로 실행됩니다.
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SubActivity01" />
Intent filter는 action과 category 등의 속성들로 구성되어있습니다.
시작 Activity로 설정하려면 Action이 "android.intent.action.MAIN"로, Category를 "android.intent.category.LAUNCHER"인 intent filter인 intent filter를 정의해야합니다.
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
Back Stack 확인해보기
이전 Project만으로도 Back Stack을 확인할 수 있습니다. 저희는 MainActivity에서 버튼을 누르면 SubActivity01이 실행되도록 구현하였습니다. SubActivity01이 실행되었을 때 뒤로가기 버튼을 SubActivity01을 종료한다면 MainActivity01로 돌아갈 것입니다.
하지만 두개는 너무 간단한 것 같아서 Activity를 1개 더 만들어보겠습니다. MainActivity와 동일한 폴더에 SubActivity02를 만들어주세요. 그리고 **/res/layout/**에 activity_sub02.xml을 만들어주세요.
activity_sub02.xml은 아래 코드처럼 LinearLayout에 TextView를 넣어주세요. text에 "Sub activity02"라고 넣어주시면 다른 Activity와 구분하기 쉽습니다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sub activity02"
android:layout_gravity="center"/>
</LinearLayout>
SubActivity02에 layout으로 activity_sub02.xml을 설정해주세요. 아마도 아래와 같이 코딩하셨을 것입니다.
package com.example.xyz.myapplication;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
/**
* Created by XYZ on 2017-08-16.
*/
public class SubActivity02 extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sub02);
}
}
Activity를 만들었으면 AndroidManifest.xml에 꼭 등록을 해야 합니다.
아래처럼 코드 1줄 넣으면 등록이 됩니다.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.xyz.myapplication">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SubActivity01" />
<activity android:name=".SubActivity02" />
</application>
</manifest>
SubActivity02는 생성하였지만 이 Activity를 실행시켜주는 부분이 없네요. SubActivity01에서 SubActivity02를 실행시킬 것이니 SubActivity01에 버튼과 호출 코드를 넣어야 합니다.
먼저 Button을 추가하려면 activity_sub01.xml을 수정해야합니다. 그리고 SubActivity02를 실행시키려면 Button이 눌렸을 때 어떤 함수를 호출해줄지 정의해줘야 합니다. 아래 코드처럼 Button을 생성하였고 android:onClick속성에 onButtonClick 함수를 정의하였습니다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sub activity02"
android:layout_gravity="center"
android:onClick="onButtonClick"/>
</LinearLayout>
이제 SubActivity01에 onButtonClick함수를 만들고 안에 SubActivity02를 호출하는 코드를 입력하겠습니다. 이전에 배운 Log도 함께 넣어주었습니다.
package com.example.xyz.myapplication;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
/**
* Created by XYZ on 2017-08-13.
*/
public class SubActivity01 extends AppCompatActivity {
public static final String TAG = "SubActivity01";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sub01);
Context context = getApplicationContext();
CharSequence text = "SubActivity01이 실행되었습니다";
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
Log.d(TAG, "onCreate: SubActivity01이 실행되었습니다.");
}
void onButtonClick(View v) {
Intent intent = new Intent(this, SubActivity02.class);
startActivity(intent);
Log.d(TAG, "onButtonClick: SubActivity02을 실행시켰습니다.");
}
}
구현은 모두 끝났습니다. 이제 실행시켜 확인해보시면 됩니다. SubActivity02까지 실행시켰다가 종료하여 다시 MainActivity로 돌아와보세요.
정리
Back Stack에 대해서 정말 간단히 알아보았습니다. 사실 Back Stack에 관련된 다양한 속성이 있습니다. 동일한 Activity가 여러번 실행되었을 때 Activity의 Stack을 어떻게 할지 등 다양한 변수가 발생하는데, Android는 개발자가 의도한 UX에 맞게 Activity가 동작하도록 여러 속성을 지원합니다.
여기서 자세한 내용은 더 다루지 않고 나중에 다루도록 하겠습니다. 자세한 내용을 알고 싶으신 분은 Back Stack : Android API Guide을 참고해주세요.
참고
- 샘플 코드는 GitHub에 있습니다
- Back Stack : Android API Guide
Related Posts
- Android 14 - 사진/동영상 파일, 일부 접근 권한 소개
- Android - adb push, pull로 파일 복사, 다운로드
- Android 14 - 암시적 인텐트 변경사항 및 문제 해결
- Jetpack Compose - Row와 Column
- Android 13, AOSP 오픈소스 다운로드 및 빌드
- Android 13 - 세분화된 미디어 파일 권한
- Android 13에서 Notification 권한 요청, 알림 띄우기
- Android 13에서 'Access blocked: ComponentInfo' 에러 해결
- 에러 해결: android gradle plugin requires java 11 to run. you are currently using java 1.8.
- 안드로이드 - 코루틴과 Retrofit으로 비동기 통신 예제
- 안드로이드 - 코루틴으로 URL 이미지 불러오기
- Android - 진동, Vibrator, VibrationEffect 예제
- Some problems were found with the configuration of task 에러 수정
- Query method parameters should either be a type that can be converted into a database column or a List
- 우분투에서 Android 12 오픈소스 다운로드 및 빌드
- Android - ViewModel을 생성하는 방법
- Android - Transformations.map(), switchMap() 차이점
- Android - Transformations.distinctUntilChanged() 소개
- Android - TabLayout 구현 방법 (+ ViewPager2)
- Android - 휴대폰 전화번호 가져오는 방법
- Android 12 - Splash Screens 알아보기
- Android 12 - Incremental Install (Play as you Download) 소개
- Android - adb 명령어로 bugreport 로그 파일 추출
- Android - adb 명령어로 App 데이터 삭제
- Android - adb 명령어로 앱 비활성화, 활성화
- Android - adb 명령어로 특정 패키지의 PID 찾기
- Android - adb 명령어로 퍼미션 Grant 또는 Revoke
- Android - adb 명령어로 apk 설치, 삭제
- Android - adb 명령어로 특정 패키지의 프로세스 종료
- Android - adb 명령어로 screen capture 저장
- Android - adb 명령어로 System 앱 삭제, 설치
- Android - adb 명령어로 settings value 확인, 변경
- Android 12 - IntentFilter의 exported 명시적 선언
- Android - adb 명령어로 공장초기화(Factory reset)
- Android - adb logcat 명령어로 로그 출력