안드로이드 Activity와 Fragment lifecycle

LifeCycle(라이프 사이클)

안드로이드에서 라이프 사이클은 라이프 사이클을 갖는 컴포넌트(액티비티, 프래그먼트)가 처음 초기화될 때부터 마지막으로 파괴되고 시스템이 메모리를 회수할 때까지 컴포넌트가 거칠 수 있는 다양한 상태를 말합니다.

안드로이드에서는 이러한 다양한 상태에 맞는 콜백 메서드가 정의되어있습니다. 우리는 우리가 만든 컴포넌트들에서 이러한 콜백 메서드를 재정의하여 사용합니다. 다음의 다이어그램은 재정의 가능한 콜백 메서드와 상태를 보여줍니다.

Activity 라이프 사이클

onCreate()

  • 시스템이 액티비티를 생성할 때 실행됩니다. 필수적으로 구현해야 합니다.
  • 액티비티에 필요한 리소스를 초기화합니다.

onStart()

  • 액티비티가 사용자들에게 보여지도록 합니다.
  • 매우 짧은 시간에 이루어집니다.

onResume()

  • 액티비티가 앱의 포커스를 얻습니다.
  • 사용자가 액티비티와 상호작용 할 수 있습니다.

onPause()

  • 액티비티가 앱의 포커스를 잃습니다.
  • 사용자는 더이상 액티비티와 상호작용 할 수 없습니다.
  • onPause상태가 되는 예
    • 일부 이벤트가 앱 실행을 방해합니다. 이것이 가장 일반적인 사례입니다. (전화가 올 때 등)
    • Android 7.0(API 수준 24) 이상에서는 여러 앱이 멀티 윈도우 모드에서 실행됩니다. 언제든지 그중 하나의 앱(창)만 포커스를 가질 수 있기 때문에 시스템이 그 외에 모든 다른 앱을 일시중지시킵니다.
    • 새로운 반투명 액티비티(예: 대화상자)가 열립니다. 액티비티가 여전히 부분적으로 보이지만 포커스 상태가 아닌 경우에는 Pause 상태로 유지됩니다.
  • onPause()는 아주 잠깐 실행되므로 저장 작업을 실행하기에는 시간이 부족할 수 있습니다. (데이터 저장, 네트워크 호출, 데이터베이스 트랜잭션)

onStop()

  • 액티비티가 사용자에게 보이지 않으면 Stop상태가 된다.
  • 앱이 사용자에게 보이지 않을때, 사용하지 않을 리소스는 해제하는게 좋다.
  • onPause에서 실행되기에 적합하지 않은 무거운 저장 작업이 실행될 수 있습니다.

    참고: 액티비티가 Stop되면 시스템은 해당 액티비티가 포함된 프로세스를 소멸시킬 수 있습니다(시스템이 메모리를 복구해야 하는 경우). 활동이 Stop된 동안 시스템이 프로세스를 소멸시키더라도 Bundle(키-값 쌍의 blob)에 있는 View 객체(예: EditText 위젯의 텍스트) 상태가 그대로 유지되고, 사용자가 이 액티비티로 돌아오면 이를 복원합니다. 사용자가 돌아올 때의 활동 복원에 관한 자세한 내용은 활동 상태 저장 및 복원을 참조하세요.

onDestroy()

  • onDestroy()는 액티비티가 소멸되기 전에 호출됩니다. 시스템은 다음 중 하나에 해당할 때 이 콜백을 호출합니다.

    • (사용자가 액티비티를 완전히 닫거나 액티비티에서 finish()가 호출되어) 액티비티가 종료되는 경우
    • 구성 변경(예: 기기 회전 또는 멀티 윈도우 모드)으로 인해 시스템이 일시적으로 액티비티를 소멸시키는 경우
  • onDestroy() 콜백은 이전의 콜백(ex. onStop())에서 아직 해제되지 않은 모든 리소스를 해제해야 합니다.

