본문 바로가기

Android/Android의 모든 것

👋Android의 모든 것 : Activity란

728x90

 

1. 액티비티(Activity)와 태스크(Task) 그리고 백 스택(back stack)

애플리케이션이라면 반드시 하나 이상의 액티비티를 가져야할 만큼 안드로이드 개발에 기초가 되는 이 놈은 무엇일까?

안드로이드 4대 컴포넌트 중 하나인 액티비티는 사용자와 상호작용을 하는 UI(User Interface)가 그려지는 Window를 가지는 애플리케이션에서 독립적으로 활용되는 실행 단위입니다.

 

애플리케이션에선 다른 액티비티를 호출하거나 다른 애플리케이션의 액티비티도 호출할 수 있습니다.

아래 시나리오를 봅시다.

 

--- 사용자가 상호작용하여 애플리케이션에서 A 액티비티에서 B 액티비티가 호출되었다가 백버튼에 의해 다시 A 액티비티로 돌아가게 되었다고 생각해봅시다. ---

 

여기서 태스크(task)백 스택(back stack)이란 개념이 등장합니다. 

태스크(task)는 어떤 작업을 수행하기 위해서 사용자와 상호작용하는 액티비티의 모음으로 위 같은 상황에선 두 액티비티가 태스트 안에서 유지됩니다.

여기서 태스크에는 먼저 호출된 액티비티가 아래로 가도록 정렬되는 First In Last Out 구조의 백 스택(back stack)이 존재합니다.

 

사용자가 애플리케이션을 실행했을 때 애플리케이션의 태스크를 찾고 없다면 새 태스크가 생성되고 애플리케이션 실행 시 사용자에게 가장 먼저 보이는 액티비티가 스택의 바닥에 추가되며 이를 Root Activity라고 합니다.

 

 

위 사진과 같이 백 스택에 액티비티가 추가되면 이 전의 액티비티는 스택에 머물러있지만 정지된 상태가 되고 스택의 맨 위에 있는(사용자에게 보여지고 있는) 액티비티가 사용자의 포커스를 갖게됩니다.

 

백 스택에 중복된 액티비티가 존재할 수 있다는 점을 유의해야 하는데, 이와 관련해서 FLAG라는 것이 등장합니다.

 

FLAG를 사용하는 방법은 LaunchModeIntent Flag 두 가지가 있습니다.

*개념을 설명하는 과정에 위와 아래의 의미는 위 사진과 같이 백 스택을 형상화하여 보았을때의 의미이지, 실제로 최상위(root) 액티비티는 가장 먼저 호출된 액티비티를 의미합니다.

*설명 과정에서 자주 언급되는 onNewIntent() 메서드의 호출 시나리오는 onPause() -> onNewIntent() -> onResume()로 재활용되기 전 기존의 액티비티에선 onStop() 메서드가 호출되지 않는 것입니다.

*어플리케이션 안에선 모든 액티비티가 default affinity를 가지고 같은 태스크에서 동작하는 것이 기본입니다.

 

LaunchMode는 manifest 파일에서 액티비티 태그 내에서 설정할 수 있으며, 액티비티 실행 시 백 스택에 대한 운영 방식을 설정하는 standard / singleTop / singleTask / singleInstance 총 4 가지 모드가 있습니다.

  • standard : 액티비티를 항상 스택에 쌓아올립니다.(default)
  • singleTop : 액티비티가 태스크에 존재하며 이 액티비티의 인스턴스가 태스크의 가장 위에 존재할 때만 새 액티비티 생성하지 않고 onNewIntent() 메서드를 통해 기존의 액티비티를 재활용합니다.
  • singleTask : 해당 액티비티는 태스크의 root Activity에만 존재할 수 있습니다. 처음 호출하는 경우 새로운 태스크를 생성하고 이 태스크에 액티비티를 생성하고, 이미 해당 액티비티가 생성된 상태에서 다시 호출된다면 onNewIntent() 메서드를 통해 기존의 액티비티를 재활용합니다. 
  • singleInstance : 액티비티가 호출되면 항상 새 태스크를 생성합니다. 즉, 하나의 태스크에 하나의 액티비티만 존재합니다.

 

