API GUIDE/2. App Components
2014. 3. 18. 20:56

저작권 표시 : 

 

Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.

 

현재 보시는 페이지는 안드로이드 오픈 소스 프로젝트에 의해 작성되고 공유된 작업물의 수정/번역본이며 크리에이티브 커먼즈 저작자 표시 2.5 라이센스에 기술된 조건의 사용 근거를 따른 것입니다.

 

본 문서의 원본은 http://developer.android.com/guide/components/activities.html이며 안드로이드 4.4 Kitkat 기준으로 설명되었습니다.





액티비티 라이프사이클 관리하기(Managing the Activity Lifecycle)



액티비티의 라이프사이클을 관리할 때 관련 콜백 메서드를 사용하면 강력하면서도 유연한 어플리케이션을 개발할 수 있다. 액티비티의 라이프사이클은 액티비티와 연계되어 있는 또 다른 액티비티, 태스크, 백스택에 의해 직접적인 영향을 받는다.


액티비티는 기본적으로 다음의 3개 상황에 처할 수 있다.


리줌 상태(Resumed)

이 상태에 있는 액티비티는 화면의 제일 전면에 있으며 사용자의 포커스를 갖고 있다. (이 상황은 종종 "실행 중"이라 일컫어지기도 한다.)


일시 정지 상태(Paused)

다른 액티비티가 화면의 제일 전면에서 포커스를 받고 있지만 해당 이 상태에 있는 액티비티는 여전히 그 일부가 사용자에게 보여지고 있다. 즉, 다른 액티비티의 일부분이 투명하거나 화면 전체를 완전히 메울 정도로 크지 않아서 이 상태에 있는 액티비티의 일부분이 보여지고 있는 상태다. 일시 중지된 액티비티는 완전한 상태로 살아있다.(Activity 객체는 모든 상태와 멤버의 정보 데이터를 보존하며 메모리에 남아 윈도우 매니저에 속하게 된다.) 하지만 메모리가 극도로 부족해지면 시스템에 의해서 강제 종료될 수도 있다.


완전 정지 상태(Stopped)

액티비티가 다른 액티비티로 완전히 가리워진 상태다.(액티비티는 현재 "백그라운드"에 있는 상태다.) 정지된 액티비티는 아직 살아있다. (Activity 객체는 마지막 상태와 멤버들의 정보를 모두 유지한 채 메모리에 남아있다. 하지만 윈도우 매니저에는 속하지 않는다.) 하지만 사용자에게는 더 이상 보여지지 않고 있으며 다른 곳에서 메모리가 필요하게 되면 시스템은 액티비티를 강제종료 시킬 수도 있다.


액티비티가 일시 정지되거나 완전 정지되면 시스템은 액티비티에 종료를 요구하거나 (finish() 메서드를 호출한다.) 그냥 프로세스를 강제 종료시키게 된다. 액티비티가 다시 열리면 (그냥 종료되거나 강제 종료 된 후에) 모든 것을 다시 생성해야 한다.


라이프사이클 콜백함수 구현하기(Implementing the lifecycle callbacks)

액티비티가 위에서 언급한 상태들 중의 하나로 상태가 변경될 때에는 다양한 콜백 메서드를 통해 알림을 받게 된다. 다시 말해, 액티비티의 상태 변화가 일어날 때 적절한 작업을 수행할 수 있는 콜백 메서드가 제공된다. 아래 코드는 액티비티의 기본적인 라이프사이클 메서드에 대한 예제다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class ExampleActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // The activity is being created.
    }
    @Override
    protected void onStart() {
        super.onStart();
        // The activity is about to become visible.
    }
    @Override
    protected void onResume() {
        super.onResume();
        // The activity has become visible (it is now "resumed").
    }
    @Override
    protected void onPause() {
        super.onPause();
        // Another activity is taking focus (this activity is about to be "paused").
    }
    @Override
    protected void onStop() {
        super.onStop();
        // The activity is no longer visible (it is now "stopped")
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // The activity is about to be destroyed.
    }
}
 


Note : 위 예제처럼 라이프사이클 메서드를 구현할 때는 어떤 작업을 수행하기 전에 부모클래스의 메서드를 먼저 호출해야 한다.


이 메서드들은 액티비티의 모든 라이프사이클에 관련되어 있다. 이 메서드들을 구현하게 되면 아래와 같은 액티비티 라이프사이클의 루프를 모니터링 해 볼 수 있다.


