본문 바로가기
안드로이드/Android

[Android] Activity

by jinwo_o 2024. 8. 9.
  • Activity 는 애플리케이션의 화면을 구성하는 컴포넌트로 하나의 Activity는 하나의 화면을 나타내며, Activity 에서 출력한 내용이 안드로이드 디바이스의 화면에 나타낸다.
  • 사용자는 액티비티가 생성한 UI 를 통해 애플리케이션과 상호 작용할 수 있다.
  • 각 액티비티는 독립적이어서 앱이 사용자와 상호작용하기 위한 진입점 역할을 하기도 한다.
  • 액티비티는 하나 이상의 뷰(View)나 뷰 그룹(View Group)을 가지고 있어야 하며, 이를 통해 UI를 표현한다.


Activity Lifecycle

  • Activity 클래스는 수명 주기 상태가 변경되었음을 Activity 에 알리는 주요한 메서드들을 제공한다.
  • 핵심 콜백 메소드로는 onCreate(), onStart(), onResume(), onPause(), onStop(), onDestroy() 이렇게 6가지가 존재한다.
  • 액티비티가 새로운 상태게 들어가게 되면, 시스템은 각 콜백 메소드를 호출하게 된다.
  • 해당 메서드들은 아래 그림에 표시된 생명 주기 순서에 따라 호출된다.
  • 생명 주기 콜백 메서드를 사용하여 Activity 가 동작하는 방식을 정의하고, 리소스를 효율적으로 관리할 수 있다.

Doit! 깡샘의 안드로이드 앱 프로그래밍 with 코틀린


1. onCreate()

  • 이 콜백은 시스템이 Activity 를 생성할 때 호출된다.
  • Activity 의 생명 주기에서 단 한 번만 발생해야 하는 대부분의 초기화 로직은 여기에서 처리되어야 한다. (예: View 초기화 및 데이터 바인딩)
  • 반드시 오버라이딩하여 구현해야 한다.
  • 액티비티 전체 수명 주기 동안 딱 한 번만 동작되는 초기화 및 시작 로직을 실행할 수 있다.
  • 예: RecyclerView 에 데이터를 바인딩하기, 버튼에 View.OnClickListener 를 설정하는 등의 동작

2. onStart()

  • 이 콜백은 onCreate() 메서드를 호출한 후, Activity 가 사용자에게 보여질 때 호출된다.
  • 여러 개의 Activity 또는 애플리케이션 간에 화면을 전환하는 경우 이 콜백 함수는 두 번 이상 호출될 수 있다.
  • 이 메소드가 호출되면 액티비티가 사용자에게 보여지고, 포그라운드 태스크로써 사용자와 상호작용할 수 있도록 준비한다.
  • 액티비티가 'STARTED' 상태에 진입하게 되어 시스템은 onStart() 와 onResume() 을 연달아 호출하게 된다.

3. onResume()

  • Activity 가 foreground 로 노출되어 사용자와 상호 작용할 준비가 되었음을 의미한다.
  • onStart() 는 매우 빠른 속도로 실행되고, 액티비티가 'RESUMED' 상태에 진입함과 동시에 onResume() 메소드를 호출하게 된다.
  • 액티비티가 'RESUMED' 상태에 진입하게 되면 포그라운드에 액티비티가 표시되고 앱이 사용자와 상호작용을 할 수 있는 상태가 된다.
  • 포그라운드에서 사용자에게 액티비티가 보여지는 동안 실행해야 하는 모든 기능을 활성화 할 수 있게 된다.

4. onPuase()

  • Activity 가 더 이상 사용자에게 보여지고 있지 않지만, 여전히 부분적으로 보여질 수 있음을 의미한다. (예: 사용자가 멀티 윈도우 모드에 있는 경우).
  • 대부분의 경우는 사용자가 Activity 를 떠나고 생명 주기가 다음 상태로 전환됨을 나타낸다.
  • 사용자가 잠시 액티비티를 떠났을 때 (다른 액티비티에 포커스를 뒀을 때) 호출되는 콜백 메소드이다.
  • 즉, 해당 액티비티가 포그라운드에 있지 않게 되었다는 것을 의미한다.
  • 액티비티가 포그라운드에 없을 동안 계속 실행되어서는 안 되지만 언젠가 다시 시작할 작업을 일시중지하는 작업을 수행한다.