Intent Flag는 Intent 객체에 addFlag() 메서드를 통해 새로운 FLAG를 추가하거나 setFlags() 메서드를 통해 Flag를 새로 설정할 수 있습니다.

  •  FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET : 태스트가 백그라운드에서 포그라운드로 변하는 경우 플래그가 설정된 액티비티보다 위에 있는 액티비티가 모두 삭제됩니다.
  • FLAG_ACTIVITY_CLEAR_TOP : 액티비티가 태스크에 존재하는 경우 해당 액티비티 보다 상위에 존재하던 액티비티들을 제거하고 해당 액티비티를 호출합니다. 즉, 호출된 액티비티가 가장 위에 존재하게 됩니다.
  • FLAG_ACTIVITY_FORWARD_RESULT : startActivityForResult를 통해 액티비티를 호출하는 경우 호출하는 액티비티에서 호출된 다음 액티비티로 부터 Result를 받을 수 있습니다. 자세하게 설명하자면, A 액티비티에서 startActivityForResult를 통해 B 액티비티를 호출하고 B 액티비티에서 해당 Flag를 이용해 C 액티비티를 호출하면 C 액티비티에서 setReulst() 메서드를 통해 A 액티비티에게 전달할 데이터를 설정하여 반환할 수 있습니다.
  • FLAG_ACTIVITY_NEW_TASK : 새로운 태스크를 생성하고 해당 태스크에 액티비티를 추가할 때 사용합니다. 앞서 말한 조건을 만족하는 상황에서 호출된 액티비티와 동일한 *어피니티(affinity)를 갖는 태스크가 있을 경우 해당 태스크에 액티비티가 이동됩니다. 즉, 무조건적으로 태스크가 새로 생성되는 것은 아닐 수 있다는 것을 유의해야합니다.
  • FLAG_ACTIVITY_MULTIPLE_TASK : 이 속성은 FLAG_ACTIVITY_NEW_TASK와 함께 사용하지 않으면 아무 효과가 없습니다. FLAG_ACTIVITY_NEW_TASK와 함께 사용하면 무조건 새로운 태스크를 생성하며 액티비티는 해당 태스크에 root Activity가 됩니다.
  • FLAG_ACTIVITY_SINGLE_TOP : 호출된 액티비티가 가장 위에 존재하는 경우 액티비티를 onNewIntent() 메서드를 통해 재활용합니다. 
  • FLAG_ACTIVITY_NO_HISTORY : 액티비티가 스택에 추가되지 않습니다.
  • FLAG_ACTIVITY_NO_ANIMATION : 액티비티가 시작될 때 애니메이션이 무시됩니다.

 

2. 액티비티 생명주기(Activity Lifecycle) 

사용자가 애플리케이션과 상호작용하는 과정에서 액티비티의 상태가 변경되면 Android 시스템이 상태가 변경되었음을 알기위해 콜백 메서드를 호출하게 되는데 이 메서드들을 Activity 생명주기 메서드라고 합니다.

 

