: 해당 포스트는 액티비티 생명주기에 대한 기본적인 내용을 안다는 가정 하에 내용을 진행한다.

: 다른 액티비티가 일부만 가리면 onPause()가 호출되고 전체를 가리면 onStop()이 호출된다. 디바이스의 메모리가 부족해 우선순위가 낮은 앱을 종료해야 한다면 앱은 onPause에서도 종료될 수 있다. 앱이 종료된다고 무조건 onStop()이나 onDestroy()가 호출된다는 보장이 없다. 그래서 주로 onPause() 메서드를 주로 오버라이드하지만 리소스를 안전하게 제거하기 위한 안전장치로 onStop()과 onDestroy()를 호출하는 것도 괜찮다. onDestroy() 메서드는 finish()를 호출할 때 말고 시스템에 의해서 제거될 때도 호출된다. 앱에서 하나의 태스크를 사용하면 메모리가 많아질 때 OutOfMemoryError가 발생하지만 여러 태스크를 사용한다면 가용 메모리의 3/4가 넘을 때 백그라운드 태스크의 액티비티를 제거한다. 시스템에 의해서 태스크의 액티비티 목록이 제거될 수 있으므로 액티비티 개수나 액티비티 목록을 메모리에 유지하는 방식은 사용 하지 않는 게 좋다. 주의해야할 생명주기 호출 시점이 있다. 먼저 onCreate()에서 finish 메서드가 호출되면 바로 onDestroy가 호출된다. 두 번째로 onActivityResult() 메서드는 onResume()보다 먼저 실행된다. 따라서 순서에 유의해 UI를 업데이트 해야 한다.


: 각 생명주기에서 어떤 처리를 하면 좋은 지 간단히 알아보자. 

* onCreate : 초기화 처리, 뷰 생성 등

* onStart : 통신이나 센서 처리 시작

* onRestart : 보통 아무 작업 안 함.

* onResume : 필요한 애니메이션 실행 등의 화면 갱신 처리

* onPause : 애니메이션 등 화면 갱신 처리 정지 또는 일시 정지할 때 필요 없는 리소스 해체하거나 필요한 데이터 영속화

* onStop : 통신이나 센서 처리 정지

* onDestroy : 필요 없는 리소스 해제, 액티비티 참조 정리


- 생명주기 메서드 호출 순서

1. 시작할 때 : onCreate -> onStart -> onResume

2. 화면 회전할 때 : onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume

3. 다른 액티비티가 위에 뜰 때/전원 키로 화면 OFF할 때/홈 키 : onPause -> onStop

4. 백 키로 액티비티 종료 : onPause -> onStop -> onDestory

5. 백 키로 기존 액티비티에 돌아올 때/홈 키로 나갔다가 돌아올 때 : onRestart -> onStart -> onResume

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


- 액티비티 라이프타임

* 전체 라이프타임 : onCreate() ~ onDestroy()

* 가시(visible) 라이프타임 : onStart() <  < onStop()

* 포그라운드 라이프타임 : onResume() < < onPause()

: 액티비티는 onPuase 이전까지 포그라운드이고 onPause()까지 실행된다면 백그라운드 상태가 된다. 또한 onPause()까지 화면 일부가 가시되고 onStop()까지 실행되면 더 이상 액티비티가 보이지 않는다. onCreate()에서 setContentView()를 호출하지만 onCreate()에서 onResume()까지는 하나의 메시지로 처리된다. 따라서 onResume() 이후에 액티비티가 가시된다. 


- startactivity(), startActivityForResult()

: A에서 B 액티비티로 위 두 메서드를 사용해 전환했다면 A는 호출자이고 B는 피호출자이다. 피호출자에서 호출자 정보에 접근할 때는 getCallingActivity()와 getCallingPackage() 메서드를 사용하면 된다. 호출자와 피호출자는 같은 태스크에 있어야 한다. 다른 태스크에 있으면 전환이 안 된다. 피호출자에서는 결과를 호출자에게 전달할 때 setResult를 호출하는 데 finish 메서드 전에 호출해야 한다. ActivityA에서 ActivityB를 시작하고 ActivityB에서 ActivityC를 시작한 후 ActivityC에서 setResult로 전달한 데이터를 ActivityA로 전달하려면 어떻게 해야 할까? 다음 코드처럼 하면 된다.

