본문 바로가기

어플리케이션 구성/액티비티(Activity)

액티비티의 생애주기(Lifecycle)

안드로이드는 모바일 기기에서 구동되는 것을 목적으로 하고 있기에 PC에서 구동되는 프로그램들에 비해 더 효율적인 메모리 관리가 필요합니다. 

안드로이드 어플리케이션을 구성하는 구성요소 중 하나인 액티비티(Activity)도 효율적인 메모리 관리를 위해 액티비티가 생성되고 소멸되는 조건인 생애주기(Lifecycle)을 가지고 있습니다. 액티비티 뿐만 아니라 브로드캐스트 리시버(Broadcast Receiver), 서비스(Service)도 일정한 생애주기를 갖고 있지만, 가장 자주 쓰이는 구성요소인 액티비티의 생애주기부터 알아보도록 하겠습니다.

액티비티의 상태는 크게 활성(Active), 일시정지(Paused), 정지(Stopped) 이 3가지로 나눌 수 있으며, 각 상태에 해당하는 생애주기들이 존재합니다. 우선, 액티비티의 상태에 대해 알아보도록 하겠습니다.

  • 활성(Active)
    현재 화면에 액티비티가 표시되는 상태이며, 사용자와 상호작용(버튼을 누르거나 글자를 입력하는 등..)을 할 수 있는 상태를 말합니다. 일반적으로 액티비티가 화면에 표시되고 있을 때 액티비티는 활성 상태입니다.

    액티비티 내의 EditText 위젯에 텍스트를 입력하는 등, 사용자와 상호작용이 가능합니다.



  • 일시정지(Paused)
    화면에서 액티비티가 보이지만, 사용자와 상호작용은 할 수 없는 상태입니다. 즉, 배경이 투명한 액티비티나 화면 전체를 가리지 않는 다른 액티비티에 의해 액티비티의 일부가 가려진 경우가 해당됩니다. 다이얼로그 또한 액티비티의 일부를 가리지만, 다이얼로는 액티비티의 일부이기 때문에 이 때에는 일시정지 상태로 바뀌지 않습니다.

    다이얼로그가 화면을 가린 경우에는 Paused 상태가 되지 않습니다.

    배경이 투명한 액티비티가 화면을 가린 경우 가려진 액티비티는 Paused 상태가 됩니다.

     
  • 정지(Stopped)
    다른 액티비티에 의해 완전히 가려진 상태입니다. 액티비티 A가 액티비티 B를 호출해서 액티비티 B가 화면에 표시된 경우, 액티비티 A의 모습은 화면에서 더이상 보이지 않게 되겠지요? 따라서 액티비티 A는 정지 상태가 됩니다.


액티비티의 상태는 위와 같이 크게 3가지로 나뉘지만, 안을 살펴보면 아래와 같이 7개의 상태로 나눠지게 됩니다. 아래의 메소드들이 호출됨에 따라 액티비티의 상태들이 바뀌게 되며, 액티비티를 구현할 때 해당 메소드를 오버라이드한 후 각 상태에 따라 처리해줄 작업을 지정해주면 됩니다.

  • void onCreate(Bundle savedInstanceState)
  • void onStart()
  • void onRestart()
  • void onResume()
  • void onPause()
  • void onStop()
  • void onDestroy()

액티비티 생애주기(Activity Lifecycle)



위 메소드들의 이름을 통해 onCreate, onStart.. 등 이 메소드들이 호출되는 조건들을 어렴풋이 짐작할 수 있습니다. 하지만 어렴풋이 아는 것 만으로는 안되겠죠?

액티비티는 액티비티의 생애주기에 따라 상태가 변하게 되는데, 액티비티의 상태를 변화시키기 위해 각 상태에 해당하는 메소드가 호출됩니다. 따라서, 어플리케이션을 작성할 때 액티비티의 생애주기에 따라 알맞는 작업을 처리해주기 위해 각 상태에 해당하는 메소드를 오버라이드하여 처리할 작업을 넣어주게 됩니다. 

