Save the date! Android Dev Summit is coming to Sunnyvale, CA on Oct 23-24, 2019.

액티비티

Activity는 일종의 애플리케이션 구성 요소로서, 사용자가 전화 걸기, 사진 찍기, 이메일 보내기 또는 지도 보기 등의 일을 하기 위해 상호작용할 수 있는 화면을 제공합 니다. 액티비티마다 창이 하나씩 주어져 이곳에 사용자 인터페이스를 끌어올 수 있습니다. 이 창은 일반적으로 화면을 가득 채우지만, 작은 창으로 만들어 다른 창 위에 띄울 수도 있습니다.

하나의 애플리케이션은 보통 여러 개의 액티비티가 느슨하게 서로 묶여 있는 형태로 구성됩니다. 통상 한 애플리케이션 내에서 하나의 액티비티가 "주요" 액티비티로 지정되며, 사용자가 이 애플리케이션을 처음 실행할 때 이 액티비티가 사용자에게 표시됩니다. 그런 후 각각의 액티비티는 여러 가지 작업을 수행하기 위해 또 다른 액티비티를 시작할 수 있습니다. 새로운 액티비티가 시작될 때마다 이전 액티비티는 중단되지만 시스템은 해당 액티비티를 스택("백 스택")에 보존합니다. 새로운 액티비티가 시작되면, 백 스택으로 푸시되고 사용자 포커스를 갖게 됩니다. 백 스택은 기본적인 "후입선출" 방식을 지키므로, 사용자가 현재 액티비티를 끝내고 Back 버튼을 누르면 해당 액티비티가 스택에서 팝되고(소멸되고) 이전 액티비티가 재개됩니다. (백 스택은 작업 및 백 스택 문서에서 상세하게 논의합니다).

한 액티비티가 새로운 액티비티의 시작으로 인해 중단된 경우, 이 상태 변경은 액티비티의 수명 주기 콜백 메서드를 통해 알려집니다. 액티비티가 시스템 액티비티를 생성, 중단, 재시작, 제거하는 등의 상태 변화로 인해 받을 수 있는 콜백 메서드는 여러 가지가 있습니다. 각 콜백은 상태 변화에 알맞은 특정 작업을 수행할 기회를 제공합니다. 예를 들어 액티비티가 중단되면 네트워크 또는 데이터베이스 연결과 같이 큰 객체를 모두 놓아주어야 합니다. 액티비티가 재개되면, 필요한 리소스를 다시 획득하여 중단된 작업을 재개할 수 있습니다. 이러한 상태 전환은 모두 액티비티 수명 주기의 일부입니다.

이 문서의 남은 부분에서는 다양한 액티비티 상태 사이의 전환을 적절히 관리할 수 있도록 액티비티 수명 주기가 작동하는 방식을 자세히 논하는 등, 액티비티를 구축하고 사용하는 기본적 방법을 설명합니다.

액티비티 생성

액티비티를 생성하려면 Activity의 서브클래스(또는 이의 기존 서브클래스)를 생성해야 합니다. 서브클래스에서는 액티비티 생성, 중단, 재개, 소멸 시기 등과 같은 수명 주기의 다양한 상태 간 액티비티가 전환될 때 시스템이 호출하는 콜백 메서드를 구현해야 합니다. 가장 중요한 두 가지 콜백 메서드는 다음과 같습니다.

onCreate()
이 메서드는 반드시 구현해야 합니다. 시스템은 액티비티를 생성할 때 이것을 호출합니다. 구현하는 중에 액티비티의 필수 구성 요소를 초기화해야 합니다. 무엇보다도 중요한 점은, 바로 여기서 setContentView()를 호출해야 액티비티의 사용자 인터페이스 레이아웃을 정의할 수 있다는 점입니다.
onPause()
시스템이 이 메서드를 호출하는 것은 사용자가 액티비티를 떠난다는 첫 번째 신호입니다(다만 이것이 항상 액티비티가 소멸 중이라는 뜻은 아닙니다). 현재 사용자 세션을 넘어서 지속되어야 하는 변경 사항을 커밋하려면 보통 이곳에서 해야 합니다(사용자가 돌아오지 않을 수 있기 때문입니다).

여러 액티비티 사이에서 원활한 사용자 환경을 제공하고, 액티비티 중단이나 심지어 소멸을 초래할 수도 있는 예상치 못한 간섭을 처리하기 위해 사용해야 하는 다른 수명 주기 콜백 메서드도 여러 가지 있습니다. 모든 수명 주기 콜백 메서드는 나중에 액티비티 수명 주기 관리 섹션에서 자세히 논의할 것입니다.

사용자 인터페이스 구현