//ActivityA
startActivityForResult(new Intent(this, ActivityB.class), REQUEST_CODE);

//ActivityB
Intent intent = new Intent(this, ActivityC.class);
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
startActivity(intent);
finish();

//ActivityC
Intent intent = new Intent();
intent.putExtra("value","Android");
setResult(RESULT_OK, intent);
finish();

위 코드를 보자. 일단 ActivityB에서 startActivity()를 호출했다. startActivityForResult()를 호출하면 예외가 발생한다. 또한 Intent.FLAG_ACTIVITY_FORWARD_RESULT를 setFlag 해야 한다. 그러면 A->B->C->A로 액티비티를 전환할 수 있다.


- 액티비티 전환 시 생명주기 메서드 호출

* 액티비티에서 다른 액티비티 시작할 때 : 먼저 ActivityA는 onPause 메서드를 호출해 백그라운드 상태가 된다. 그 다음 ActivityB가 onCreate(), onStart(), onResume()을 호출하고 포그라운드 상태가 된다. ActivityB가 전체 화면을 덮어서 ActivityA가 보이지 않으면 ActivityA는 onStop()을 호출하고 ActivityB가 투명하거나 일부만 덮으면 onStop을 실행하지 않는다. 이 순서가 중요한 이유는 ActivityA에서 저장한 값을 ActivityB이 onCreate()에서 사용할 때 onStop()이 아닌 onPause()에서 값을 저장해야 정상적으로 동작하기 때문이다.

* 포그라운드 액티비티가 닫힐 때(위와 반대 상황) : ActivityB는 onPause() 메서드를 호출해 백그라운드 상태가 된다. ActivityA는 onRestart(), onStart(), onResume() 메서드를 실행해 포그라운 상태가 된다. ActivityB는 onStop(), onDestroy() 메서드를 호출해 종료된다.


- 생명주기 메서드 사용에 대한 유의점

* 리소스 생성과 소멸은 대칭이 되는 생명주기 메서드에서 실행해야 한다. onCreate()에서 DB를 열었다면 onDestroy()에서 닫아야 한다. onCreate-onDestory, onResume-onPause, onStart-onStop 이 서로 대칭이 된다. 만약 onCreate에서 DB를 열고 onPause에서 DB를 닫으면 오류가 발생할 수 있다. 만약 A 액티비티에서 B액티비티로 전환하고 back 키를 눌러 A 액티비티로 돌아왔을 때 A 액티비티에서는 onPause 호출로 DB가 닫혔지만 다시 돌아왔을 때 onCreate 호출이 되지 않아 DB가 안 열린다. 이와 같은 상황이 올 수 있기에 대칭으로 생명주기를 사용하는 게 좋다.

* 생명주기의 super 메서드를 호출할 때 주의해야한다. onCreate(), onStart(), onResume()메서드는 super 클래스의 해당 함수를 호출하고 작업해야 한다. onPause(), onStop(), onDestroy()는 작업을 하고 super 클래스의 해당 함수를 호출해야 한다. 추가적으로 생명주기 콜백 메서드들을 직접 호출하는 것은 추천하지 않는다. 호출을 해야 한다면 별도의 메서드를 정의해서 해당 메서드를 호출하는 걸 권장한다. 

* finish 메서드를 호출한 다음에는 바로 리턴해야 한다. finish 메서드 내에는 return을 포함하지 않는다. 단지 finish 처리 메시지를 보낼 뿐이다. 따라서 finish 호출 후 return 하지 않으면 finish가 호출된 메서드의 나머지 부분이 실행된다. 이는 불필요할 뿐만 아니라 오류를 발생시킬 수 있다. 그래서 메서드의 마지막 부분에서 finish를 호출하는 것은 굳이 return 해줄 필요 없다. 




+ Recent posts