액티비티 시리즈 - 1) 생명주기.. 그치만 더 딥한

woga·2022년 4월 24일
1

Android 공부

목록 보기
24/49
post-thumbnail

생명주기

많이들 액티비티의 생명주기하면 공식처럼 알 것이다. 처음 액티비티가 시작하면 onCreate 메서드를 타고 onStart onResume 를 거쳐 실행이 되고 종료되면 또 그에 맞게 플로우를 타리라고. 그치만 이 포스트에서는 다 알법한 이야기보다는 자세한 이야기를 해볼까 한다.

우선순위가 높은 더 높은 앱이 메모리를 확보

우선순위가 더 높은 앱이 메모리가 필요하다면 앱은 언제든지 종료될 수 있다. 그렇게 때문에 onStop, onDestroy 메서드는 반드시 실행된다는 보장이 없다.

시스템에 의한 액티비티 제거

onDestroy 메서드가 불리는 것은 finish()가 호출될 때 만으로 이해하기 쉽다. 근데 시스템에서 액티비티를 제거할때도 이 메서드가 불린다.

심지어 하나의 태스크만 사용할 때는 메모리 사용이 많아지면서 OOM이 발생하지만, 앱에서 여러 태스크를 사용한다면 OOM이 발생하기 전에 메모리 문제 가능성을 줄일 수 있다. 가용 메모리 3/4가 넘을 때 포그라운드의 태스크를 우선시하면서 메모리를 확보하기 위해 백그라운드의 태스크에서 액티비를 종료하는 것이다.

시스템에 의해서도 액티비티는 제거될 수 있기 때문에 태스크의 액티비티 목록이 유지된다고 가정해서는 안된다.

생명주기 메서드 호출 케이스들

  • 시작할 때
    - onCreate -> onStart -> onResume

  • 화면 회전할 때(가로/세로)
    - onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume

  • 다른 액티비티가 위에 뜰 때 or 전원키로 화면 off할 때 or 홈키로 앱 나갈 때
    - onPause -> onStop

  • back 키로 액티비티 종료할 때
    - opPause -> onStop -> onDestroy

  • back 키로 기존 액티비티에 돌아올 때 or 홈키로 나갔다가 돌아올 때
    - onRestart -> onStart -> onResume

  • 다이알로그 테마 액티비티나 투명 액티비티가 위에 뜰 때
    - onPause

액티비티 라이프타임

activity api 문서를 보면 3가지 라이프타임으로 구분한다

  • 전체 라이프타임: onCreate() ~ onDestroy()
  • visible(가시) 라이프타임: onStart() ~ onStop()
  • foreground 라이프타임: onResume() ~ onPause()

여기싀 from ~ tofrom < 라이프타임 < to 이다.

Activity는 onPause() 이전까지가 포그라운드 상태이고, onPause()까지 실행된다면 일부가 보이면서 백그라운드 상태가 된다.

또 하나 혼동하지 말하야할게 있다. setContenView()onStart()에서부터 화면에 보이는 것일까?

아니다. onCreate부터 onResume까지는 하나의 Message 처리이므로, setContentView의 결과 화면은 onResume 이후에 보인다.

onStart()부터 가시 라이프타임이라는 것은 액티비티가 화면에 보이지 않다가 다시 보일 때는 여기부터 실행된다는 의미다. onResume()도 마찬가지이다. 백그라운드에 있으면서 아직 보이는 상태에 있다가 포그라운드로 오면 onResume()부터 실행된다

추가로 생명주기 시점

  • onCreate()에서 finish()를 호출하면 다른 생명주기 메서드를 거치지 않고 곧바로 onDestroy()를 실행한다.

  • onActivityResult()는 onResume()보다 먼저 실행된다. onActivityResult()에서 가져온 결과를 업데이트하고 onResume()에서도 최신 데이터를 업데이트해야 할 때, 이 실행ㅇ 순서를 주의하자.

onPostCreate()나 onPostResume() 같은 메서드는 앱에서 권장하지 않음

시스템에서 초기화를 위해서 사용하는 것이므로 앱에서 쓰는 것은 권장하지 않는다.

액티비티를 시작하는 메서드

액티비티 A->B 로 전환한다고 하면 A는 호출자, B는 피호출자라 하자

액티비티를 시작하는 방법은 startActivity()startActivityForResult() 메서드를 호출하는 것이다.