5. onStop()

  • 이 콜백은 Activity 가 사용자에게 더 이상 보여지지 않을 때 호출된다.
  • 여러 Activity 또는 애플리케이션 간에 전환하는 경우 이 콜백 함수는 두 번 이상 호출될 수 있다.
  • 액티비티가 사용자에게 더 이상 표시되지 않으면 'STOPPED' 상태에 진입하고 시스템이 onStop() 콜백 메소드를 호출한다. 
  • 즉, 새로 시작된 액티비티가 화면 전체를 차지할 경우에 해당된다. 혹은 액티비티의 실행이 완료되어 종료될 시점에 onStop() 를 호출할 수도 있다.
  • 이 메소드에서는 필요하지 않은 리소스를 해제하거나 조정해야 한다. 예를 들어 애니메이션을 일시중지하거나, GPS 사용 시 배터리를 아끼기 위해 위치 인식 정확도를 '세밀한 위치' 에서 '대략적인 위치' 로 전환할 수 있다.

6. onDestroy()

  • 이 콜백은 Activity 가 완전히 파괴되기 전에 호출된다.
  • 시스템은 Activity 가 종료되거나 화면전환으로 인해 시스템이 Activity 를 일시적으로 파괴할 때 이 콜백을 호출한다.
  • 이 콜백은 남은 모든 리소스를 해제하고 Garbage Collector 가 할당된 모든 메모리를 회수하도록 할 때 사용할 수 있다.
  • 액티비티가 완전히 소멸되기 전에 이 콜백 메소드가 호출된다. 아래와 같은 경우 액티비티가 완전히 소멸된다.
    1. finish() 가 호출되거나 사용자가 앱을 종료하여 액티비티가 종료되는 경우
    2. *화면 구성이 변경되어 일시적으로 액티비티를 소멸시키는 경우
      • 화면모드(가로모드, 세로모드)의 전환, 입력기기(키보드) 변경, 지역 및 언어의 변경, 다크 모드 등
        • 구성 변경 : 기기 상태가 매우 급격하게 변경되어 액티비티가 완전히 종료되고 다시 빌드하는 것

Activity 상태 변화

1. 앱을 최초로 실행했을 경우

  • onCreate → onStart → onResumse

2. 앱을 실행하고 종료했을 경우

  • onCreate → onStart → onResume → onPause → onStop → onDestroy

3. 앱을 실행하고 홈 버튼을 눌러 백그라운드로 보낸 후 다시 실행한 경우

  • onCreate → onStart → onResume → onPause → onStop → onRestart → onStart → onResume

4. 화면을 회전한 경우

  • (onResume →) onPuase → onStop → onDestroy → onCreate → onStart → onResume

5. 다이얼로그(Dialog) 실행하고 다시 홈으로 돌아온 경우

  • 새로운 액티비티가 최상단으로 온 상황이 아니기 때문에 아무런 함수가 호출되지 않는다.

6. (공유 버튼으로) 화면의 일부만 가려진 경우

  • (onResume →) onPase → onResume

7. 첫 번째 액티비티에서 두 번째 액티비티로 화면을 전환할 경우

  • 첫번째 액티비티에서 onCreate, onStart, onResume, onPause 까지 호출되고, 두번째 액티비티에서 onCreate, onStart, onResume 을 호출한 이후에 다시 첫번째 액티비티에서 onStop을 호출된다.

8. 두 번째 액티비티에서 화면을 종료하고 첫 번째 액티비티로 다시 돌아갈 때

  • 두번째 액티비티에서 onPause를 호출한 다음, 첫번째 액티비티에서 onRestart, onStart, onResume 이 호출되고 두번째 액티비티에서 onStop, onDestroy가 호출된다.