액티비티의 전체 라이프타임(entire lifetime)onCreate() 호출 시점 이후부터 onDestroy() 호출 시점 이전까지를 말한다. 액티비티는 onCreate() 메서드에서 (레이아웃 정의와 같은) 모든 준비를 완료해야 하며 onDestroy() 메서드에서는 남아 있는 모든 리소스를 해제시켜야 한다. 만일 액티비티에 네트워크로부터 데이터를 다운로드 받는 쓰레드가 동작하도록 되어 있다고 하면 onCreate()에서 쓰레드를 생성하고 onDestroy()에서 정지시켜야 한다.


액티비티의 비저블 라이프타임(visible lifetime)onStart() 호출 시점 이후부터 onStop() 호출 시점 이전까지를 말한다. 이 시간동안 사용자는 액티비티를 볼 수 있으며 소통할 수 있다. 예를 들어, 새로운 액티비티가 시작되면 onStop()이 호출되고 해당 액티비티는 더 이상 보여지지 않게 된다. onStart()가 호출될 때부터 onStop()이 호출될 때까지 사용자에게 액티비티를 보여주기 위해 필요한 리소스를 유지시킬 수 있다. 예를 들어, UI에 영향을 줄 만한 변화를 감지하기 위해 onStart()에서 BroadcastReceiver를 등록하고 사용자에게 UI를 더 이상 보여줄 필요가 없어지면 onStop()에서 등록을 해지할 수 있다. 사용자가 액티비티를 "보았다 안보았다"를 반복할 수 있기 때문에 액티비티의 전체 라이프타임 동안 onStart()와 onStop()은 여러 번 반복해서 호출될 수도 있다.


액티비티의 포어그라운드 라이프타임(foreground lifetime)onResume() 호출 시점 이후부터 onPause() 호출 시점 이전까지를 말한다. 이 라이프타임에서는 액티비티가 다른 어떤 액티비티보다도 화면의 맨 앞에 위치하게 되며 사용자의 입력을 받을 수 있게 된다. 액티비티는 자주 이 상태에 진입하기도 하고 빠져나가기도 한다. 기기가 슬립모드로 전환되거나 다이얼로그가 앞에 나오는 상황이 되면 액티비티는 이 라이프타임에서 빠져나가게 되며 반대로 슬립모드가 해제되거나 다이얼로그가 사라지면 다시 이 라이프타임으로 진입하게 된다. 이 라이프타임은 다른 라이프타임에 비해 매우 빈번하게 상태 변화가 일어나기 때문에 프로그램이 느려지지 않도록 하려면 onResume()onPause()를 매우 간단한 작업만 하도록 구현해야한다.


[그림 1]은 위의 3가지 루프와 액티비티의 상태변화가 어떤 순서로 일어나는지에 대해서 설명하고 있다.



그림 1. 액티비티의 라이프사이클



아래 [표 1]에서는 위 그림에 표시된 콜백 메서드들에 대한 좀 더 상세한 설명과 각 콜백 메서드가 액티비티의 라이프사이클 루프 중 어디에 속하는지 그리고 호출된 후에 액티비티가 강제 종료될 가능성이 있는지에 대해서 설명한다.


표 1. 액티비티 라이프사이클 콜백 메서드 요약

메서드명

 설명

호출 후

강제종료?

음 메서드

onCreate()

액티비티가 처음 생성되면 호출된다. 이메서드에서 뷰 생성이나 변수의 초기화 같은 셋업작업이 이루어져야 한다. 만일 이전에 실행되었을 때 마지막 상태가 저장되어 있었다면 (뒤에 나오는 액티비티 상태 저장하기(Saving Activity State) 참고) 이 메서드에 액티비티의 이전 상태에 대한 정보를 갖고 있는 Bundle 객체가 파라메터로 넘어온다.

이 메서드 다음에는 항상 onStart()가 실행된다.

아니오

onStart()

 

onRestart()

액티비티가 완전 정지 되었다가 다시 시작되기 바로 전에 호출된다.

이 메서드 다음에는 항상 onStart()가 실행된다.

아니오

onStart()

 

onStart()

액티비티가 사용자에게 보여지기 직전에 호출된다. 액티비티가 전면에 보여질거라면 다음에 onResume()이 호출되며 숨겨질거라면 다음에 onStop()이 호출된다.

아니오

onResum()
또는
onStop()

 

 

onResume()

액티비티가 사용자와 소통을 시작하기 바로 전에 호출된다. 이 시점에서 액티비티는 액티비티 스택의 최정점에 위치하게 되며 사용자의 입력을 받게 된다.

다음에 항상 onPause()가 호출된다.

아니오

onPause()

 

 

onPause()