한 액티비티에 대한 사용자 인터페이스는 뷰 계층—즉, View 클래스에서 파생된 객체가 제공합니다. 각 뷰는 액티비티 창 안의 특정한 직사각형 공간을 제어하며 사용자 상호작용에 대응할 수 있습니다. 예를 들어, 어떤 뷰는 사용자가 터치하면 작업을 시작하는 버튼일 수 있습니다.

Android는 레이아웃을 디자인하고 정리하는 데 사용할 수 있도록 여러 가지 뷰를 미리 만들어 제공합니다. "위젯"이란 화면에 시각적(및 대화형) 요소를 제공하는 뷰입니다. 예를 들어 버튼, 텍스트 필드, 체크박스나 그저 하나의 이미지일 수도 있습니다. "레이아웃"은 선형 레이아웃, 격자형 레이아웃, 상대적 레이아웃과 같이 하위 레이아웃에 대해 독특한 레이아웃 모델을 제공하는 ViewGroup에서 파생된 뷰입니다. 또한, ViewViewGroup 클래스(또는 기존 서브클래스)의 아래로 내려가서 위젯과 레이아웃을 생성하고 이를 액티비티 레이아웃에 적용할 수 있습니다.

뷰를 사용하여 레이아웃을 정의하는 가장 보편적인 방식은 애플리케이션 리소스에 저장된 XML 레이아웃 파일을 사용하는 것입니다. 이렇게 하면 액티비티의 동작을 정의하는 소스 코드와 별개로 사용자 인터페이스 디자인을 유지할 수 있습니다. setContentView()로 액티비티의 UI로서 레이아웃을 설정하고, 해당 레이아웃의 리소스 ID를 전달할 수 있습니다. 그러나 액티비티 코드에 새로운 View를 생성하고 새로운 ViewViewGroup에 삽입하여 뷰 계층을 구축한 뒤 루트 ViewGroupsetContentView()에 전달해도 해당 레이아웃을 사용할 수 있습니다.

사용자 인터페이스 생성에 관한 정보는 사용자 인터페이스 문서를 참조하세요.

매니페스트에서 액티비티 선언

시스템에서 액티비티에 액세스할 수 있게 하려면 이를 매니페스트 파일에서 선언해야만 합니다. 액티비티를 선언하려면 매니페스트 파일을 열고 <activity> 요소를 <application> 요소의 하위 항목으로 추가합니다. 예:

<manifest ... >
  <application ... >
      <activity android:name=".ExampleActivity" />
      ...
  </application ... >
  ...
</manifest >

액티비티 레이블, 액티비티 아이콘 또는 액티비티 UI 스타일용 테마와 같이 이 요소에 포함할 수 있는 특성이 여러 가지 있습니다. android:name 특성은 유일한 필수 특성이며, 액티비티의 클래스 이름을 지정합니다. 일단 애플리케이션을 게시하고 나면 이 이름을 변경해서는 안됩니다. 이름을 변경하면 애플리케이션 바로 가기와 같은 일부 기능이 작동되지 않을 수도 있습니다(블로그 게시물의 바꿀 수 없는 항목을 참조하세요).

매니페스트에서 액티비티를 선언하는 것에 관한 자세한 정보는 <activity> 요소 참고 자료를 참조하세요.

인텐트 필터 사용

<activity> 요소 또한 여러 가지 인텐트 필터를 지정할 수 있습니다. 다른 애플리케이션 구성 요소가 이를 활성화하는 방법을 선언하기 위해 <intent-filter>를 사용하는 것입니다.

Android SDK 도구를 사용하여 새 애플리케이션을 생성하는 경우, 개발자를 위해 생성되어 있는 스텁 액티비티에 자동으로 인텐트 필터가 포함되어 있어 "주요" 동작에 응답하는 액티비티를 선언하며, 이는 "시작 관리자" 범주에 배치해야 합니다. 인텐트 필터는 다음과 같은 형태를 띱니다.

<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

<action> 요소는 이것이 애플리케이션으로 가는 "주요" 진입 지점이라는 것을 나타냅니다. <category> 요소는 이 액티비티가 시스템의 애플리케이션 시작 관리자에 목록으로 나열되어야 한다는 것을 나타냅니다(사용자가 이 액티비티를 시작할 수 있도록 해줌).

애플리케이션이 자체 포함 방식이기를 원하고 다른 애플리케이션이 이 애플리케이션의 액티비티를 활성화하도록 허용하지 않고자 하면, 달리 인텐트 필터가 더 필요하지 않습니다. "주요" 동작과 "시작 관리자" 범주가 있는 액티비티는 하나뿐이어야 합니다(이전 예시 참조). 다른 애플리케이션에서 사용할 수 없게 하고자 하는 액티비티에는 인텐트 필터가 있으면 안 됩니다. 이러한 액티비티는 명시적인 인텐트를 사용해 직접 시작할 수 있습니다(이 내용은 다음 섹션에서 논의).