Activity 상태 유지

  • Configuration Changed 로 인해 원하지 않는 경우에도 onDestroy 가 호출되기 때문에, 기존에 Activity 가 가지고 있던 UI 상태들을 일게 된다.
  • 비정상적인 종료에도 상태를 저장하기 위해 onSaveInstanceState() 를 사용할 수 있다.
    • onSaveInstanceState() : 액티비티가 가지는 콜백 중 하나로, 화면 재생성에 대비하여 액티비티의 상태를 *Bundle 로 저장하는 함수
      • Bundle : Map형태로 구현된 데이터의 묶음(Bundle)이다. Map 형태라 key 값이 String 이며, value 값에는 Int, String 과 같은 간단한 타입부터 Serializable, Parcelable 같은 복잡한 타입이 들어올 수 있다.
        • 데이터 저장 객체로 상태 저장 및 복구에 사용된다. Activity가 onStop() 되기 전에 onSavedInstanceState() 에서 저장할 데이터를 저장시키며, onStart() 이후에 onRestoreInstanceState() 에서 복구시킨다.
// Android P (API 28) 이후 부터는 onStop 이후에 onSaveInstanceState 를 발생시킨다.
@CallSuper
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
    Lifecycle lifecycle = getLifecycle();
    if (lifecycle instanceof LifecycleRegistry) {
        // 해당 시점의 Lifecycle의 상태를 CREATED 로 설정하고 super 를 호출한다.
        ((LifecycleRegistry) lifecycle).setCurrentState(Lifecycle.State.CREATED);
    }
    super.onSaveInstanceState(outState);
    mSavedStateRegistryController.performSave(outState);
}
override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)

    outState.putString("KEY", "VALUE")
}

 

상태 복원

1. onCreate() 는 Nullable 한 Bundle 객체를 통해 이전 상태를 복원할 수 있다.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    
    if (savedInstanceState != null) {
        // restore data
    }
}

 

2. onRestoreInstanceState()

protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
    if (mWindow != null) {
        Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
        if (windowState != null) {
            mWindow.restoreHierarchyState(windowState);
        }
    }
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
    super.onRestoreInstanceState(savedInstanceState)
    
    val state = savedInstanceState.getString("KEY")
}
  • onRestoreInstanceState 는 저장된 상태가 있고, Activity 가 다시 초기화되었을 때 onStart 이후에 호출되는 콜백 메소드이다.
  • 마찬가지로 Bundle 을 통해 상태를 복원할 수 있고, onCreate 와는 다르게 Non-null 한 Bundle 을 사용한다.
  • 항상 호출되는 Lifecycle 콜백이 아닌 반드시 비정상적인 종료에 의해 호출되는 콜백이기 때문에 어떤 데이터든 무조건 포함되어 있다.

 

화면을 회전한 경우

  • (onResume →) onPuase → onStop → onSaveInstanceState → onDestroy → onCreate → onStart → onRestoreInstanceState → onResume

 

반전

1. onStop() 이 불렸다고 해서 onSaveInstanceState() 가 항상 불리는 것은 아니다.

  • 액티비티가 올라온 상태에서 백버튼을 누르면 onSaveInstanceState() 가 호출되지 않는다.
  • 상태를 보존할 필요가 없기 때문이다. onSaveInstanceState() 는 액티비티의 재생성에 대비해서 현재 상태를 저장한다.
  • finish() 함수를 호출하면 onSaveInstanceState() 가 호출되지 않는다.

2. onSaveInstanceState() 가 onStop() 보다 먼저 호출될 수 있다.

  • HoneyComb 미만 : onPause() 직전
  • HoneyComb 이상, Pie 미만 : onStop() 직전
  • Pie 이상 : onStop() 직후
  • WHY) 버전에 따라 process kill 당할 수 있는 기준이 다르기 때문이다. (자세히)

생명주기를 활용하여 에러 방지하기

1. 사용자가 앱을 사용하는 도중에 전화가 걸려오거나 다른 앱으로 전환할 때 비정상 종료되는 문제

  • 활동이 전화나 다른 앱으로 가려질 때에는 onStop() 이, 재개될 때에는 onResume() 이 실행된다.
  • 따라서, 이 두 콜백 메서드를 잘 활용해 종료를 방지할 수 있다.