액티비티 생명주기 메서드에는 onCreate(), onStart(), onRestart(), onResume(), onPause(), onStop(), onDestroy()가 있습니다.

  • onCreate() : 액티비티 인스턴스가 최초로 생성될 시 호출되며 액티비티가 생성되기 전 소멸된 동일한 액티비티의 인스턴스로 부터 매개변수인 savedInstanceState라는 Bundle 객체가 전달되기 때문에 상태를 유지할 수 있습니다. 클래스 인스턴스 생성, ViewBinding 및 DataBinding을 사용하여 Layout과 연결, ViewModel 연결 등의 대부분의 초기화 작업을 진행합니다.
  • onRestart() : 액티비티가 런타임 시스템에 의해 중단되었다가 다시 시작될 때 호출됩니다.
  • onStart() : onStart() 메서드 호출은 처음 생성 이후 호출될 때와 onRestart 콜백을 수신 후 재시작될 때 호출되며, 해당 메서드는 Activity가 사용자에게 표시되기 직전에 빠르게 완료가 되고 onResume 콜백을 호출합니다.
  • onResume() : 액티비티가 백 스택의 최상단에 존재하며, 사용자에게 보여지는 상태로 사용자와 상호작용할 수 있게됩니다.
  • onPause() : 액티비티의 화면의 일부가 가려지는 시점이나 포커스를 잃은 경우 호출되어 잠깐 실행되므로 오랜 시간이 소요되는 작업은 하지않는 것이 좋습니다. 
  • onStop() : 액티비티가 더 이상 사용자에게 보이지 않는 경우 호출되며, 액티비티가 메모리에 남아 데이터를 관리하고 있는 상태이기 때문에 액티비티가 포그라운드로 돌아가면 초기화 작업을 하지않고 바로 onRestart() 메서드가 호출되고, 액티비티가 종료되면 onDestroy() 메서드가 호출됩니다. onDestroy()에서 설명하겠지만 액티비티에서 사용되지 않을 리소스들을 해제하거나 상태 유지를 위한 데이터를 저장하기 위한 작업을 진행하기에 적합합니다.
  • onDestroy() : 액티비티가 소멸되기 직전에 호출되며 finish() 메서드 호출, Configuration Change, 시스템에 의해 메모리가 해제되는 경우 호출됩니다. 중요한 점은 액티비티가 종료될 때 항상 호출되는 것이 아니기 때문에 리소스 해제 및 데이터 저장 작업을 onStop 상태에서 하는 것이 좋습니다.

 

3. 액티비티 클래스(Activity class) 

우리는 애플리케이션을 개발하기 위해서 액티비티를 생성해야 하는데 액티비티를 생성하려면 Activity class를 상속받는 클래스를 만들어야 합니다.

 

액티비티 생성에 주로 사용되는 Activity class에는 Activity, FragmentActivity, AppCompatActivity가 있습니다.

Activity(android.app.Activity) : 안드로이드 버전의 기본 라이브러리 액티비티 클래스이며, 액티비티 클래스는 이 액티비티 클래스의 서브 클래스가 됩니다.

 

FragmentActivity(android.support.v4.app.FragmentActivity) : 과거 버전과의 호환성을 유지하면서 프래그먼트를 사용할 때 필요한 액티비티 클래스입니다.

 

AppCompatActivity(android.app.support.v7.AppCompatActivity) : 과거 안드로이드 버전과의 호환성을 유지하면서 새로운 버전의 기능도 사용할 수 있도록 만들어진 액티비티 클래스로 Activity와 FragmentActivity 모두를 상속 받는 클래스이기 때문에 Activity의 기본 기능 + Framgment도 쉽게 사용할 수 있습니다.

프로젝트 생성 시 기본적으로 액티비티를 AppCompatActivity 클래스의 서브 클래스로 생성해주고 있습니다.


 

4. 예제

 

1. 액티비티 호출

# 1. 애플리케이션 실행
D/DEBUG_LOG: [ MainActivity.kt ] onCreate
D/DEBUG_LOG: [ MainActivity.kt ] onStart
D/DEBUG_LOG: [ MainActivity.kt ] onResume

# 2. B액티비티 호출
D/DEBUG_LOG: [ MainActivity.kt ] onPause
D/DEBUG_LOG: [ BActivity.kt ] onCreate
D/DEBUG_LOG: [ BActivity.kt ] onStart
D/DEBUG_LOG: [ BActivity.kt ] onResume
D/DEBUG_LOG: [ MainActivity.kt ] onStop

# 3. 뒤로 가기 버튼 클릭
D/DEBUG_LOG: [ BActivity.kt ] onPause
D/DEBUG_LOG: [ MainActivity.kt ] onRestart
D/DEBUG_LOG: [ MainActivity.kt ] onStart
D/DEBUG_LOG: [ MainActivity.kt ] onResume
D/DEBUG_LOG: [ BActivity.kt ] onStop
D/DEBUG_LOG: [ BActivity.kt ] onDestroy

 

2. 화면 전환

# 1. 애플리케이션 실행
[ MainActivity.kt ] onCreate
[ MainActivity.kt ] onStart
[ MainActivity.kt ] onResume