그러나 AndroidX Activity와 Fragment에서는startActivityForResult()는 메모리 부족으로 인해 프로세스와 액티비티가 없어질 수 있기 때문에 deprecated되고 콜백함수로 바뀌었다.

출처 : 안드로이드 공식 문서

startActivity()는 Context의 메서드이기 때문에 Activity 뿐만 아니라 Service, BroadcastReceiver, Application 어디서든 startActivity()를 실행할 수 있다. 또한, startActivity()는 피호출자에 데이터를 전달하기만 한다. 이를 통해 시작된 피호출자에서 getCallingActivity()getCallingPackage() 메서드는 null을 리턴한다.

deprecated 됐지만 startActivityForResult()는 Activity의 메서드이다. 액티비티끼리만 데이터를 주고 받을 수 있다.

동일한 태스크에 있을 때만 유효

호출자와 피호출자가 다른 태스크에 속해있다면 onActivityResult()에서 결과를 다시 받을 수 없다. -> 그치만 현재는 콜백함수로 부르기 때문에 다를 수 있음

Intent.FLAG_ACTIVITY_FORWARD_RESULT 플래그

Activity A -> Activity B -> Activity C 인 상황에서 C에서 A로 결과를 전달하고 싶다면 어떻게 해야할까?

Intent.FLAG_ACTIVITY_FORWARD_RESULT 플래그를 추가하면 된다. 근데 이것도 잘 사용해야 데이터가 전달이 된다. 아래의 케이스들을 살펴보자

1) 실패 케이스1 : RuntimeException

Activity A startActivityForResult() -> Activity B startActivityForResult() -> Activity C setResult

2) 실패 케이스2 : 데이터가 정상적으로 전달되지 않음

Activity A startActivityForResult() -> Activity B intent add flags, startActivityForResult() -> Activity C setResult

3) 성공

Activity A startActivityForResult() -> Activity B intent add flags, startActivity() -> Activity C setResult

액티비티에서 다른 액티비티를 시작할 때

Activity A -> Activity B인 상황일 때, A는 onPause()와 onStop()을 실행하고 B는 onCreate()부터 onResume()을 실행한다.

  1. A는 onPause() 메서드를 실행 (백그라운드 이동)
  2. B는 onCreate(), onStart(), onResume() 메서드를 실행하고 포커스를 갖는다 (포그라운드 이동)
  3. A는 onStop()을 실행, 그러나 B가 투명하거나 화면을 일부만 덮는 경우에는 onStop을 실행하지 않는다.

피호출자가 뜨고 난 이후에야 onStop()이 필요한지 알 수 있기 때문에 해당 순서를 갖는다.

포그라운드 액티비티가 닫힐 때

이 경우에는 위의 경우와 반대다. 이 때의 생명주기는 역순이다.

  1. B는 onPause() 메서드를 실행 (백그라운드 이동)
  2. A는 onReStart(), onStart(), onResume() 메서드를 실행 (포그라운드 이동)
  3. B는 onStop(), onDestroy() 실해 (종료)

super.onXxx() 호출 순서

onCreate(), onStart(), onResume()는 super 함수를 먼저 실행한다.

onPause(), onStop(), onDestroy()는 super 함수를 나중에 실행한다.

생명주기를 시작할 때는 뭔가를 만들어내는 일이 많고, 끝날 때는 정리하는 일이 많다는 것을 생각하자.

finish() 메서드를 호출하고 바로 리턴 필요

finish() 메서드는 리턴을 대신한 것도 아니고 리턴을 포함한 것도 아닌, 종료하라고 Message를 보내는 함수다.

그러므로 이 메서드 호출 위치가 끝일 때만 리턴이 필요 없는 것이지 메서드 중간이라면 리턴은 반드시 필요하다.

여기까지가 액티비티 생명주기에 대한 딥한 이야기의 끝이다. 자세한건 아래 레퍼런스로 연결한 책의 챕터 5를 읽으면 더 이해하기 쉬울 것이다! 책 읽고 정리한 포스트와 다름이 없기 때문이다.

super 함수와 메서드 이름이 같아서 읽는 사람에게 혼동을 주지 말자 등.. 당연한 이야기나 굳이 자세하게 쓸 필요 없는 내용들은 쳐내고 핵심만 넣었기 때문에 책을 사서 읽는 것을 추천한다!

Reference

  • 안드로이드 프로그래밍 Next Step
profile
와니와니와니와니 당근당근

0개의 댓글