그러나, 액티비티가 다른 애플리케이션(및 본인의 애플리케이션)에서 전달된 암시적 인텐트에 응답하도록 하려면 액티비티에 추가로 인텐트 필터를 정의해야만 합니다. 응답하게 하고자 하는 각 인텐트 유형별로 <action> 요소를 포함하는 <intent-filter>를 하나씩 포함시켜야 하며, 선택 사항으로 <category> 요소 및/또는 <data> 요소를 포함시킬 수 있습니다. 이와 같은 요소는 액티비티가 응답할 수 있는 인텐트의 유형을 나타냅니다.

액티비티가 인텐트에 응답하는 방식에 관한 자세한 정보는 인텐트 및 인텐트 필터 문서를 참조하세요.

액티비티 시작

다른 액티비티를 시작하려면 startActivity()를 호출한 다음 이에 시작하고자 하는 액티비티를 설명하는 Intent를 전달하면 됩니다. 인텐트는 시작하고자 하는 액티비티를 정확히 나타내거나, 수행하고자 하는 작업의 유형을 설명합니다(시스템이 적절한 액티비티를 선택하며, 이는 다른 애플리케이션에서 가져온 것일 수도 있습니다). 인텐트는 소량의 데이터를 운반하여 시작된 액티비티에서 사용할 수 있습니다.

본인의 애플리케이션 안에서 작업하는 경우에는, 알려진 액티비티를 시작하기만 하면 되는 경우가 잦습니다. 이렇게 하려면 시작하고자 하는 액티비티를 명시적으로 정의하는 인텐트를 클래스 이름을 사용하여 생성하면 됩니다. 예를 들어, 다음은 한 액티비티가 SignInActivity라는 이름의 다른 액티비티를 시작하는 방법입니다.

Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);

그러나, 애플리케이션이 다른 몇 가지 동작을 수행하고자 할 수도 있습니다. 예를 들어 이메일 보내기, 문자 메시지 보내기 또는 상태 업데이트 등을 액티비티의 데이터를 사용하여 수행할 수 있습니다. 이 경우, 본인의 애플리케이션에 그러한 동작을 수행할 자체 액티비티가 없을 수도 있습니다. 따라서 기기에 있는 다른 애플리케이션이 제공하는 액티비티를 대신 활용하여 동작을 수행하도록 할 수 있습니다. 바로 이 부분에서 인텐트의 진가가 발휘됩니다. 수행하고자 하는 동작을 설명하는 인텐트를 생성하면 시스템이 적절한 액티비티를 다른 애플리케이션에서 시작합니다. 해당 인텐트를 처리할 수 있는 액티비티가 여러 개 있는 경우, 사용자가 어느 것을 사용할지 선택합니다. 예를 들어 사용자가 이메일 메시지를 보낼 수 있게 하려면, 다음과 같은 인텐트를 생성하면 됩니다.

Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);

인텐트에 추가된 EXTRA_EMAIL은 이메일이 전송되어야 할 이메일 주소의 문자열 배열입니다. 이메일 애플리케이션이 이 인텐트에 응답하면, 애플리케이션은 엑스트라에서 제공된 문자열 배열을 읽은 다음 이를 이메일 작성 양식의 "수신" 필드에 배치합니다. 이 상황에서 이메일 애플리케이션의 액티비티가 시작되고 사용자가 작업을 끝내면 여러분의 액티비티가 재개됩니다.

결과에 대한 액티비티 시작

때로는 시작한 액티비티에서 결과를 받고 싶을 수도 있습니다. 그런 경우, (startActivity() 대신) startActivityForResult()를 호출해서 액티비티를 시작합니다. 그런 다음 후속 액티비티에서 결과를 받으려면, onActivityResult() 콜백 메서드를 구현합니다. 해당 후속 액티비티가 완료되면, 이것이 Intent 형식으로 결과를 onActivityResult() 메서드에 반환합니다.

예를 들어 사용자가 연락처 중에서 하나를 고를 수 있도록 하고 싶을 수 있습니다. 즉 여러분의 액티비티가 해당 연락처의 정보로 무언가 할 수 있도록 하는 것입니다. 그와 같은 인텐트를 생성하고 결과를 처리하려면 다음과 같이 하면 됩니다.

private void pickContact() {
    // Create an intent to "pick" a contact, as defined by the content provider URI
    Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
    startActivityForResult(intent, PICK_CONTACT_REQUEST);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // If the request went well (OK) and the request was PICK_CONTACT_REQUEST
    if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) {
        // Perform a query to the contact's content provider for the contact's name
        Cursor cursor = getContentResolver().query(data.getData(),
        new String[] {Contacts.DISPLAY_NAME}, null, null, null);
        if (cursor.moveToFirst()) { // True if the cursor is not empty
            int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);
            String name = cursor.getString(columnIndex);
            // Do something with the selected contact's name...
        }
    }
}

