상황 별 호출되는 액티비티의 생명주기 메소드들

안드로이드의 앱을 구성하는 일명 4대 컴포넌트는 모두 생명주기가 있다. 이 중, 가장 복잡한 생명주기를 가진 컴포넌트가 액티비티다. 액티비티의 생명주기에 대한 글은 수없이 많다. 하지만, 그런 글들을 읽고 액티비티의 생명주기를 이해하더라도, 문제 분석 시 ‘그래서 지금 사용자가 뭘 했길래 이렇게 호출된거야?’라는 생각이 드는 경우가 있다. 그러므로, 여기서는 액티비티 생명주기 소개 대신에 상황 별로 호출되는 액티비티의 생명주기 메소드들을 정리한다.

https://developer.android.com/reference/android/app/Activity

참고로, 여기서 상황이란 ‘앱 아이콘을 통해 액티비티를 최초 실행 시’ 등 액티비티에게 변화를 주는 액션을 사용자가 하는 때이며, O OS 단말 기준으로 테스트했다.

onCreate() > onStart() > onResume() 순으로 호출되는 경우

  • (액티비티 미실행 상태) – 앱아이콘 클릭 – 액티비티 최초 실행
  • (최근 앱 목록 표시 중, 액티비티는 미실행 상태) – 액티비티 선택 – 액티비티 실행

onCreate() > onStart() > onResume() > onPause() > onStop() 순으로 호출되는 경우

  • (화면 잠김 상태) – ADB 커맨드를 통한 액티비티 실행

onRestart() > onStart() > onResume() 순으로 호출되는 경우

  • (액티비티 foreground 상태에서 화면 잠김 상태) – 화면 잠금 해제(홈이나 전원 버튼, 필요하다면 비밀번호 입력 등) – 액티비티 실행
  • (액티비티를 실행한 적이 있으며 숨겨져 있는 상태) – 액티비티 실행(앱 아이콘 클릭 등) – 액티비티 실행

onPause()만 호출되는 경우

  • (액티비티 foreground 상태) – 다른 액티비티 실행

onPause() > onStop() 순으로 호출되는 경우

  • (액티비티 foreground 상태) – 전원 버튼 누르기 – 화면 잠금
  • (액티비티 foreground 상태) – 홈 버튼 누르기 – 런쳐 홈으로 이동

onPause() > onStop() > onDestroy() 순으로 호출되는 경우

  • (액티비티 foreground 상태) – 뒤로가기 버튼 눌러 액티비티 나가기

onDestroy()만 호출되는 경우

  • (최근 앱 목록 표시 중) – 액티비티 제거하기(밀어내기나 ‘X’ 버튼)

그 밖에

  • 액티비티를 실행한 적이 있다는 말의 의미는, 액티비티 컴포넌트가 소멸되지 않고 메모리에 상주하고 있어 재사용될 수 있다는 의미다.
  • 최근 목록 앱에 액티비티가 표시되더라도 액티비티는 미실행 상태일 수 있다. 최근 목록 앱은 파일 형태로 저장되어 단말 재부팅 후에도 히스토리 관리를 위해 유지되기 때문이다.
  • 액티비티 위에 다이얼로그를 표시하는 경우는, 생명주기 변화가 없다. 액티비티 위에 액티비티가 뜨는 경우에만, 변화가 있다.
  • 액티비티 컴포넌트의 onDestroy() 메소드가 호출되지 않고도, 자원 부족으로 인해 컴포넌트가 사라지기도 한다.

참고

Fragment의 onActivityResult로 결과 받기

안드로이드 어플리케이션은 컴포넌트로 구성된다. ActivityManager는 이름과 달리 (액티비티 사이가 아닌) 컴포넌트들 사이에 Intent 형태의 데이터를 주고 받을 수 있게 해준다. 특히, 액티비티 간 데이터를 주고 받을 때 용이하도록 액티비티는 startActivity(), startActivityForResult(), onActivityResult() 메소드를 제공한다. 이 메소드들은 프레그먼트에도 있는데, 여기서는 프레그먼트의 onActivityResult()에 대해 살펴보자.