2. 사용자가 앱을 활발하게 사용하지 않는 경우 귀중한 시스템 리소스가 소비되는 문제

  • 액티비티를 떠났는데 백그라운드 작업이 종료되지 않는 경우
  • onDestroy() 또는 onStop() 에서 문제가 되는 작업을 종료시키면, 액티비티가 종료되거나 잠시 떠나 있을 때 해당 작업도 자동으로 중지된다.

3. 사용자가 앱에서 나갔다가 나중에 돌아왔을 때 사용자의 진행 상태가 저장되지 않는 문제

  • 상태를 임시 저장해야 할 경우에는, onStop() 이후에 onSavedInstanceState() 라는 메서드가 자동으로 실행된다.
  • (안드로이드 P 이후 기준) 또한 활동을 복구해야 할 때에는, onRestoreInstanceState() 메서드가 자동으로 실행된다.
  • 따라서, onSavedInstanceState() 와 onRestoreInstanceState() 수명 주기 콜백을 활용해 방지할 수 있다.

4. 화면이 가로 방향과 세로 방향 간에 회전할 경우, 비정상 종료되거나 사용자의 진행 상태가 저장되지 않는 문제

  • 이 문제 역시 3번의 onSavedInstanceState() 로 어느 정도는 해결할 수 있을 것이다.
  • 다만, 회전의 경우에는 configChanges 를 이용해 처음부터 다시 시작되지 않게 사전에 처리할 수 있다.

Task, 백 스택

  • 사용자가 특정 작업을 할 때 상호작용하는 Activity 의 집합
  • 하나의 Task 에는 액티비티 집합을 열린 순서대로 정렬해놓는 백 스택이 존재한다.
  • Stack 의 성질을 가지고 있으며 가장 최신에 열린 액티비티가 가장 맨위로 위치하게 된다.
  • 예시: 이메일 앱에는 새 메시지 목록을 표시하는 액티비티가 존재하고, 사용자가 메시지 목록에서 메시지를 하나 클릭하면 메시지의 내용을 상세하게 볼 수 있도록 새로운 액티비티가 열리게 된다. 이때 메시지 상세보기 액티비티는 백 스택에 추가되며, 만약에 사용자가 뒤로가기 버튼을 탭하면 메시지 상세보기 액티비티는 백 스택에서 pop 된다.

 

백 스택의 기본 흐름

  • 사용자가 앱을 시작하면 해당 앱의 Task 가 포그라운드로 나오게 된다.
  • 만약, 백그라운드에 해당 앱의 Task 가 존재하지 않는다면 새로운 Task 가 생성되고, 이 앱의 MainActivity 가 백 스택의 루트 액티비티로 열리게 된다.

 

  • 현재 액티비티가 또 다른 액티비티를 시작하면 새 액티비티가 백 스택 맨 위에 푸시되고 포커스를 갖게 된다.
    • 이전 액티비티는 스택에 남아있지만 중지됨 상태를 갖게 된다. 액티비티가 중지되면 시스템은 UI의 상태를 보존한다.
    • 만약 사용자가 뒤로가기 버튼을 누르면 현재 액티비티가 pop 되고, 이전 액티비티가 UI 의 상태를 유지한채로 시작된다.
  • 스택의 액티비티는 절대로 다시 정렬되지 않으며, 이러한 성질을 계속 유지하게 된다.

 

  • 사용자가 계속 뒤로 버튼을 누르면 계속 이전 액티비티가 나타나고, 만약 모든 액티비티가 스택에서 삭제되면 Task 는 더 이상 존재하지 않게 된다.
  • 사용자가 새 Task 를 시작하거나 홈 버튼을 통해 홈 화면으로 이동할 때 현재 Task 는 통째로 백그라운드로 이동한다.
    • Task 의 모든 액티비티는 백그라운드에 있는 동안 모두 중지되지만, Task 의 백 스택은 그대로 유지된다.
  • 이후에 Task 가 다시 포그라운드로 돌아가게 되면 백 스택이 유지 되어있기에 사용자는 스택의 가장 상단에 있는 액티비티를 그대로 이어나갈 수 있다.

 