이 예시는 액티비티 결과를 처리하기 위해 onActivityResult() 메서드에서 사용해야 할 기본 로직을 보여줍니다. 첫 번째 조건은 요청이 성공적인지 확인합니다. 요청에 성공했다면 resultCodeRESULT_OK가 됩니다. 또한, 이 결과가 응답하는 요청이 알려져 있는지도 확인합니다. 이 경우에는 requestCodestartActivityForResult()와 함께 전송된 두 번째 매개변수와 일치합니다. 여기서부터 코드가 Intent(data 매개변수)로 반환된 데이터를 쿼리하여 액티비티 결과를 처리합니다.

그러면 ContentResolver가 콘텐츠 제공자에 대한 쿼리를 수행하고, 콘텐츠 제공자는 쿼리된 데이터를 읽을 수 있게 허용하는 Cursor를 반환합니다. 자세한 내용은 콘텐츠 제공자 문서를 참조하세요.

인텐트 사용에 관한 자세한 정보는 인텐트 및 인텐트 필터문서를 참조하세요.

액티비티 종료

액티비티를 종료하려면 해당 액티비티의 finish() 메서드를 호출하면 됩니다. finishActivity()를 호출하여 이전에 시작한 별도의 액티비티를 종료할 수도 있습니다.

참고: 대부분의 경우, 이와 같은 메서드를 사용하여 액티비티를 명시적으로 종료해서는 안 됩니다. 다음 섹션에서 액티비티 수명 주기에 관해 논의한 바와 같이, Android 시스템이 액티비티의 수명을 대신 관리해주므로 직접 액티비티를 종료하지 않아도 됩니다. 이와 같은 메서드를 호출하면 예상되는 사용자 환경에 부정적인 영향을 미칠 수 있으며, 따라서 사용자가 액티비티의 이 인스턴스에 돌아오는 것을 절대 바라지 않는 경우에만 사용해야 합니다.

액티비티 수명 주기 관리

콜백 메서드를 구현하여 액티비티의 수명 주기를 관리하는 것은 강력하고 유연한 애플리케이션 개발에 대단히 중요한 역할을 합니다. 액티비티의 수명 주기는 다른 액티비티와의 관계, 액티비티의 작업과 백 스택 등에 직접적으로 영향을 받습니다.

액티비티는 기본적으로 세 가지 상태로 존재할 수 있습니다.

재개됨(Resumed)
액티비티가 화면 포그라운드에 있고 사용자 포커스를 갖고 있습니다. (이 상태를 때로는 "실행 중"이라고 일컫기도 합니다).
일시정지됨(Paused)
다른 액티비티가 포그라운드에 나와 있고 사용자의 시선을 집중시키고 있지만, 이 액티비티도 여전히 표시되어 있습니다. 다시 말해, 다른 액티비티가 이 액티비티 위에 표시되어 있으며 해당 액티비티는 부분적으로 투명하거나 전체 화면을 덮지 않는 상태라는 뜻입니다. 일시정지된 액티비티는 완전히 살아있지만(Activity 객체가 메모리에 보관되어 있고, 모든 상태 및 멤버 정보를 유지하며, 창 관리자에 붙어 있는 상태로 유지됨), 메모리가 극히 부족한 경우 시스템이 중단시킬 수 있습니다.
정지됨(Stopped)
이 액티비티가 다른 액티비티에 완전히 가려진 상태입니다(액티비티가 이제 "백그라운드"에 위치함). 중단된 액티비티도 여전히 살아 있기는 마찬가지입니다(Activity 객체가 메모리에 보관되어 있고, 모든 상태와 멤버 정보를 유지하시만 창 관리자에 붙어 있지 않음 ). 그러나, 이는 더 이상 사용자에게 표시되지 않으며 다른 곳에 메모리가 필요하면 시스템이 종료시킬 수 있습니다.

액티비티가 일시정지 또는 중단된 상태이면, 시스템이 이를 메모리에서 삭제할 수 있습니다. 이러기 위해서는 종료를 요청하거나(finish() 메서드를 호출) 단순히 이 액티비티의 프로세스를 중단시키면 됩니다. 액티비티가 다시 열릴 때에는(종료 또는 중단된 후에) 처음부터 다시 생성해야 합니다.

수명 주기 콜백 구현

위에서 설명한 바와 같이 액티비티가 여러 가지 상태를 오가며 전환되면, 이와 같은 내용이 여러 가지 콜백 메서드를 통해 통지됩니다. 콜백 메서드는 모두 후크로서, 액티비티 상태가 변경될 때 적절한 작업을 하기 위해 이를 재정의할 수 있습니다. 다음의 기본 액티비티에는 기본 수명 주기 메서드가 각각 하나씩 포함되어 있습니다.

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.
    }
}

