본문 바로가기
Android/안드로이드 프로그래밍 Next Step

안드로이드 프로그래밍 Next Step - 5.1 액티비티 생명주기

by 몽슬몽슬 2019. 3. 29.

액티비티 생명주기

1. 생명주기 메서드 호출 사이클

시작할 때
onCreate -> onStart -> onResume

회면 회전할 때 / 시스템에 의해 제거될 때 / Configuration(구성)이 변경될 때
onPause -> onSaveInstanceState -> onStop -> onDestroy -> onCreate -> onStart -> onRestoreInstanceState -> onResume

다른 액티비티가 위에 뜰 때 / 전원 키로 화면 OFF / 홈 키 누를 때
onPause -> onSaveInstanceState -> onStop

백 키로 액티비티 종료
onPause -> onStop -> onDestroy

백 키로 기존 액티비티에 돌아올 때 / 홈으로 갔다가 다시 돌아올 때
onRestart -> onStart -> onResume

다이얼로그나 투명 액티비티가 위에 뜰 때
onPause

추가로 또 다른 케이스에서의 생명주기 메서드

  • onCreate()에서 finish()를 호출하면 다른 생명주기 메서드를 거치지 않고 곧바로 onDestroy()를 실행한다.
  • onActivityResult()는 onResume() 보다 먼저 실행된다.
  • onSaveInstanceState()는 API 레벨 11 이전에는 onPause() 이전, API 11 이후로는 onStop() 이전에 호출된다.
  • 저장된 State를 복구할 때 onCreate()에서도 가능하지만, 대칭을 위해 onRestoreInstanceState()에서 작업하는 경우가 많다. onRestoreInstanceState()는 onStart() 이후에 호출된다.

2. StartActivity & StartActivityForResult

StartActivity는 Context의 메서드이기 때문에 다른 컴포넌트(Activity, Service, BroadcaseReceiver, Application) 혹은 Context를 넘겨받은 곳이라면 어디서든 사용할 수 있는 반면에 StartActivityForResult는 Activity의 메서드이기 때문에 액티비티에서만 실행할 수 있다. (물론 Activity 참조를 가진 곳에서도 사용할 수 있지만, 액티비티끼리만 데이터를 주고 받을 수 있다고 이해하는 것이 좋겠다.)

Intent.FLAG_ACTIVITY_FORWARD_RESULT 플래그
만약 액티비티A에서 액티비티B를 startActivityForResult()를 통해 실행한 뒤, 액티비티B가 종료하면서 또 다른 액티비티C를 호출한다면
C에서의 Result가 A로 전달될 수 있을까? Intent.FLAG_ACTIVITY_FORWARD_RESULT 플래그를 사용하면 가능하다.

3. 액티비티 전환 시 생명주기 메서드

액티비티 A에서 B를 시작할 때

  1. A의 onPause() 호출 -> 백그라운드로 이동
  2. B의 onCreate(), onStart(), onResume() 호출 -> 포그라운드로 이동
  3. A의 onSavedInstanceState(), onStop() 호출 -> B가 화면 전체를 덮은 경우에만 호출됨
    이 순서를 꼭 알고 있어야 하는 경우를 예를 들면, A에서 변경된 값을 B에서 사용하고자 할 때 A의 onStop() 메서드에서 변경된 값을 DB 혹은 SharedPreference에 저장하면 B의 onCreate() 에서 정상적으로 불러오지 못한다. 이 경우 A의 onStop()이 아닌 onPause()에서 값을 저장하는 작업을 해주어야 B가 생성될 때 정상적으로 불러와 사용할 수 있다.

액티비티 A에서 B를 시작한 후 B가 닫힐 때

  1. B의 onPause() 호출 -> 백그라운드로 이동
  2. A의 onRestart(), onStart(), onRestoreSavedInstanceState(), onActivityResult(), onResume() 호출 -> 포그라운드로 이동
  3. B의 onStop(), onDestroy() 호출 -> 액티비티 B 종료
    이 경우에도 B에서 변경된 값을 A에서 다시 사용하고자 할 때 B의 onStop()이 아닌 onPause()에서 값을 저장하는 작업을 해주어야 A가 정상적으로 불러올 수 있다.

액티비티 A에서 다이얼로그 테마 액티비티 B를 시작할 때

  1. A의 onPause(), onSavedInstanceState() 호출 -> 백그라운드로 이동
  2. B의 onCreate(), onStart(), onResume() 호출 -> 포그라운드로 이동
    이 상태에서 화면을 회전하면 B의 onSavedInstanceState가 호출되고 재시작 후, A가 재시작된다.

4. Configuration(구성)이 변경되어도 액티비티 재시작하지 않도록 하는 속성

Manifest.xml의 android:configChanges 속성으로 Configuration이 변경되어도 액티비티를 재시작하지 않도록 할 수 있다. android:confiaChanges="orientation|locale"와 같이 설정하면 된다. Configuration 변경 후에 할 작업은 액티비티의 onConfigurationChanged() 메서드를 오버라이드해서 처리하면 된다. onConfigurationChanged()가 불린 이후 화면을 다시 그린다.

/res/value-port/dimens.xml
/res/value-land/dimens.xml
와 같이 가로와 세로에 대한 각각의 dimes 파일에 값을 지정하고, 레이아웃에서는 dimen 리소스를 참조하도록 한다. 아래 코드와 같이onConfiguationChanged()에서는 화면 방향에 맞는 dimen 값을 대입해주어야한다. 변경된 리소스를 사용하여 뷰를 다시 그릴 것이라고 착각하기 쉽지만 그렇지않다. onCreate()에서 호출하는 setContentView()는 내부적으로 LayoutInflater의 inflate() 메서드를 사용하는데 이때 View 생성 시 View의 속성을 먼저 반영한다. 즉, android 네임스페이스에 있는 값(android:layout_height, android:layout_gravity 등)들은 inflate() 호출 시 이미 대입되고, 리소스가 변경되었다고해서 변경된 값으로 다시 대입되지 않는다.

@Override
protected void onConfigurationChanged(Configuration newConfig){
    super.onConfigurationChanged(newConfig);
    ViewGroup.LayoutParams params = leftView.getLayoutParams();
    params.width = getResources().getDimensionPixelSize(R.dimen.left_width);
    leftView.setLayoutParams(params);
}

댓글