시스템이 다른 액티비티를 막 재개하려고 할 때 호출된다. 이 메서드는 저장되지 않은 정보를 저장하거나 애니메이션처럼 CPU를 사용하는 작업을 중지할 때 사용된다. 이 메서드가 완료되어야만 다음 액티비티가 재개될 수 있기 때문에 이 메서드에서는 무슨 작업이 되었건 빠르게 수행하고 리턴해야한다.

액티비티가 다시 재개되느냐, 중지되느냐에 따라 다음 실행될 메서드는 onResume()이 될 수도 있고 onStop()이 될 수도 있다.

onResum()

또는
onStop()

 

onStop()

액티비티가 사용자에게 더 이상 보여지지 않게 되면 호출된다. 이 메서드는 액티비티가 파괴될 때나 다른 액티비티에 의해서 완전히 가리워질 때 발생한다.

사용자가 다시 이 액티비티를 불러내게 되면 다음 실행될 메서드는 onRestart()가 호출되며 그대로 액티비티가 종료된다면 onDestroy()가 호출된다.

onRestart()
또는
onDestroy()

onDestroy()

액티비티가 파괴되기 전에 호출된다.

액티비티가 수행할 수 있는 마지막 메서드다. 액티비티가 종료되거나(어디선가 finish()가 호출되거나), 메모리 부족으로 시스템이 액티비티를 강제 종료할 때 호출된다. 이 두가지의 호출상황은 isFinishing() 메서드로 구분이 가능하다.

없음.


"호출 후 강제종료?"라고 된 열은 말 그대로 시스템이 액티비티를 순간 강제 종료시킬 가능성이 있는지를 나타낸다. 3개 메서드가 "예"라고 표시되어 있다(onPause(), onStop(), onDestroy()). onPause()는 액티비티가 생성된 이후 3개의 메서드 중에서 첫번째 메서드이기 때문에 프로세스가 강제 종료될 때에도 수행이 보장되는 마지막 메서드다. 만일 시스템이 긴급하게 메모리를 확보해야 한다면 나머지 2개의 메서드(onStop(), onDestroy())는 반드시 수행된다고 보장하기는 어렵다. 따라서 사용자가 수정했다던가 하는 중요한 데이터는 onPause()에서 저장해 두어야 한다. 그러나 onPause()에서 보관해야하는 데이터에 대해서는 어느 정도 선별이 필요하다. onPause()가 수행을 종료해야만 다음 액티비티로의 상태 전환이 이루어지기 때문에 과도한 작업을 수행하면 사용자 경험이 느려지게 된다.


"강제 종료" 항목이 "아니오"라고 표시된 메서드들은 수행 중 프로세스가 강제 종료 되지 않도록 보호가 된다. 그래서 액티비티는 onPause()가 리턴되고 onResume()이 호출되기 전까지 강제 종료가 가능한 상태가 되며 onPause()가 다시 호출되고 리턴될 때까지 강제 종료의 대상에서 제외된다.


Note : [표 1]에서 강제 종료 여부가 "아니오"라고 표시되어 있다고 하더라도 시스템에 의해서 강제 종료될 수 있다. 더 이상 시스템 자원이 없는 극한의 상태가 된다면 강제 종료될 수도 있다. 액티비티가 강제 종료되는 상황에 대해 더 많은 정보가 필요하다면 Processes and Threading 문서를 참조하기 바란다.


액티비티 상태 저장하기(Saving activity state)

액티비티 라이프사이클 관리하기(Managing the Acitivity Lifecycle)의 소개 부분에서 액티비티가 일시 정지(pause)되거나 완전 정지(stop)될 때 액티비티의 상태가 유지되는 것에 대해 간략히 언급하였다. 액티비티가 일시 정지되거나 완전 정지되어도 Activity 오브젝트는 여전히 메모리에 로드되어 있는 상태이기 때문에 모든 멤버의 정보나 마지막 상태 정보도 모두 유지된다. 그래서 액티비티가 다시 화면의 맨 앞으로 나오게 되면(resume 될 때) 모든 정보가 그대로 남아있게 된다.


그러나 시스템이 메모리를 확보하기 위해 액티비티를 파괴하게 되면 시스템이 다시 액티비티를 복원(resume)시켜 간단히 원래 상태로 돌아올 수가 없다. 대신에 사용자가 다시 그 액티비티로 돌아가려고 하면 시스템은 액티비티 오브젝트를 다시 생성시킨다. 하지만 사용자는 시스템이 액티비티를 파괴하고 재생성한 일은 알지 못하기 때문에 마지막에 보았던 상태 그대로 유지되어 있을 것으로 생각한다. 이런 상황에서는 onSaveInstanceState()라는 또다른 콜백 메서드를 구현하여 액티비티의 중요한 정보를 유지시킬 수 있다.