Intent Flag

FLAG_ACTIVITY_NEW_TASK

  • 액티비티를 새 Task 에서 시작한다.
  • 지금 시작하고 있는 액티비티에 대해 이미 실행 중인 Task 가 존재한다면 그 Task 의 마지막 상태가 복원되어 포그라운드로 이동하고 액티비티는 새 인텐트를 수신한다.
  • 즉, *singleTask 와 동일한 동작이 발생한다.
    • singleTask : 액티비티의 인스턴스가 이미 별도의 Task 에 있다면 시스템은 새 인스턴스를 생성하지 않고 기존 인스턴스로 라우팅한다. 즉, 액티비티의 인스턴스가 한 번에 하나만 존재할 수 있다.

FLAG_ACTIVITY_SINGLE_TOP

  • 시작 중인 액티비티가 백 스택의 맨 위에 있는 액티비티면 새 인스턴스가 생성되는 대신 기존 인스턴스가 인텐트를 수신한다.
  • 즉, singleTop 과 동일한 동작이 발생한다.
    • singleTop
      • 액티비티의 인스턴스가 현재 Task 의 백 스택 맨 위에 있으면 시스템은 액티비티의 인스턴스를 생성하지 않고 인텐트를 기존 인스턴스(맨 위에 있는)로 라우팅한다.
      • 액티비티는 여러 번 인스턴스화될 수 있고, 여러 Task 나 한 Task 에 여러 인스턴스가 존재할 수 있지만 만약 백 스택 맨 위에 동일한 액티비티가 존재한다면 새 인스턴스를 생성하지 않는다.

FLAG_ACTIVITY_CLEAR_TOP

  • 시작 중인 액티비티가 현재 Task 에서 이미 실행 중이면 액티비티의 새 인스턴스가 실행되는 대신 Task 의 맨 위에 있는 다른 모든 액티비티가 제거되고 이미 실행 중이던 액티비티가 맨 위로 위치하게 되며, 인텐트를 수신하게 된다.

 

[개념 콕] 안드로이드 4대 컴포넌트 - 내일배움캠프 블로그

내일배움캠프 수료생이 개발에 꼭 필요한 핵심 개념만 콕 집어 드립니다. | 앱 개발, 📚아티클

nbcamp.spartacodingclub.kr

 

Activity Lifecycle 완벽 정복하기

안드로이드 Activity 의 생명주기 알아보기

velog.io

 

안드로이드 개발자 로드맵 — Part2: App Components

Android developer roadmap은 총 5부로 연재될 예정이며, 각 회차는 안드로이드 생태계의 다양한 측면을 다룹니다. 2부에서는 App Components, Intents, App Entry Points에 대한 내용을 다룹니다.

velog.io

 

(Android) 놓치기 쉬운 Lifecycle

Activity Lifecycle 관리

medium.com

 

[내 맘대로 정리한 안드로이드] 액티비티 생명주기 (Activity Lifecycle)

생명주기(Lifecycle)를 알아야 하는 이유 액티비티 생명주기란 액티비티가 생성되고, 정지되고, 재생되고, 종료되는 등의 상태 변화와 그에 따른 콜백 메서드를 총칭한다. 안드로이드 개발자라면

holika.tistory.com

 

[ANDROID] 백 스택에 대한 이해

개요 Task에 대해 이해한다. Task와 액티비티와의 관계에 대해 이해한다. 백 스택의 동작에 대해 이해한다. Task, Activity, 백 스택 Task는 사용자가 특정 작업을 할 때 상호작용하는 Activity의 집합입니

ybdeveloper.tistory.com

'안드로이드 > Android' 카테고리의 다른 글

[Android] 메모리 누수  (0) 2024.09.03
[Android] App Manifest  (0) 2024.08.08
[Android] Gradle  (0) 2024.08.06