참고: 이와 같은 수명 주기 메서드를 구현하려면, 항상 슈퍼클래스 구현을 호출한 다음에만 다른 작업을 시작할 수 있습니다(위의 예시 참조).

이와 같은 메서드를 모두 합쳐 한 액티비티의 수명 주기 전체를 정의합니다. 이러한 메서드를 구현함으로써 액티비티 수명 주기 내의 세 가지 중첩된 루프를 모니터할 수 있습니다.

  • 한 액티비티의 전체 수명onCreate() 호출과 onDestroy() 호출 사이를 말합니다. 액티비티는 onCreate()에서 "전체" 상태(레이아웃 정의 등)의 설정을 수행한 다음 나머지 리소스를 모두 onDestroy()에 해제해 주어야 합니다. 예를 들어, 액티비티에 네트워크에서 데이터를 다운로드하기 위해 배경에서 실행 중인 스레드가 있는 경우, 이는 해당 스레드를 onCreate()에서 생성한 다음 onDestroy()에서 그 스레드를 중단할 수 있습니다.
  • 한 액티비티의 가시적 수명onStart() 호출과 onStop() 호출 사이를 말합니다. 이 기간 중에는 사용자가 액티비티를 화면에서 보고 이와 상호작용할 수 있습니다. 예컨대 onStop()이 호출되어 새 액티비티가 시작되면 이 액티비티는 더 이상 표시되지 않게 됩니다. 이와 같은 두 가지 메서드 중에서 사용자에게 액티비티를 표시하는 데 필요한 리소스를 유지하면 됩니다. 예를 들어, onStart()에서 BroadcastReceiver를 등록하고 UI에 영향을 미치는 변화를 모니터링하고 onStop()에서 등록을 해제하면 사용자는 여러분이 무엇을 표시하고 있는지 더 이상 볼 수 없게 됩니다. 시스템은 액티비티의 전체 수명 내내 onStart()onStop()을 여러 번 호출할 수 있으며, 이때 액티비티는 사용자에게 표시되었다 숨겨지는 상태를 오가게 됩니다.

  • 액티비티의 전경 수명onResume() 호출과 onPause() 호출 사이를 말합니다. 이 기간 중에는 이 액티비티가 화면에서 다른 모든 액티비티 앞에 표시되며 사용자 입력도 여기에 집중됩니다. 액티비티는 포그라운드에 나타났다 숨겨지는 전환을 자주 반복할 수 있습니다. 예를 들어 , 기기가 절전 모드에 들어가거나 대화상자가 나타나면 onPause()가 호출됩니다. 이 상태는 자주 전환될 수 있으므로, 이 두 가지 메서드의 코드는 상당히 가벼워야 합니다. 그래야 전환이 느려 사용자를 기다리게 하는 일을 피할 수 있습니다.

그림 1은 액티비티가 상태 사이에서 취할 수 있는 이와 같은 루프와 경로를 나타낸 것입니다. 직사각형이 액티비티가 여러 상태 사이를 전환할 때 작업을 수행하도록 구현할 수 있는 콜백 메서드를 나타냅니다.

그림 1. 액티비티 수명 주기.

같은 수명 주기 콜백 메서드가 표 1에 나열되어 있으며, 이 표는 각 콜백 메서드를 더욱 상세하게 설명하며 액티비티의 전반적인 수명 주기 내에서 각 메서드의 위치를 나타내기도 합니다. 여기에는 콜백 메서드가 완료된 다음 시스템이 액티비티를 중단시킬 수 있는지 여부도 포함되어 있습니다.

표 1. 액티비티 수명 주기 콜백 메서드의 요약입니다.

메서드 설명 완료 후 중단 가능? 다음
onCreate() 액티비티가 처음 생성되었을 때 호출됩니다. 이곳에서 일반적인 정적 설정을 모두 수행해야 합니다. 즉 뷰 생성, 목록에 데이터 바인딩하기 등을 말합니다. 이 메서드에는 액티비티의 이전 상태가 캡처된 경우 해당 상태를 포함한 번들 객체가 전달됩니다(이 문서 나중에 나오는 액티비티 상태 저장을 참조하세요 ).

이 뒤에는 항상 onStart()가 따라옵니다.

아니요 onStart()
     onRestart() 액티비티가 중단되었다가 다시 시작되기 직전에 호출됩니다.

이 뒤에는 항상 onStart()가 따라옵니다.

아니요 onStart()
onStart() 액티비티가 사용자에게 표시되기 직전에 호출됩니다.

액티비티가 전경으로 나오면 onResume()이 뒤에 따라오고, 액티비티가 숨겨지면 onStop()이 뒤에 따라옵니다.