예를 들면, 데이터베이스를 사용하는 어플리케이션에서는 메인 액티비티가 실행될 때 데이터베이스를 열고 사용할 준비를 해야 합니다. 그리고, 데이터베이스를 사용하는 액티비티가 소멸될 때에는 사용하던 데이터베이스를 닫아줘야 합니다. 만약 이러한 작업을 적절히 처리해주지 않으면 데이터베이스를 닫아주지 않아 에러가 발생하는 등 여러가지 문제가 생길 것입니다.

그럼, 액티비티의 생애주기 내에서 액티비티의 상태가 어떻게 변하는지, 어떤 메소드가 호출되면 액티비티의 상태가 변하게 되는지 알아보도록 합시다.


액티비티의 시작


액티비티를 처음 시작하려면, 액티비티를 만들어 줘야겠지요? 객체를 사용하려면 객체의 생성자를 이용하여 새로운 인스턴스를 만들어주듯이, 액티비티 또한 이러한 과정이 필요합니다.

액티비티의 생성은 onCreate(Bundle savedInstanceState) 메소드가 호출을 통해 이루어집니다. 액티비티가 생성되니까 onCreate()메소드가 호출되는 것은 알겠는데, 인자로 받는 savedInstanceState는 뭔지 잘 모르겠다구요? - 요놈은 잠시 후에 설명할테니, 궁금해도 조금만 참아주세요. ^^;

onCreate() 메소드에서는 액티비티의 "초기화"를 담당하는 코드들을 처리해줍니다. 액티비티에서는 기본적으로 각종 버튼이나 리스트, 체크박스, 입력 표시줄(EditText) 위젯들이 배치되어 있는 레이아웃을 구성하므로, 그러한 위젯들이 사용자와 상호작용을 할 수 있도록 해주는 코드들을 담고 있어야 합니다. 예를 들면, 버튼을 누르면 다른 액티비티가 뜨게 하거나, 아니면 입력 표시줄(EditText)에서 받은 데이터를 처리하는 것 등이 될 수 있겠죠.

onCreate() 메소드를 통해 액티비티를 시작하기 위한 준비를 모두 끝마쳤다면, 액티비티를 시작해야겠지요? 액티비티의 시작은 onStart() 메소드를 통해 이루어집니다. 하지만, 이 상태는 완전히 액티비티가 시작된 상태가 아니라 액티비티가 화면에 표시되기 "직전"의 상태입니다. 이어서 onResume()메소드가 호출되면 액티비티가 화면에 표시되고, 사용자와 상호작용을 하기 직전의 상태가 됩니다. 액티비티가 생성된 후 바로 액티비티를 실행하면 간단할 것 같은데, 왜 굳이 복잡하게 onStart()onResume()으로 나누었을까요? 


onStart()와 onResume()의 비교

 
액티비티 생애주기 그림을 다시 보도록 하죠. onStart()onResume()의 위치가 다른 것을 볼 수 있습니다.

onStart()는 액티비티가 실행되다가 다른 액티비티가 화면에 표시되어 정지 상태가 되면서 onStop()이 호출되었다가, 메모리에서 사라지기 전에 다시 정지되었던 액티비티가 호출되어 onRestart()를 거쳐 오거나, 혹은 메모리가 부족하여 프로세스가 종료되었다가 다시 액티비티를 생성하고, (onCreate() 호출) 액티비티를 화면에 표시하면서 onStart()를 호출하게 됩니다. 즉, onStart()에서는 정지(Stopped) 상태에서 액티비티가 복귀될 때 호출된다고 할 수 있습니다. 이는 한마디로 액티비티가 화면에서 보이지 않다가 다시 나타나는 것을 의미합니다.


반면, onResume()은 액티비티가 일시정지(Paused) 상태에서 복귀될 때 호출됩니다. 즉, 이는 액티비티가 사용자와 상호작용을 하지 못하다가 다시 상호작용을 할 수 있게 되는 것을 의미합니다.

이렇게, 액티비티가 대기 상태에 있다가 복귀되는 시점 및 복귀되는 기능이 다르기 때문에 onStart()onResume()처럼 시작 과정이 복잡하게 나누어져 있는 것입니다.