시스템은 액비티티가 파괴되기 전에 onSaveInstanceState()를 호출한다. 시스템은 이 메서드를 호출할 때 액티비티의 상태 정보를 저장할 수 있도록 Bundle이라는 것을 넘겨준다. BundleputString()putInt()와 같은 메서드(리치크루 : 이 이름의 메서드들 외에 다른 메서드들도 있다.)를 사용하여 이름과 값이 짝을 이루는 형태(name-value pair)로 정보를 저장할 수 있다. 이후에 시스템이 어플리케이션 프로세스를 파괴하고 사용자가 다시 되돌아오는 상황이 발생하면 시스템은 액티비티를 재생성하고 onCreate()onRestoreInstanceState()에 Bundle을 넘겨준다. 이 두 개의 메서드를 사용하면 onSaveInstanceState()에서 저장한 상태 정보를 Bundle에서 추출하여 액티비티의 상태를 복구할 수 있다. 만일 복구해야할 정보가 없다면 Bundlenull값으로 넘어온다(액티비티가 처음 생성될 때 이런 상황이 된다.).


그림 2. 온전한 상태로 사용자 포커스를 다시 얻게되는 두 가지 상황.
(1) 액티비티가 파괴되고 재생성될 때 이전에 저장된 상태를 복구하거나
(2) 액티비티가 정지되었다가 복원(resume)되면 상태가 온전하게 유지된다.


Note : onSaveInstanceState() 메서드는 액티비티가 파괴되기 전에 반드시 호출되는 것은 아니다. 상태 정보를 저장할 필요가 없는 경우도 있기 때문이다. 예를 들어, 사용자가 "뒤로"버튼을 눌러서 액티비티를 종료하는 것은 사용자에게 명백하게 종료 의도가 있는 것이기 때문에 onSaveInstanceState()가 호출되지 않는다. 시스템이 onSaveInstanceState()를 호출하는 시점은 onStop()전이나 onPause()전일 수 있다.


그러나 onSaveInstanceState() 메서드에서 아무런 작업을 하지 않거나 심지어 이 메서드를 구현하지 않아도 Activity 클래스에서 기본적으로 구현해 놓은 onSaveInstanceState()가 일부 액티비티의 상태를 복구해 준다. 구체적으로 설명하자면 레이아웃에 있는 View들이 저장할 필요가 있는 정보를 저장할 수 있도록 각 View의 onSaveInstanceState() 메서드를 호출해 준다. 안드로이드 프레임워크에 있는 거의 대부분의 위젯은 시각적으로 보여지는 내용의 변화는 재생성될 때 다시 복구할 수 있도록 자체적으로 적절한 메서드를 구현하고 있다. 예를 들어, EditText는 사용자가 입력한 텍스트를 저장하며 CheckBox는 체크된 상태를 저장한다. 각 위젯이 이렇게 자동으로 상태를 저장할 수 있도록 하기 위해 해야할 일은 각 위젯에 유니크ID를 부여하는 것이다.(android:id 속성 사용) 위젯이 ID를 갖지 않는다면 시스템은 위젯의 상태를 저장할 수가 없다.


레이아웃에 있는 뷰가 상태를 저장하지 않도록 하려면 android:saveEnabeld 속성을 "false"로 설정하거나 setSaveEnabled() 메서드를 호출하면된다. 일반적으로 액티비티의 상태를 다른 방법으로 복구할 게 아니라면 이 항목을 해제시켜서는 안된다.

이렇게 액티비티의 상태를 자동으로 저장하도록 기본 onSaveInstanceState() 메서드가 구현되어 있긴 하지만 다른 추가적인 정보를 저장하기 위해서 이 메서드를 오버라이드 해야 할 수도 있다. UI외에도 UI와 연결되어 있는 멤버 변수들은 자동으로 저장되고 복구되지 않기 때문에 직접 onSaveInstanceState() 메서드를 구현해 줘야 한다.


기본적으로 구현되어 있는 onSaveInstanceState() 메서드는 UI의 상태를 저장해주고 있기 때문에 추가정보를 저장하기 위해서 메서드를 오버라이딩하려면 다른 작업을 하기 전에 항상 부모클래스의 onSaveInstanceState()를 먼저 호출해야 한다. 같은 맥락으로 onRestoreInstanceState()를 구현할 때도 부모클래스의 메서드를 먼저 호출해야만 UI의 상태가 복구된다.