아니요 onResume()
또는
onStop()
     onResume() 액티비티가 시작되고 사용자와 상호작용하기 직전에 호출됩니다. 이 시점에서 액티비티는 액티비티 스택의 맨 위에 있으며, 사용자가 정보를 입력하고 있습니다.

이 뒤에는 항상 onPause()가 따라옵니다.

아니요 onPause()
onPause() 시스템이 다른 액티비티를 재개하기 직전에 호출됩니다. 이 메서드는 일반적으로 데이터를 유지하기 위해 저장되지 않은 변경 사항을 커밋하는 데, 애니메이션을 비롯하여 CPU를 소모하는 기타 작업을 중단하는 등 여러 가지 용도에 사용됩니다. 이 메서드는 무슨 일을 하든 매우 빨리 끝내야 합니다. 이것이 반환될 때까지 다음 액티비티가 재개되지 않기 때문입니다.

액티비티가 다시 전경으로 돌아오면 onResume()이 뒤에 따라오고 액티비티가 사용자에게 보이지 않게 되면 onStop()이 뒤에 옵니다.

onResume()
또는
onStop()
onStop() 액티비티가 더 이상 사용자에게 표시되지 않게 되면 호출됩니다. 이것은 액티비티가 소멸되고 있기 때문에 일어날 수도 있고, 다른 액티비티 (기존 것이든 새로운 것이든)가 재개되어 이것을 덮고 있기 때문일 수도 있습니다.

액티비티가 다시 사용자와 상호작용하면 onRestart()가 뒤에 따라오고 액티비티가 사라지면 onDestroy()가 뒤에 따라옵니다.

onRestart()
또는
onDestroy()
onDestroy() 액티비티가 소멸되기 전에 호출됩니다. 이것이 액티비티가 받는 마지막 호출입니다. 이것이 호출될 수 있는 경우는 액티비티가 완료되는 중이기 때문(누군가가 여기에 finish()를 호출해서)일 수도 있고, 시스템이 공간을 절약하기 위해 액티비티의 이 인스턴스를 일시적으로 소멸시키는 중이기 때문일 수도 있습니다. 이와 같은 두 가지 시나리오는 isFinishing() 메서드로 구분할 수 있습니다. 없음