어떤 빌드업 이벤트에서 초기화 작업을 실행하든 빌드업 이벤트에 상응하는 수명 주기 이벤트를 사용하여 리소스를 해제하세요. ON_START 이벤트 이후에 무언가를 초기화하는 경우, ON_STOP 이벤트 이후에 이를 해제하거나 종료하세요. ON_RESUME 이벤트 이후에 초기화하는 경우, ON_PAUSE 이벤트 이후에 해제하세요.
아래의 코드스니펫은 카메라 초기화 코드를 수명 주기 인식 구성요소에 넣습니다. 이 방법 대신 활동 수명 주기 콜백(예: onStart(), onStop())을 직접 넣을 수는 있지만 권장하지는 않습니다. 이 로직을 독립적인 수명 주기 인식 구성요소에 넣으면 코드를 복사하지 않고도 여러 활동에서 구성요소를 다시 사용할 수 있습니다. 수명 주기 인식 구성요소를 생성하는 방법에 대해 알아보려면 수명 주기 인식 구성요소로 수명 주기 처리를 참조하세요.

class CameraComponent : LifecycleObserver {

    ...

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun initializeCamera() {
        if (camera == null) {
            getCamera()
        }
    }

    ...
}

Fragment 라이프사이클


Android 프래그먼트 수명 주기는 Activity 수명 주기와 유사하며 여러 프래그먼트별 메서드가 있습니다.

onAttach()

  • 프래그먼트가 소유자 Activity과 연결될 때 호출됩니다.

onCreateView()

  • 프래그먼트와 연결된 뷰 계층을 생성하기 위해 호출됩니다.

onViewCreated()

  • onCreateView()가 반환된 직후에 호출되지만 저장된 상태가 뷰에 복원되기 전에 호출됩니다.

onDestroyView()

  • 프래그먼트와 연결된 뷰 계층이 제거되는 중일 때 호출됩니다.

onDetach()

  • 프래그먼트가 액티비티와 연결이 끊어지는 중일 때 호출됩니다.

로그

// 처음 앱 시작시
navigation I/MainActiviy: onCreate Called
navigation I/TitleFragment: onAttach called
navigation I/TitleFragment: onCreate called
navigation I/TitleFragment: onCreateView called
navigation I/TitleFragment: onViewCreated called
navigation I/TitleFragment: onStart called
navigation I/MainActivity: onStart Called
navigation I/MainActivity: onResume Called
navigation I/TitleFragment: onResume called

// 다른 프래그먼트로 이동시
navigation I/GameFragment: onAttach called
navigation I/GameFragment: onCreate called
navigation I/GameFragment: onCreateView called
navigation I/GameFragment: onViewCreated called
navigation I/GameFragment: onStart called
navigation I/GameFragment: onResume called
navigation I/TitleFragment: onPause called
navigation I/TitleFragment: onStop called
navigation I/TitleFragment: onDestroyView called
TitleFragment 제외 OnDetach called

// Upbutton으로 타이틀 프래그먼트로 이동시
navigation I/TitleFragment: onCreateView called
navigation I/TitleFragment: onViewCreated called
navigation I/TitleFragment: onStart called
navigation I/TitleFragment: onResume called
navigation I/GameFragment: onPause called
navigation I/GameFragment: onStop called
navigation I/GameFragment: onDestroyView called
navigation I/GameFragment: onDetach called

// BackButton으로 타이틀 프래그먼트로 이동시
Upbutton과 동일.

// BackButtoon으로 앱 종료시
navigation I/TitleFragment: onPause called
navigation I/MainActivity: onPause Called
navigation I/TitleFragment: onStop called
navigation I/MainActivity: onStop Called
navigation I/TitleFragment: onDestroyView called
navigation I/TitleFragment: onDetach called
navigation I/MainActivity: onDestroy Called

// 홈 화면으로 이동시
navigation I/GameFragment: onPause called
navigation I/MainActivity: onPause Called
navigation I/GameFragment: onStop called
navigation I/MainActivity: onStop Called