결론부터 말하면, 프레그먼트의 onActivityResult()도 다른 액티비티에서 데이터를 받을 수 있다. 다른 특별한 처리 없이 말이다.

지금 인터넷을 보면 액티비티의 onActivityResult()만 다른 액티비티나 프레그먼트에서 startActivityForResult() 통해 요청한 작업의 결과를 받을 수 있다고 한다. 그리고, 프레그먼트의 onActivityResult()에 데이터를 전달하는 다양한 방법들(FragmentManager를 통한 프레그먼트의 onActivityResult() 명시적 호출, 이벤트 버스 사용 등)을 소개한다. 글의 말미에 안드로이드가 이상하게 동작한다는 불만도 잊지 않는다.

아마도 이런 오해는 어떤 메소드를 호출하느냐에 따라 액티비티 간 데이터 전달 동작이 다르다는 것을 몰라 생긴 것 같다. 데이터를 주고 받는 규칙을 살펴보자.

  • 액티비티의 onActivityResult()는 액티비티나 프레그먼트 둘 중 어느 것의 결과든 받는다.
  • 프레그먼트의 onActivityResult()는 프레그먼트의 startActivityForResult()를 통해 호출한 액티비티에서만 결과를 받는다.
  • 프레그먼트는 액티비티의 onActivityResult()가 호출 된 후 호출 된다. 정확히, 프레그먼트의 onActivityResult()는 액티비티의 onActivityResult() 코드 내부에서 호출된다.

그러므로, 아래와 같이 사용하면 결과를 받을 수 없다.

  • 프레그먼트 안에서 (예를 들면, getActivity() 같은 메소드를 통해 얻은) 액티비티 객체의 startActivityForResult()를 호출한다. 이 때는, 액티비티의 startActivityForResult()를 호출한 것이므로 액티비티로만 결과가 전달된다.
  • 내 액티비티에서 onActivityResult() 메소드를 오버라이드 한 후, 이 메소드에서 super.onActicityResult()로 결과를 전달하지 않고 전달받은 결과 데이터를 소진한다. 프레그먼트는 데이터를 액티비티의 onActivityResult()를 통해 전달받기 때문에 super.onActivityResult()로 값을 전달하지 않으면 프레그먼트에도 전달되지 않는다.

그러므로 프레그먼트에서 호출하고 결과도 받고자 한다면, 아래와 같이 해야한다.

  • Activity.startActivityForResult()가 아닌Fragment.startActivityForResult()를 사용해 호출한다.

프레그먼트 속 onActivityResult()로 결과가 오지 않아 힘들어 했다면, 지금이라도 바르게 사용해 보자.

그 밖에

  • 사실 액티비티에서 프레그먼트에 결과를 전달하는 과정은 인터넷에 알려진 FragmentManager를 이용한 자식 프레그먼트들의 onActivityResult() 명시적 호출과 크게 다르지 않다.
  • 프레그먼트를 추가할 때 건넨 tag를 이용해 프레그먼트를 찾기라도 한다면, 이 태그를 통해 액티비티와 프레그먼트가 강결합하게 되어 더 나쁘다.
  • 액티비티-프레그먼트A-프레그먼트B 순으로 포함하는 계층 구조라도, 프레그먼트 B에서 호출한 액티비티의 결과를 프레그먼트 A가 받지는 않는다. 액티비티에서 각 프레그먼트에 결과 값을 전달한다.
  • Fragment의 공식 문서를 보면 프레그먼트의 onActivityResult()에 대한 설명이 단촐하다. ‘액티비티의 onActivityResult()와 관련이 있다.’는 언급이 있을 뿐이다.
  • 액티비티의 onActivityResult()는 스코프(scope)가 protected이고, 프래그먼트의 그 것은 public이다. 액티비티에서 호출하기 위한 목적일 것이다.

참조