"완료 후 중단 가능?"이라는 레이블이 붙은 열은 시스템이 메서드가 반환된 후 액티비티 코드의 다른 줄을 실행하지 않고도 언제든 이 액티비티를 호스팅하는 프로세스를 중단시킬 수 있는지 여부를 나타냅니다. 다음 세 가지 메서드가 "예"로 표시되어 있습니다({@linkandroid.app.Activity#onPause onPause()}, onStop()onDestroy()). onPause()가 세 가지 메서드 중에서 첫 번째이므로, 액티비티가 생성되면 onPause()는 프로세스가 지워지기 전에 반드시 호출되는 마지막 메서드입니다. 시스템이 비상 시에 메모리를 복구해야 할 경우, onStop()onDestroy()는 호출되지 않을 수도 있습니다. 따라서, 중요한 영구적 데이터(사용자 편집 등)를 보관하기 위해 작성하는 경우에는 onPause()를 사용해야 합니다. 그러나, onPause() 중에 어느 정보를 유지해야 할지는 조심해서 선택해야 합니다. 이 메서드에 차단 절차가 있으면 다음 액티비티로의 전환을 차단하고 사용자 환경을 느려지게 할 수 있기 때문입니다.

중단 가능한 열에 "아니요"로 표시된 메서드는 액티비티를 호스팅하는 프로세스를 보호하여 호출된 즉시 중단되지 않도록 방지합니다. 따라서 액티비티는 onPause()가 반환되는 시기부터 onResume()이 호출되는 시기 사이에 중단시킬 수 있습니다. 다시 중단시킬 수 있는 상태가 되려면 onPause()가 다시 호출되어 반환되어야 합니다.

참고: 표 1에 나타난 이런 정의에 따르면 엄밀히 말해 "중단 가능한" 것이 아닌 액티비티라도 시스템이 중단시킬 수는 있습니다. 다만 이것은 다른 리소스가 없는 극단적인 상황에서만 발생합니다. 액티비티가 중단될 수 있는 시기가 언제인지는 프로세스 및 스레딩 문서에서 보다 자세히 논의합니다.

액티비티 상태 저장

액티비티 수명 주기 관리 도입부에서는 액티비티가 일시중지되거나 중단되었더라도 액티비티의 상태는 그대로 유지된다고 잠시 언급한 바 있습니다. 이것은 Activity 객체가 일시중지되거나 중단된 경우에도 메모리 안에 그대로 보관되었기 때문에 가능합니다. 즉 이 액티비티의 멤버와 현재 상태에 대한 모든 정보가 아직 살아 있다는 뜻입니다. 따라서, 사용자가 액티비티 내에서 변경한 모든 내용도 그대로 유지되어 액티비티가 포그라운드로 돌아갈 때("재개"될 때) 그와 같은 변경 사항도 그대로 존재하게 됩니다.

그러나 시스템이 메모리를 복구하기 위해 액티비티를 소멸시키는 경우에는 Activity 객체가 소멸되므로 시스템이 액티비티의 상태를 온전히 유지한 채로 간단하게 재개할 수 없게 됩니다. 대신, 사용자가 다시 이 액티비티로 이동해 오면 시스템이 Activity 객체를 다시 생성해야 합니다. 하지만, 사용자는 시스템이 해당 액티비티를 소멸시켰다가 다시 생성했다는 것을 모릅니다. 따라서 액티비티가 예전과 똑같을 것이라고 예상할 것입니다. 이런 상황에서는 액티비티 상태에 관한 정보를 저장할 수 있는 추가 콜백 메서드 onSaveInstanceState()를 구현하여 액티비티 상태에 관한 중요한 정보를 보존할 수 있습니다.

시스템이 onSaveInstanceState() 를 호출한 다음에 액티비티를 소멸되기 쉽게 만듭니다. 시스템은 putString()putInt() 같은 메서드를 사용하여, 액티비티에 관한 정보를 이름-값 쌍으로 저장할 수 있는 Bundle을 위 메서드에 전달합니다. 그리고 시스템이 애플리케이션 프로세스를 종료하고 사용자가 액티비티로 다시 돌아오면, 시스템이 액티비티를 다시 생성하고 BundleonCreate()onRestoreInstanceState()에게 전달합니다. 이들 메서드 중 하나를 사용하여 Bundle에서 저장된 상태를 추출하고 액티비티 상태 를 복원할 수 있습니다. 복구할 상태 정보가 없는 경우, 여러분에게 전달되는 Bundle은 null입니다(액티비티가 처음 생성되었을 때 이런 경우가 발생합니다).

그림 2. 액티비티의 상태가 온전한 채로 사용자 포커스로 다시 돌아오는 데에는 두 가지 방식이 있습니다. 하나는 액티비티가 소멸되었다가 다시 생성되어 액티비티가 이전에 저장된 상태를 복구해야 하는 경우, 다른 하나는 액티비티가 중단되었다가 재개되었으며 액티비티 상태가 온전히 유지된 경우입니다.

참고: 상태를 저장할 필요가 없는 경우도 있으므로 액티비티가 소멸되기 전에 onSaveInstanceState()가 호출된다는 보장은 없습니다 (예컨대 사용자가 명시적으로 액티비티를 닫기 위해 Back 버튼을 눌러서 액티비티를 떠날 때가 이에 해당합니다). 시스템이 onSaveInstanceState()를 호출하는 경우, onStop() 전에 호출하는 것이 일반적이며 onPause() 전에 호출할 가능성도 높습니다.

그러나 아무것도 하지 않고 onSaveInstanceState()를 구현하지 않더라도, Activity 클래스의 기본 구현 onSaveInstanceState()가 일부 액티비티 상태를 복구합니다. 특히, 기본 구현은 레이아웃에서 onSaveInstanceState()가 나올 때마다 해당하는 View 메서드를 호출하고, 각 뷰가 저장해야 하는 자체 관련 정보를 제공할 수 있게 해줍니다. Android 프레임워크를 사용하는 위젯은 거의 대부분 이 메서드를 필요에 따라 구현하므로, UI에 눈에 보이는 변경이 있으면 모두 자동으로 저장되며 액티비티를 다시 생성하면 복구됩니다. 예를 들어, EditText 위젯은 사용자가 입력한 모든 텍스트 를 저장하고 CheckBox 위젯은 확인 여부를 저장합니다. 여러분이 해야 할 유일한 작업은 상태를 저장하고자 하는 각 위젯에 고유 ID(android:id 속성 포함)를 제공하는 것입니다. 위젯에 ID가 없으면 시스템이 그 위젯의 상태를 저장할 수 없습니다.

onSaveInstanceState()의 기본 구현이 액티비티 UI의 유용한 정보를 저장하지만 추가 정보를 저장하려면 이를 재설정해야 할 수도 있습니다. 예를 들어, 액티비티 수명에서 변경된 멤버 값을 변경해야 할 수도 있습니다( UI에서 복구된 값과 상관관계가 있을 수 있지만 이런 UI 값을 보유한 멤버는 기본적으로 복구되지 않습니다 ).

onSaveInstanceState()의 기본 구현이 UI 상태를 저장하는 데 도움이 되기 때문에, 추가 상태 정보를 저장하기 위해 이 메서드를 재정의하려면 작업을 하기 전에 항상 onSaveInstanceState()의 슈퍼클래스 구현 을 호출해야 합니다. 이와 마찬가지로 onRestoreInstanceState()를 재정의하는 경우, 이것의 슈퍼클래스 구현도 호출해야 합니다. 이렇게 해야 기본 구현이 뷰 상태를 복구할 수 있습니다.

참고: onSaveInstanceState()의 호출이 보장되지 않으므로 이것은 액티비티의 일시적 상태(UI의 상태 )를 기록하는 데에만 사용하고, 영구 데이터를 보관하는 데 사용해서는 안 됩니다. 대신, 사용자가 액티비티를 떠날 때 영구적인 데이터(데이터베이스에 저장되어야 하는 데이터 등)를 저장하려면 onPause()를 사용해야 합니다.

애플리케이션의 상태 저장 기능을 시험하는 좋은 방법은 기기를 회전해보고 화면 방향이 바뀌는지 확인하는 것입니다. 화면 방향이 바뀌면 시스템은 액티비티를 소멸시켰다가 다시 생성하여 새 화면 구성에서 이용할 수 있을지 모르는 대체 리소스를 적용합니다. 이 이유 하나만으로도 액티비티가 다시 생성되었을 때 상태를 완전히 복구할 수 있어야 한다는 점이 대단히 중요합니다. 사용자는 애플리케이션을 사용하면서 화면을 자주 돌리기 때문입니다.

구성 변경 처리

몇몇 기기 구성은 런타임 중에 변경될 수 있습니다(예: 화면 방향, 키보드 가용성 및 언어 등). 이러한 변경이 발생하면 Android는 실행 중인 액티비티를 다시 생성합니다 (시스템이 onDestroy()를 호출하고 즉시 onCreate()를 호출합니다). 이런 동작은 여러분이 제공한 대체 리소스로 애플리케이션을 자동으로 다시 로딩함으로써 새로운 구성에 애플리케이션이 적응하는 것을 돕도록 설계되었습니다 (예: 다양한 화면 방향과 크기에 대한 다양한 레이아웃).

액티비티를 적절히 설계하여 화면 방향 변경으로 인한 재시작을 감당할 수 있으며 위에 설명한 것처럼 액티비티 상태를 복구할 수 있도록 하면, 애플리케이션이 액티비티 수명 주기에서 예기치 못한 이벤트가 일어나도 더욱 탄력적으로 복구될 수 있습니다.

이러한 재시작을 처리하는 가장 좋은 방법은 이전 섹션에서 논의한 바와 같이 onSaveInstanceState()onRestoreInstanceState()(또는 onCreate())를 사용하여 액티비티 상태를 저장하고 복구하는 것입니다.

런타임에 발생하는 구성 변경과 그 처리 방법에 대한 자세한 정보는 런타임 변경 처리에 대한 가이드를 읽어보세요.

액티비티 조정

한 액티비티가 다른 액티비티를 시작하면, 둘 모두 수명 주기 전환을 겪습니다. 첫 번째 액티비티는 일시중지되고 중단되는 반면(백그라운드에서 계속 보이는 경우에는 중단되지 않지만), 다른 액티비티는 생성됩니다. 이와 같은 액티비티가 디스크 또는 다른 곳에 저장된 데이터를 공유하는 경우, 첫 번째 액티비티는 두 번째 액티비티가 생성되기 전에 완전히 중단되지 않는다는 점을 이해하는 것이 중요합니다. 그렇다기보다는, 두 번째 액티비티의 시작 과정이 첫 번째 액티비티 중단 과정과 겹쳐 일어납니다.

수명 주기 콜백은 분명히 정의된 순서가 있으며 특히 두 개의 액티비티가 같은 프로세스 안에 있으면서 하나가 다른 하나를 시작하는 경우 순서가 더욱 확실합니다. 다음은 액티비티 A가 액티비티 B를 시작할 때 발생하는 작업 순서입니다.

  1. 액티비티 A의 onPause() 메서드가 실행됩니다.
  2. 액티비티 B의 onCreate(), onStart()onResume() 메서드가 순차적으로 실행됩니다. (이제 사용자는 액티비티 B에 시선을 집중합니다).
  3. 그런 다음, 액티비티 A가 더 이상 화면에 표시되지 않는 경우 이 액티비티의 onStop() 메서드가 실행됩니다.

이처럼 수명 주기 콜백의 순서를 예측할 수 있기 때문에 한 액티비티에서 다른 액티비티로 전환되는 정보를 관리할 수 있습니다. 예를 들어 첫 번째 액티비티가 중단될 때 데이터베이스에 내용을 작성해서 다음 액티비티가 그 내용을 읽을 수 있도록 하려면, 데이터베이스에는 onPause() 중에(onStop() 중이 아니라) 쓰기 작업을 해야 합니다.