// 홈 화면에서 종료시
navigation I/GameFragment: onDestroyView called
navigation I/GameFragment: onDetach called
navigation I/TitleFragment: onDetach called
/* TitleFragment는 앱 종료시에만 종료되는듯 */
navigation I/MainActivity: onDestroy Called 

onSaveInstance()와 onRestoreInstance()를 통한 상태복구

이전에 만들었던 DessertClicker에서 디저트를 몇번 탭하고 홈화면에서 멀티윈도우 모드로 켜보면 onDestroy 콜백 메서드가 실행되어 앱이 초기상태로 돌아가는것을 볼 수 있다.

dessertclicker I/MainActivity: onCreate Called
dessertclicker I/MainActivity: onStart Called
dessertclicker I/MainActivity: onResume Called
// 홈화면으로 갔을 때
dessertclicker I/MainActivity: onPause Called
dessertclicker I/MainActivity: onStop Called
// 멀티 윈도우 실행했을 때
dessertclicker I/MainActivity: onDestroy Called
dessertclicker I/MainActivity: onCreate Called
dessertclicker I/MainActivity: onStart Called
dessertclicker I/MainActivity: onResume Called
dessertclicker I/MainActivity: onPause Called

하지만 우리는 멀티 윈도우로 실행 했을 때도 내가 사용하던 앱이 현재 상태를 유지하기를 기대합니다.

onDestroy() 이후에도 앱의 상태를 유지하기 위해서는 onSaveInstance()와 onRestoreInstance()를 사용해야합니다. (다른 방법이 있을수도 있음. 근데 난 모름...)

우선, onSaveInstance()와 onRestoreInstance()를 추가하여 로그를 찍어보면 아래와 같이 나옵니다.

// 앱 시작
dessertclicker I/MainActivity: onCreate Called
dessertclicker I/MainActivity: onStart Called
dessertclicker I/MainActivity: onResume Called
// 홍 버튼
dessertclicker I/MainActivity: onPause Called
dessertclicker I/MainActivity: onStop Called
dessertclicker I/MainActivity: onSaveInstanceState
// 멀티 윈도우 모드
dessertclicker I/MainActivity: onDestroy Called
dessertclicker I/MainActivity: onCreate Called
dessertclicker I/MainActivity: onStart Called
dessertclicker I/MainActivity: onRestoreInstanceState
dessertclicker I/MainActivity: onResume Called
dessertclicker I/MainActivity: onPause Called

이 로그를 통해 앱이 홈버튼을 누를 때, onSaveInstanceState()로 상태를 저장하고 멀티 윈도우 모드를 사용할 때, 새로 액티비티가 만들어지면서 onRestoreInstanceState()로 상태를 복구한다는 사실을 알 수 있습니다.

따라서 우리는 onSaveInstanceState()로 우리가 상태를 유지해야 하는 데이터의 값을 저장하고 onRestoreInstanceState()에서 저장한 값을 받아서 다시 변수에 집어넣어주면 됩니다.

DessertClicker에서의 예시

override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        outState?.run {
            putInt(stateRevenue, revenue)
            putInt(stateDesserts, dessertsSold)
            putInt(stateCurrDessert, currentDessert.imageId)
            Timber.i("onSaveInstanceState")
        }
        
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
        super.onRestoreInstanceState(savedInstanceState)
        val prevImageId = savedInstanceState?.getInt(stateCurrDessert)
        val prevRevenue = savedInstanceState?.getInt(stateRevenue)
        val prevDessert = savedInstanceState?.getInt(stateDesserts)
        binding.revenue = prevRevenue
        binding.amountSold = prevDessert
        binding.dessertButton.setImageResource(prevImageId)
        revenue = prevRevenue
        dessertsSold = prevDessert
        Timber.i("onRestoreInstanceState")
    }

출처

좋은 웹페이지 즐겨찾기