# 2. 세로에서 가로로 화면 전환
D/DEBUG_LOG: [ MainActivity.kt ] onPause
D/DEBUG_LOG: [ MainActivity.kt ] onStop
D/DEBUG_LOG: [ MainActivity.kt ] onDestroy
D/DEBUG_LOG: [ MainActivity.kt ] onCreate
D/DEBUG_LOG: [ MainActivity.kt ] onStart
D/DEBUG_LOG: [ MainActivity.kt ] onResume

# 3. 가로에서 세로로 화면 전환
D/DEBUG_LOG: [ MainActivity.kt ] onPause
D/DEBUG_LOG: [ MainActivity.kt ] onStop
D/DEBUG_LOG: [ MainActivity.kt ] onDestroy
D/DEBUG_LOG: [ MainActivity.kt ] onCreate
D/DEBUG_LOG: [ MainActivity.kt ] onStart
D/DEBUG_LOG: [ MainActivity.kt ] onResume

 

 

3. 홈 버튼

# 1. 애플리케이션 실행
[ MainActivity.kt ] onCreate
[ MainActivity.kt ] onStart
[ MainActivity.kt ] onResume

# 2. 홈버튼
D/DEBUG_LOG: [ MainActivity.kt ] onPause
D/DEBUG_LOG: [ MainActivity.kt ] onStop

# 3. 백그라운드에 있던 애플리케이션 실행
D/DEBUG_LOG: [ MainActivity.kt ] onRestart
D/DEBUG_LOG: [ MainActivity.kt ] onStart
D/DEBUG_LOG: [ MainActivity.kt ] onResume

 

4. 폰트 사이즈 변경

# 1. 애플리케이션 실행
[ MainActivity.kt ] onCreate
[ MainActivity.kt ] onStart
[ MainActivity.kt ] onResume

# 2. 홈버튼
D/DEBUG_LOG: [ MainActivity.kt ] onPause
D/DEBUG_LOG: [ MainActivity.kt ] onStop

# 3. 폰트 사이즈 변경 후 백그라운드에 있던 애플리케이션 실행
D/DEBUG_LOG: [ MainActivity.kt ] onDestroy
D/DEBUG_LOG: [ MainActivity.kt ] onCreate
D/DEBUG_LOG: [ MainActivity.kt ] onStart
D/DEBUG_LOG: [ MainActivity.kt ] onResume

 

5. FLAG

# launchMode 설정

<activity
    android:name=".MainActivity"
    android:launchMode="속성값"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
# MainActivity launchMode: singleTop
# standard인 경우 태스크에 메인액티비티 두 개가 존재하지만 singleTop인 경우 하나의 메인액티비티를 재활용합니다.

# 1. 애플리케이션 실행
[ MainActivity.kt ] onCreate
[ MainActivity.kt ] onStart
[ MainActivity.kt ] onResume

# 2. 홈버튼
[ MainActivity.kt ] onPause
[ MainActivity.kt ] onResume
# MainActivity launchMode: singleTask
# 4번 작업의 로그를 보면 standard의 경우 C액티비티 위에 새 Main액티비티가 쌓이지만 singleTask의 경우 Main액티비티가 root가 되며 B, C액티비티가 소멸됩니다.

# 1. 애플리케이션 실행
[ MainActivity.kt ] onCreate
[ MainActivity.kt ] onStart
[ MainActivity.kt ] onResume

# 2. B액티비티 호출
[ MainActivity.kt ] onPause
[ BActivity.kt ] onCreate
[ BActivity.kt ] onStart
[ BActivity.kt ] onResume
[ MainActivity.kt ] onStop

# 3. C액티비티 호출
[ BActivity.kt ] onPause
[ CActivity.kt ] onCreate
[ CActivity.kt ] onStart
[ CActivity.kt ] onResume
[ BActivity.kt ] onStop

# 4. Main액티비티 호출
[ CActivity.kt ] onPause
[ BActivity.kt ] onDestroy
[ MainActivity.kt ] onRestart
[ MainActivity.kt ] onStart
[ MainActivity.kt ] onResume
[ CActivity.kt ] onStop
[ CActivity.kt ] onDestroy

# 5. 뒤로가기 버튼 클릭
[ MainActivity.kt ] onPause
[ MainActivity.kt ] onStop

# 6. 네비게이션 바에서 앱 종료
[ MainActivity.kt ] onDestroy