액티비티의 정지/일시정지


액티비티가 화면에 표시되고, 사용자와 상호작용을 할 수 있는 활성(Active)상태에서 다른 액티비티에 의해 가려지거나, 화면에서 사라지게 되면 그 액티비티는 정지(Stopped) 상태가 됩니다. onStop()메소드가 호출되면 해당 액티비티는 정지 상태가며, 이곳에서부터 액티비티의 운명은 갈리게 됩니다.




일반적으로 정지된 액티비티는 사용자가 다시 사용할 것을 대비하여 메모리서 바로 사라지지 않고 올라가 있다가 메모리가 부족하게 되면 메모리 확보를 위해 강제로 종료되게 되죠. 그 후, 사용자가 다시 그 액티비티를 실행시키게 되면 다시 액티비티를 생성한 후(onCreate() ) 액티비티를 실행시키게 됩니다. 반면에, 메모리 부족 등이 발생하지 않아 액티비티가 대기 상태로 메모리에 올려져 있을 때 다시 그 액티비티를 호출하게 되면 액티비티를 다시 만들 필요 없이 다시 화면에 표시하면(onRestart()) 되므로, 액티비티를 다시 시작하기만 하면 됩니다.

액티비티가 일시정지(Paused)되는 것은 액티비티가 화면에는 표시되고 있지만, 사용자와 상호작용을 하지 않아서입니다. 일시정지 상태에는 이것 외에도 중요한 것이 하나 더 있습니다. 위의 생애주기에서도 확인할 수 있듯이, 일시정지 상태에서도 메모리가 부족하면 액티비티가 종료될 수 있습니다. 

일시정지 상태에서는 대부분 사용자가 어떤 내용을 입력하고 있는 등, "어떠한 작업이 진행 중"일 가능성이 높습니다. 그런데, 메모리가 부족해 액티비티를 종료시키게 된다면 사용자가 입력했던 데이터들 (예:작성중이던 메시지 등..)이 손실됩니다.

이를 방지하기 위해 메모리 부족 등으로 "어쩔 수 없이" 액티비티를 종료해야 하는 경우 onSaveInsstanceState() 메소드가 호출되며 액티비티의 상태를 Bundle 형태로 저장하게 됩니다. 따라서, onSavedInstanceState() 메소드는 항상 호출되지 않으며, 만약 호출된다면 프로세스가 종료되기 전, onPause() 이후 혹은 onStop() 이후에 호출될 수 있습니다. 

onSaveInstanceState() 메소드를 통해 저장된 상태는 Bundle 객체 형태로 저장되어 있다가, 나중에 해당 액티비티를 다시 실행할 때 onCreate()메소드 및 onRestoreInstanceState()메소드에서 액티비티가 강제로 종료될 때의 상태로 액티비티를 복원하게 됩니다. 

onCreate() 메소드와 onRestoreInstanceState() 모두 Bundle 객체를 인자로 받고 있으며, 실제로 액티비티가 강제 종료될 때 저장된 상태정보가 담겨있는 Bundle 객체의 정보를 동일하게 전달받게 됩니다. 따라서 액티비티 상태의 복구는 두 메소드 모두에서 처리해줄 수 있으며, 상황에 따라 적합한 메소드에서 상태를 복원해주는 코드를 넣어주면 됩니다.
onRestoreInstanceState() 메소드 역시 항상 호출되는 메소드는 아니며, 호출된다면  onCreate() 메소드 이후에 호출되게 됩니다.

즉, 위의 두 메소드는 액티비티가 "강제로" 종료되었을 때 혹은 그 후 액티비티를 다시 실행시킬 때만 실행되므로, 만약 액티비티가 시작하고나 종료될 때 해 주어야 할 작업이 있다면 저 두 메소드를 오버라이드하는 대신 onCreate(), onStop() 및 onDestroy()메소드를 오버라이드한 후 그 안에 코드를 구현해야 안정적으로 해당 작업을 처리하줄 수 있습니다.

아래에 액티비티 생애 주기를 구성하는 주요 메소드의 역할을 간단히 정리해 보았습니다.