Note : onSaveInstanceState() 는 반드시 호출된다고 보장할 수 없기 때문에 액티비티의 일시적 데이터(UI의 상태)를 저장할 때만 사용해야 하며 영구적으로 저장해야 할 때는 사용해선 안된다. 대신 사용자가 액티비티를 떠날 때에 (데이터베이스에 저장하는 것과 같이) 영구적으로 보관해야 하는 데이터는 onPause()에서 저장해야만 한다.


여러분이 만든 앱이 상태를 얼마나 잘 복구하는지 확인해 보려면 그냥 기기를 돌려서 화면이 회전되도록 해보면 된다. 화면의 방향이 바뀌면(가로방향에서 세로방향으로 또는 세로방향에서 가로방향으로) 시스템은 다른 방향의 레이아웃에 맞는 대체 자원을 적용시키기 위해서 액티비티를 파괴하고 새로 생성한다. 사용자들은 앱을 사용하면서 화면을 회전시키는 경우가 빈번하기 때문에 이런 상황 하나만으로도 액티비티가 파괴되고 재생성될 때 UI의 상태를 저장하고 복구하는게 얼마나 중요한지 알 수 있다.


설정 변화 핸들링하기(Handling configuration changes)

기기의 설정 일부는 실행 도중에 변경될 수도 있다(화면 방향, 키보드 설정, 언어 등과 같은 설정들). 이런 것들이 변경되면 안드로이드는 실행 중인 액티비티를 재생성하게 된다(시스템은 onDestroy()를 호출하고 바로 onCreate()를 호출한다.). 이렇게 하는 이유는 프로그램 작성자가 제공한 리소스들을 변경된 설정에 맞게 자동으로 다시 적용시키기 위해서다(예를 들어, 가로화면과 세로화면에 맞는 레이아웃을 따로 만들어 두었다면 사용자가 기기를 회전시켰을 때 그에 맞는 레이아웃을 다시 적용시키는 것이다.).


만일 위헤서 설명한 대로 화면의 방향이 바뀌고 그에 따라 액티비티의 상태가 복구될 수 있도록 프로그램을 작성했다면 그 앱은 액티비티의 라이프사이클 도중에 예상치 못한 상황에도 좀 더 유연해지게 된다.


이렇게 재시작되는 상황을 잘 대처하기 위해서는 앞서 설명한대로 onSaveInstanceState()에서 액티비티의 상태를 저장하고 onRestoreInstanceState()(또는 onCreate())에서 복구되도록 하는 것이 최선의 방법이다.


실행 중에 발생하는 설정 변경과 그에 대한 처리 방법에 대해 좀 더 알고 싶다면 Handling Runtime Changes를 참고하기 바란다.


액티비티간 콜백 메서드의 수행 순서(Coordinating activities)

어떤 액티비티가 다른 액티비티를 구동시키면 두 액티비티는 모두 라이프사이클의 상태 변화가 생긴다. 처음 액티비티는 일시정지(pause)된 후에 완전정지(stop)되며 (물론 액티비티가 뒤쪽에서 조금이라도 보이게 되면 안전정지(stop)되지는 않는다.) 반면에 다른 액티비티는 생성(create)이 된다. 이런 경우 이 두 개의 액티비티가 디스크나 다른 곳에 데이터를 저장하여 서로 정보를 공유하는 방식을 사용한다면, 두 번째 액티비티가 생성되기 전에 첫번째 액티비티는 완전하게 정지되지 않는다는 사실을 반드시 알고 있어야 한다. 좀 더 정확히 얘기하면 두번째 액티비티를 생성하는 프로세스와 첫번째 액티비티를 정지하는 프로세스가 겹쳐져서 수행된다는 말이다.


라이프사이클 콜백 메서드의 순서, 특히 같은 프로세스에서 하나의 액티비티가 다른 액티비티를 구동할 때의 순서는 명확하게 정해져 있다. 다음은 액티비티A가 액티비티B를 구동시킬 때의 동작 순서를 나열한 것이다.


  1. 액티비티A의 onPause() 메서드가 실행된다.
  2. 액티비티B의 onCreate(), onStart(), onResume()이 순서대로 실행된다.(액티비티B가 사용자 포커스를 받게 된다.)
  3. 이제 액티비티A가 화면에서 더 이상 보이지 않게 되면 onStop() 메서드가 실행된다.


이러한 라이프사이클 콜백 메서드의 순서를 참고하면 하나의 액티비티에서 다른 액티비티로 정보가 옮겨지는 것을 문제없이 수행할 수 있다. 예를 들어, 다음 액티비티에서 읽어내야 할 데이터를 첫번째 액티비티가 데이터베이스에 써 놓아야 한다면 onStop() 대신에 onPause()에서 데이터베이스 쓰기 작업을 수행해야만 한다.

posted by 리치크루