diff options
Diffstat (limited to 'docs/html-intl/intl/ko/guide/components')
-rw-r--r-- | docs/html-intl/intl/ko/guide/components/activities.jd | 756 | ||||
-rw-r--r-- | docs/html-intl/intl/ko/guide/components/bound-services.jd | 658 | ||||
-rw-r--r-- | docs/html-intl/intl/ko/guide/components/fragments.jd | 812 | ||||
-rw-r--r-- | docs/html-intl/intl/ko/guide/components/fundamentals.jd | 480 | ||||
-rw-r--r-- | docs/html-intl/intl/ko/guide/components/index.jd | 57 | ||||
-rw-r--r-- | docs/html-intl/intl/ko/guide/components/intents-filters.jd | 901 | ||||
-rw-r--r-- | docs/html-intl/intl/ko/guide/components/loaders.jd | 494 | ||||
-rw-r--r-- | docs/html-intl/intl/ko/guide/components/processes-and-threads.jd | 411 | ||||
-rw-r--r-- | docs/html-intl/intl/ko/guide/components/recents.jd | 256 | ||||
-rw-r--r-- | docs/html-intl/intl/ko/guide/components/services.jd | 813 | ||||
-rw-r--r-- | docs/html-intl/intl/ko/guide/components/tasks-and-back-stack.jd | 578 |
11 files changed, 6216 insertions, 0 deletions
diff --git a/docs/html-intl/intl/ko/guide/components/activities.jd b/docs/html-intl/intl/ko/guide/components/activities.jd new file mode 100644 index 000000000000..001982f6f63c --- /dev/null +++ b/docs/html-intl/intl/ko/guide/components/activities.jd @@ -0,0 +1,756 @@ +page.title=작업 +page.tags=activity,intent +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> +<h2>이 문서의 내용</h2> +<ol> + <li><a href="#Creating">액티비티 생성하기</a> + <ol> + <li><a href="#UI">사용자 인터페이스 구현하기</a></li> + <li><a href="#Declaring">매니페스트에서 액티비티 선언하기</a></li> + </ol> + </li> + <li><a href="#StartingAnActivity">액티비티 시작하기</a> + <ol> + <li><a href="#StartingAnActivityForResult">결과에 대한 액티비티 시작하기</a></li> + </ol> + </li> + <li><a href="#ShuttingDown">액티비티 종료하기</a></li> + <li><a href="#Lifecycle">액티비티 수명 주기 관리하기</a> + <ol> + <li><a href="#ImplementingLifecycleCallbacks">수명 주기 콜백 구현하기</a></li> + <li><a href="#SavingActivityState">액티비티 상태 저장하기</a></li> + <li><a href="#ConfigurationChanges">구성 변경 처리하기</a></li> + <li><a href="#CoordinatingActivities">액티비티 조정하기</a></li> + </ol> + </li> +</ol> + +<h2>Key 클래스</h2> +<ol> + <li>{@link android.app.Activity}</li> +</ol> + +<h2>참고 항목</h2> +<ol> + <li><a href="{@docRoot}guide/components/tasks-and-back-stack.html">작업 및 백 +스택</a></li> +</ol> + +</div> +</div> + + + +<p>{@link android.app.Activity}는 일종의 애플리케이션 구성 요소로서, +사용자와 상호작용할 수 있는 화면을 제공하여 전화 걸기, 사진 찍기, 이메일 보내기 또는 지도 보기 등의 일을 할 수 +있습니다. 액티비티마다 창이 하나씩 주어져 이곳에 사용자 인터페이스를 끌어올 수 있습니다. 이 창은 +일반적으로 화면을 가득 채우지만, 작은 창으로 만들어 다른 창 위에 띄울 수도 +있습니다.</p> + +<p> 하나의 애플리케이션은 보통 여러 개의 액티비티가 느슨하게 서로 묶여 있는 형태로 +구성됩니다. 통상 한 애플리케이션 내에서 하나의 액티비티가 "주요" 액티비티로 지정되며, +사용자가 이 애플리케이션을 처음 실행할 때 이 액티비티가 사용자에게 표시됩니다. 그런 후 +각각의 액티비티는 여러 가지 작업을 수행하기 위해 또 다른 액티비티를 시작할 수 있습니다. 새로운 +액티비티가 시작될 때마다 이전 액티비티는 중단되지만 시스템은 해당 액티비티를 +스택("백 스택")에 보존합니다. 새로운 액티비티가 시작되면, 이것이 백 스택 위로 밀려와 사용자의 +시선을 끕니다. 백 스택은 기본적인 "후입선출" 방식을 지키므로, +사용자가 현재 액티비티를 끝내고 <em>뒤로</em> 버튼을 누르면 해당 액티비티가 +스택에서 튀어나와(소멸되고) 이전 액티비티를 재개합니다 (백 스택은 +<a href="{@docRoot}guide/components/tasks-and-back-stack.html">작업 +및 백 스택</a> 문서에서 상세하게 논의합니다).</p> + +<p>한 액티비티가 새로운 액티비티의 시작으로 인해 중단된 경우, 이 상태 변경은 +액티비티의 수명 주기 콜백 메서드를 통해 알려집니다. +액티비티가 시스템 액티비티를 생성, 중단, 재시작, 제거하는 등의 상태 변화로 인해 받을 수 있는 콜백 메서드는 여러 가지가 있습니다. + + 각 콜백은 상태 변화에 알맞은 특정 작업을 수행할 기회를 제공합니다. + 예를 들어 액티비티가 중단되면 네트워크 또는 데이터베이스 연결과 같이 +큰 개체는 모두 해제해야 합니다. 액티비티가 재개되면, 필요한 리소스를 +다시 획득하여 중단된 작업을 재개할 수 있습니다. 이러한 상태 전환은 +모두 액티비티 수명 주기의 일부입니다.</p> + +<p>이 문서의 남은 부분에서는 다양한 액티비티 상태 사이의 전환을 적절히 관리할 수 있도록 액티비티 수명 주기가 작동하는 방식을 자세히 논하는 등, +액티비티를 구축하고 사용하는 기본적 방법을 +설명합니다.</p> + + + +<h2 id="Creating">액티비티 생성하기</h2> + +<p>액티비티를 생성하려면 {@link android.app.Activity}의 하위 클래스(또는 이의 +기존 하위 클래스)를 생성해야 합니다. 하위 클래스에서는 +액티비티 생성, 중단, 재개, 소멸 시기 등과 같은 +수명 주기의 다양한 상태 간 액티비티가 전환될 때 시스템이 호출하는 콜백 메서드를 구현해야 합니다. 가장 중요한 두 가지 콜백 메서드는 +다음과 같습니다.</p> + +<dl> + <dt>{@link android.app.Activity#onCreate onCreate()}</dt> + <dd>이 메서드는 반드시 구현해야 합니다. 시스템은 액티비티를 생성할 때 이것을 호출합니다. + 구현하는 중에 액티비티의 필수 구성 요소를 초기화해야 +합니다. + 무엇보다도 중요한 점은, 바로 여기서 {@link android.app.Activity#setContentView + setContentView()}를 호출해야 액티비티의 사용자 인터페이스 레이아웃을 정의할 수 있다는 점입니다.</dd> + <dt>{@link android.app.Activity#onPause onPause()}</dt> + <dd>시스템이 이 메서드를 호출하는 것은 사용자가 액티비티를 떠난다는 +첫 번째 신호입니다(다만 이것이 항상 액티비티가 소멸 중이라는 뜻은 아닙니다). 현재 사용자 세션을 넘어서 +지속되어야 하는 변경 사항을 커밋하려면 보통 이곳에서 해아 합니다(사용자가 +돌아오지 않을 수 있기 때문입니다).</dd> +</dl> + +<p>여러 액티비티 사이에서 원활한 사용자 경험을 제공하고, 액티비티 중단이나 심지어 +소멸을 초래할 수도 있는 예상치 못한 간섭을 처리하기 위해 사용해야 하는 다른 수명 주기 +콜백 메서드도 여러 가지 있습니다. 모든 수명 주기 콜백 메서드는 나중에 +<a href="#Lifecycle">액티비티 수명 주기 관리</a> 섹션에서 자세히 논의할 것입니다.</p> + + + +<h3 id="UI">사용자 인터페이스 구현하기</h3> + +<p> 한 액티비티에 대한 사용자 인터페이스는 보기 계층—즉, +{@link android.view.View} 클래스에서 파생된 개체가 제공합니다. 각 보기는 액티비티 창 안의 특정한 직사각형 공간을 + 제어하며 사용자 상호 작용에 대응할 수 있습니다. 예를 들어, 어떤 보기는 사용자가 터치하면 +작업을 시작하는 버튼일 수 있습니다.</p> + +<p>Android는 레이아웃을 디자인하고 정리하는 데 사용할 수 있도록 여러 가지 보기를 미리 만들어 +제공합니다. "위젯"이란 화면에 시각적(이자 대화형) 요소를 제공하는 보기입니다. 예를 들어 +버튼, 텍스트 필드, 확인란이나 그저 하나의 이미지일 수도 있습니다. "레이아웃"은 선형 레이아웃, 격자형 레이아웃, 상대적 레이아웃과 같이 하위 레이아웃에 대해 독특한 레이아웃 모델을 제공하는 {@link +android.view.ViewGroup}에서 파생된 +보기입니다. 또한, {@link android.view.View}와 +{@link android.view.ViewGroup} 클래스(또는 기존 하위 클래스)의 아래로 내려가서 위젯과 레이아웃을 생성하고 +이를 액티비티 레이아웃에 적용할 수 있습니다.</p> + +<p>보기를 사용하여 레이아웃을 정의하는 가장 보편적인 방식은 애플리케이션 리소스에 저장된 +XML 레이아웃 파일을 사용하는 것입니다. 이렇게 하면 액티비티의 동작을 정의하는 +소스 코드와 별개로 사용자 인터페이스 디자인을 유지할 수 있습니다. +{@link android.app.Activity#setContentView(int) setContentView()}로 액티비티의 UI로서 레이아웃을 설정하고, +해당 레이아웃의 리소스 ID를 전달할 수 있습니다. 그러나 액티비티 코드에 새로운 {@link android.view.View}를 생성하고 + 새로운 {@link +android.view.View}를 {@link android.view.ViewGroup}에 삽입하여 보기 계층을 구축한 뒤 루트 +{@link android.view.ViewGroup}을 {@link android.app.Activity#setContentView(View) +setContentView()}에 전달해도 해당 레이아웃을 사용할 수 있습니다.</p> + +<p>사용자 인터페이스 생성에 관한 정보는 <a href="{@docRoot}guide/topics/ui/index.html">사용자 인터페이스</a> 문서를 참조하십시오.</p> + + + +<h3 id="Declaring">매니페스트에서 액티비티 선언하기</h3> + +<p>시스템에서 액티비티에 액세스할 수 있게 하려면 이를 매니페스트 파일에서 +선언해야만 합니다. 액티비티를 선언하려면 매니페스트 파일을 열고 <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> 요소를 + <a href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a> +요소의 하위 항목으로 추가합니다. 예:</p> + +<pre> +<manifest ... > + <application ... > + <activity android:name=".ExampleActivity" /> + ... + </application ... > + ... +</manifest > +</pre> + +<p>액티비티 레이블, 액티비티 아이콘 또는 액티비티 UI 스타일용 테마와 같이 +이 요소에 포함할 수 있는 속성이 여러 가지 있습니다. + <a href="{@docRoot}guide/topics/manifest/activity-element.html#nm">{@code android:name}</a> + 속성이 유일한 필수 속성입니다. 이것이 액티비티의 클래스 이름을 지정합니다. 일단 + 애플리케이션을 게시하고 나면 이 이름을 변경해서는 안 됩니다. 이름을 변경하면 +애플리케이션 바로 가기와 같은 일부 기능이 고장 날 수 있습니다(블로그 게시물의 <a href="http://android-developers.blogspot.com/2011/06/things-that-cannot-change.html">바꿀 수 없는 +항목</a>을 참조하십시오).</p> + +<p>매니페스트에서 액티비티를 선언하는 것에 관한 자세한 정보는 <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> 요소 + 참고 자료를 참조하십시오.</p> + + +<h4>인텐트 필터 사용하기</h4> + +<p><a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code +<activity>}</a> 요소 또한 여러 가지 인텐트 필터를 지정할 수 있습니다. 다른 애플리케이션 구성 요소가 이를 활성화하는 방법을 선언하기 위해 <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a>를 사용하는 +것입니다.</p> + +<p>Android SDK 도구를 사용하여 새 애플리케이션을 생성하는 경우, 개발자를 위해 +생성되어 있는 스텁 액티비티에 자동으로 인텐트 필터가 포함되어 있어 "주요" 동작에 +응답하는 액티비티를 선언하며, 이는 "시작 관리자" 범주에 배치해야 합니다. 인텐트 필터는 다음과 +같은 형태를 띱니다.</p> + +<pre> +<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> +</pre> + +<p><a href="{@docRoot}guide/topics/manifest/action-element.html">{@code +<action>}</a> 요소는 이것이 애플리케이션으로 가는 "주요" 진입 지점이라는 것을 나타냅니다. <a href="{@docRoot}guide/topics/manifest/category-element.html">{@code +<category>}</a> 요소는 이 액티비티가 시스템의 +애플리케이션 시작 관리자에 목록으로 나열되어야 한다는 것을 나타냅니다(사용자가 이 액티비티를 시작할 수 있도록 해줌).</p> + +<p>애플리케이션이 자체 포함 방식이기를 원하고 다른 애플리케이션이 이 +애플리케이션의 액티비티를 활성화하도록 허용하지 않고자 하면, 달리 인텐트 필터가 더 필요하지 않습니다. "주요" 동작과 "시작 관리자" 범주가 있는 +액티비티는 하나뿐이어야 합니다(이전 예시 참조). 다른 애플리케이션에서 +사용할 수 없게 하고자 하는 액티비티에는 인텐트 필터가 있으면 안 됩니다. +이러한 액티비티는 명시적인 인텐트를 사용해 직접 시작할 수 있습니다(이 내용은 다음 섹션에서 논의).</p> + +<p>그러나, 액티비티가 다른 애플리케이션(및 본인의 애플리케이션)에서 +전달된 암시적 인텐트에 응답하도록 하려면 액티비티에 추가로 인텐트 필터를 정의해야만 +합니다. 응답하게 하고자 하는 각 인텐트 유형별로 +<a href="{@docRoot}guide/topics/manifest/action-element.html">{@code +<action>}</a> 요소를 포함하는 <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a>를 하나씩 포함시켜야 하며, 선택 사항으로 <a href="{@docRoot}guide/topics/manifest/category-element.html">{@code +<category>}</a> 요소 및/또는 <a href="{@docRoot}guide/topics/manifest/data-element.html">{@code +<data>}</a> 요소를 포함시킬 수 있습니다. 이와 같은 요소는 액티비티가 응답할 수 있는 인텐트의 유형을 +나타냅니다.</p> + +<p>액티비티가 인텐트에 응답하는 방식에 관한 자세한 정보는 <a href="{@docRoot}guide/components/intents-filters.html">인텐트 및 인텐트 필터</a> +문서를 참조하십시오.</p> + + + +<h2 id="StartingAnActivity">액티비티 시작하기</h2> + +<p>다른 액티비티를 시작하려면 {@link android.app.Activity#startActivity + startActivity()}를 호출한 다음 이에 시작하고자 하는 액티비티를 설명하는 {@link android.content.Intent}를 +전달하면 됩니다. 인텐트는 시작하고자 하는 액티비티를 정확히 나타내거나, 수행하고자 하는 작업의 +유형을 설명하는 것입니다(시스템이 적절한 액티비티를 선택하며, +이는 + 다른 애플리케이션에서 가져온 것일 수도 있습니다). 인텐트는 소량의 데이터를 운반하여 시작된 액티비티에서 + 사용할 수 있습니다.</p> + +<p>본인의 애플리케이션 안에서 작업하는 경우에는, 알려진 액티비티를 시작하기만 하면 되는 경우가 잦습니다. + 이렇게 하려면 시작하고자 하는 액티비티를 명시적으로 정의하는 인텐트를 클래스 이름을 +사용하여 생성하면 됩니다. 예를 들어, 다음은 한 액티비티가 {@code +SignInActivity}라는 이름의 다른 액티비티를 시작하는 방법입니다.</p> + +<pre> +Intent intent = new Intent(this, SignInActivity.class); +startActivity(intent); +</pre> + +<p>그러나, 애플리케이션이 다른 몇 가지 동작을 수행하고자 할 수도 있습니다. 예를 들어 이메일 보내기, 문자 +메시지 보내기 또는 상태 업데이트 등을 액티비티의 데이터를 사용하여 수행하는 것입니다. 이 경우, 본인의 애플리케이션에 +그러한 동작을 수행할 자체 액티비티가 없을 수도 있습니다. 따라서 기기에 있는 다른 애플리케이션이 +제공하는 액티비티를 대신 활용하여 동작을 수행하도록 할 수 있습니다. 바로 이 부분에서 +인텐트의 진가가 발휘됩니다. 수행하고자 하는 동작을 설명하는 인텐트를 생성하면 +시스템이 적절한 액티비티를 + 다른 애플리케이션에서 시작하는 것입니다. 해당 인텐트를 + 처리할 수 있는 액티비티가 여러 개 있는 경우, 사용자가 어느 것을 사용할지 선택합니다. 예를 + 들어 사용자가 이메일 메시지를 보낼 수 있게 하려면, 다음과 같은 인텐트를 +생성하면 됩니다.</p> + +<pre> +Intent intent = new Intent(Intent.ACTION_SEND); +intent.putExtra(Intent.EXTRA_EMAIL, recipientArray); +startActivity(intent); +</pre> + +<p>인텐트에 추가된 {@link android.content.Intent#EXTRA_EMAIL} 추가 사항은 이메일이 전송되어야 할 이메일 주소의 + 문자열 배열입니다. 이메일 애플리케이션이 이 인텐트에 +응답하면, 애플리케이션은 추가 사항이 제공한 문자열 배열을 읽어낸 다음 이를 이메일 작성 양식의 + "수신" 필드에 배치합니다. 이 상황에서 이메일 애플리케이션의 액티비티가 시작되고 사용자가 + 작업을 끝내면 본인의 액티비티가 재개되는 것입니다.</p> + + + + +<h3 id="StartingAnActivityForResult">결과에 대한 액티비티 시작하기</h3> + +<p>때로는 시작한 액티비티에서 결과를 받고 싶을 수도 있습니다. 그런 경우, + ({@link android.app.Activity#startActivity + startActivity()} 대신) {@link android.app.Activity#startActivityForResult + startActivityForResult()}를 호출해서 액티비티를 시작합니다. 그런 다음 후속 액티비티에서 결과를 +받으려면, {@link android.app.Activity#onActivityResult onActivityResult()} 콜백 + 메서드를 구현합니다. 해당 후속 액티비티가 완료되면, 이것이 {@link +android.content.Intent} 형식으로 결과를 {@link android.app.Activity#onActivityResult onActivityResult()} + 메서드에 반환합니다.</p> + +<p>예를 들어 사용자가 연락처 중에서 하나를 고를 수 있도록 하고 싶을 수 있습니다. +즉 여러분의 액티비티가 해당 연락처의 정보로 무언가 할 수 있도록 하는 것입니다. 그와 같은 인텐트를 생성하고 결과를 처리하려면 +다음과 같이 하면 됩니다.</p> + +<pre> +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... + } + } +} +</pre> + +<p>이 예시는 액티비티 결과를 처리하기 위해 {@link +android.app.Activity#onActivityResult onActivityResult()} 메서드에서 사용해야 할 기본 논리를 + 나타낸 것입니다. 첫 번째 조건은 요청이 성공적인지 확인합니다. 요청에 성공했다면 +{@code resultCode}가 {@link android.app.Activity#RESULT_OK}가 됩니다. 또한, 이 결과가 응답하는 요청이 +알려져 있는지도 확인합니다. 이 경우에는 {@code requestCode}가 +{@link android.app.Activity#startActivityForResult +startActivityForResult()}와 함께 전송된 두 번째 매개변수와 일치합니다. 여기서부터 코드가 +{@link android.content.Intent}({@code data} 매개변수)로 반환된 데이터를 쿼리하여 액티비티 결과를 처리합니다.</p> + +<p>그러면 {@link +android.content.ContentResolver}가 콘텐츠 제공자에 대한 쿼리를 수행하고, 콘텐츠 제공자는 쿼리된 데이터를 읽을 수 있게 허용하는 +{@link android.database.Cursor}를 반환합니다. 자세한 내용은 +<a href="{@docRoot}guide/topics/providers/content-providers.html">콘텐츠 제공자</a> 문서를 참조하십시오.</p> + +<p>인텐트 사용에 관한 자세한 정보는 <a href="{@docRoot}guide/components/intents-filters.html">인텐트 및 인텐트 + 필터</a>문서를 참조하십시오.</p> + + +<h2 id="ShuttingDown">액티비티 종료하기</h2> + +<p>액티비티를 종료하려면 해당 액티비티의 {@link android.app.Activity#finish +finish()} 메서드를 호출하면 됩니다. +{@link android.app.Activity#finishActivity finishActivity()}를 호출하여 이전에 시작한 별도의 액티비티를 종료할 수도 있습니다.</p> + +<p class="note"><strong>참고:</strong> 대부분의 경우, 이와 같은 메서드를 사용하여 액티비티를 명시적으로 +종료해서는 안 됩니다. 다음 섹션에서 액티비티 수명 주기에 관해 논의한 바와 같이, +Android 시스템이 액티비티의 수명을 대신 관리해주므로 직접 액티비티를 종료하지 +않아도 됩니다. 이와 같은 메서드를 호출하면 예상되는 사용자 +환경에 부정적인 영향을 미칠 수 있으며, 따라서 사용자가 액티비티의 이 인스턴스에 돌아오는 것을 절대 바라지 않는 경우에만 +사용해야 합니다.</p> + + +<h2 id="Lifecycle">액티비티 수명 주기 관리하기</h2> + +<p>콜백 메서드를 구현하여 액티비티의 수명 주기를 관리하는 것은 +강력하고 유연한 애플리케이션 개발에 +대단히 중요한 역할을 합니다. 액티비티의 수명 주기는 다른 액티비티와의 관계, +액티비티의 작업과 백 스택 등에 직접적으로 영향을 받습니다.</p> + +<p>액티비티는 기본적으로 세 가지 상태로 존재할 수 있습니다.</p> + +<dl> + <dt><i>재개됨</i></dt> + <dd>액티비티가 화면 전경에 있으며 사용자의 초점이 맞춰져 있습니다 (이 상태를 때로는 +"실행 중"이라고 일컫기도 합니다).</dd> + + <dt><i>일시정지됨</i></dt> + <dd>다른 액티비티가 전경에 나와 있고 사용자의 시선을 집중시키고 있지만, 이 액티비티도 여전히 표시되어 있습니다. 다시 말해, +다른 액티비티가 이 액티비티 위에 표시되어 있으며 해당 액티비티는 부분적으로 투명하거나 +전체 화면을 덮지 않는 상태라는 뜻입니다. 일시정지된 액티비티는 완전히 살아있지만({@link android.app.Activity} +개체가 메모리에 보관되어 있고, 모든 상태 및 구성원 정보를 유지하며, +창 관리자에 붙어 있는 상태로 유지됨), 메모리가 극히 부족한 경우 시스템이 중단시킬 수 있습니다.</dd> + + <dt><i>정지됨</i></dt> + <dd>이 액티비티가 다른 액티비티에 완전히 가려진 상태입니다(액티비티가 이제 +"배경"에 위치함). 중단된 액티비티도 여전히 살아 있기는 마찬가지입니다({@link android.app.Activity} +개체가 메모리에 보관되어 있고, 모든 상태와 구성원 정보를 유지하시만 창 관리자에 붙어 있지 <em>않음</em> +). 그러나, 이는 더 이상 사용자에게 표시되지 않으며 +다른 곳에 메모리가 필요하면 시스템이 종료시킬 수 있습니다.</dd> +</dl> + +<p>액티비티가 일시정지 또는 중단된 상태이면, 시스템이 이를 메모리에서 삭제할 수 있습니다. 이러기 위해서는 +종료를 요청하거나({@link android.app.Activity#finish finish()} 메서드를 호출) 단순히 이 액티비티의 프로세스를 중단시키면 +됩니다. 액티비티가 다시 열릴 때에는(종료 또는 중단된 후에) 처음부터 다시 생성해야 +합니다.</p> + + + +<h3 id="ImplementingLifecycleCallbacks">수명 주기 콜백 구현하기</h3> + +<p>위에서 설명한 바와 같이 액티비티가 여러 가지 상태를 오가며 전환되면, 이와 같은 내용이 +여러 가지 콜백 메서드를 통해 통지됩니다. 콜백 메서드는 모두 후크로서, +액티비티 상태가 변경될 때 적절한 작업을 하기 위해 이를 재정의할 수 있습니다. 다음의 골격 +액티비티에는 기본 수명 주기 메서드가 각각 하나씩 포함되어 있습니다.</p> + + +<pre> +public class ExampleActivity extends Activity { + @Override + public void {@link android.app.Activity#onCreate onCreate}(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // The activity is being created. + } + @Override + protected void {@link android.app.Activity#onStart onStart()} { + super.onStart(); + // The activity is about to become visible. + } + @Override + protected void {@link android.app.Activity#onResume onResume()} { + super.onResume(); + // The activity has become visible (it is now "resumed"). + } + @Override + protected void {@link android.app.Activity#onPause onPause()} { + super.onPause(); + // Another activity is taking focus (this activity is about to be "paused"). + } + @Override + protected void {@link android.app.Activity#onStop onStop()} { + super.onStop(); + // The activity is no longer visible (it is now "stopped") + } + @Override + protected void {@link android.app.Activity#onDestroy onDestroy()} { + super.onDestroy(); + // The activity is about to be destroyed. + } +} +</pre> + +<p class="note"><strong>참고:</strong> 이와 같은 수명 주기 메서드를 구현하려면, 항상 + 슈퍼클래스 구현을 호출한 다음에만 다른 작업을 시작할 수 있습니다(위의 예시 참조).</p> + +<p>이와 같은 메서드를 모두 합쳐 한 액티비티의 수명 주기 전체를 정의합니다. 이러한 메서드를 구현함으로써 +액티비티 수명 주기 내의 세 가지 중첩된 루프를 모니터링할 수 있습니다. </p> + +<ul> +<li>한 액티비티의 <b>전체 수명</b>은 {@link +android.app.Activity#onCreate onCreate()} 호출과 {@link +android.app.Activity#onDestroy} 호출 사이를 말합니다. 액티비티는 {@link android.app.Activity#onCreate onCreate()}에서 +"전체" 상태(레이아웃 정의 등)의 설정을 수행한 다음 나머지 리소스를 +모두 {@link android.app.Activity#onDestroy}에 해제해야 합니다. 예를 들어, +액티비티에 네트워크에서 데이터를 다운로드하기 위해 배경에서 실행 중인 스레드가 있는 경우, 이는 +해당 스레드를 {@link android.app.Activity#onCreate onCreate()}에서 생성한 다음 {@link +android.app.Activity#onDestroy}에서 그 스레드를 중단할 수 있습니다.</li> + +<li><p>액티비티의 <b>가시적 수명</b>은 {@link +android.app.Activity#onStart onStart()} 호출에서 {@link +android.app.Activity#onStop onStop()} 호출 사이를 말합니다. 이 기간 중에는 사용자가 액티비티를 화면에서 보고 이와 +상호작용할 수 있습니다. 예컨대 {@link android.app.Activity#onStop onStop()}이 호출되어 + 새 액티비티가 시작되면 이 액티비티는 더 이상 표시되지 않게 됩니다. 이와 같은 두 가지 메서드 중에서 +사용자에게 액티비티를 표시하는 데 필요한 리소스를 유지하면 됩니다. 예를 들어, +{@link +android.app.Activity#onStart onStart()}에서 {@link android.content.BroadcastReceiver}를 등록하고 UI에 영향을 미치는 변화를 모니터링하고 +{@link android.app.Activity#onStop onStop()}에서 등록을 해제하면 사용자는 여러분이 무엇을 표시하고 있는지 더 이상 볼 수 +없게 됩니다. 시스템은 액티비티의 전체 수명 내내 {@link android.app.Activity#onStart onStart()} 및 {@link +android.app.Activity#onStop onStop()}을 여러 번 호출할 수 있으며, 이때 +액티비티는 사용자에게 표시되었다 숨겨지는 상태를 오가게 됩니다.</p></li> + +<li><p>액티비티의 <b>전경 수명</b>은 {@link +android.app.Activity#onResume onResume()} 호출에서 {@link android.app.Activity#onPause +onPause()} 호출 사이를 말합니다. 이 기간 중에는 이 액티비티가 화면에서 다른 모든 액티비티 앞에 표시되며 사용자 입력도 +여기에 집중됩니다. 액티비티는 전경에 나타났다 숨겨지는 전환을 자주 반복할 수 있습니다. 예를 들어 +, 기기가 절전 모드에 들어가거나 대화 상자가 +나타나면 {@link android.app.Activity#onPause onPause()}가 호출됩니다. 이 상태는 자주 전환될 수 있으므로, 이 두 가지 메서드의 코드는 +상당히 가벼워야 합니다. 그래야 전환이 느려 사용자를 기다리게 하는 일을 피할 수 있습니다.</p></li> +</ul> + +<p>그림 1은 액티비티가 상태 사이에서 취할 수 있는 이와 같은 루프와 경로를 나타낸 것입니다. +직사각형이 액티비티가 여러 상태 사이를 전환할 때 작업을 수행하도록 +구현할 수 있는 콜백 메서드를 나타냅니다. <p> + +<img src="{@docRoot}images/activity_lifecycle.png" alt="" /> +<p class="img-caption"><strong>그림 1.</strong> 액티비티 수명 주기입니다.</p> + +<p>같은 수명 주기 콜백 메서드가 표 1에 나열되어 있으며, 이 표는 각 콜백 +메서드를 더욱 상세하게 설명하며 액티비티의 전반적인 +수명 주기 내에서 각 메서드의 위치를 나타내기도 합니다. 여기에는 콜백 메서드가 완료된 다음 +시스템이 액티비티를 중단시킬 수 있는지 여부도 포함되어 있습니다.</p> + +<p class="table-caption"><strong>표 1.</strong> 액티비티 수명 주기 +콜백 메서드의 요약입니다.</p> + +<table border="2" width="85%" frame="hsides" rules="rows"> +<colgroup align="left" span="3"></colgroup> +<colgroup align="left"></colgroup> +<colgroup align="center"></colgroup> +<colgroup align="center"></colgroup> + +<thead> +<tr><th colspan="3">메서드</th> <th>설명</th> <th>완료 후 중단 가능?</th> <th>다음</th></tr> +</thead> + +<tbody> +<tr> + <td colspan="3" align="left"><code>{@link android.app.Activity#onCreate onCreate()}</code></td> + <td>액티비티가 처음 생성되었을 때 호출됩니다. + 이곳에서 일반적인 정적 설정을 모두 수행해야 합니다. +즉 보기 생성, 목록에 데이터 바인딩하기 등을 말합니다. 이 메서드에는 +액티비티의 이전 상태가 캡처된 경우 해당 상태를 포함한 +번들 개체가 전달됩니다(이 문서 나중에 나오는 <a href="#actstate">액티비티 상태 저장하기</a>를 참조하십시오 +). + <p>이 뒤에는 항상 {@code onStart()}가 따라옵니다.</p></td> + <td align="center">아니요</td> + <td align="center">{@code onStart()}</td> +</tr> + +<tr> + <td rowspan="5" style="border-left: none; border-right: none;"> </td> + <td colspan="2" align="left"><code>{@link android.app.Activity#onRestart +onRestart()}</code></td> + <td>액티비티가 중단되었다가 다시 시작되기 직전에 +호출됩니다. + <p>이 뒤에는 항상 {@code onStart()}가 따라옵니다.</p></td> + <td align="center">아니요</td> + <td align="center">{@code onStart()}</td> +</tr> + +<tr> + <td colspan="2" align="left"><code>{@link android.app.Activity#onStart onStart()}</code></td> + <td>액티비티가 사용자에게 표시되기 직전에 호출됩니다. + <p>액티비티가 전경으로 나오면 {@code onResume()}이 뒤에 따라오고, +액티비티가 숨겨지면 {@code onStop()}이 뒤에 따라옵니다.</p></td> + <td align="center">아니요</td> + <td align="center">{@code onResume()} <br/>또는<br/> {@code onStop()}</td> +</tr> + +<tr> + <td rowspan="2" style="border-left: none;"> </td> + <td align="left"><code>{@link android.app.Activity#onResume onResume()}</code></td> + <td>액티비티가 시작되고 사용자와 상호 작용하기 직전에 +호출됩니다. 이 시점에서 액티비티는 액티비티 +스택의 맨 위에 있으며, 사용자 입력이 입력되고 있습니다. + <p>이 뒤에는 항상 {@code onPause()}가 따라옵니다.</p></td> + <td align="center">아니요</td> + <td align="center">{@code onPause()}</td> +</tr> + +<tr> + <td align="left"><code>{@link android.app.Activity#onPause onPause()}</code></td> + <td>시스템이 다른 액티비티를 재개하기 직전에 +호출됩니다. 이 메서드는 일반적으로 데이터를 유지하기 위해 저장되지 않은 변경 사항을 +커밋하는 데, 애니메이션을 비롯하여 CPU를 소모하는 기타 작업을 중단하는 등 +여러 가지 용도에 사용됩니다. 이 메서드는 무슨 일을 하든 매우 빨리 끝내야 합니다. +이것이 반환될 때까지 다음 액티비티가 재개되지 않기 때문입니다. + <p>액티비티가 다시 전경으로 돌아오면 {@code onResume()}이 뒤에 따라오고 +액티비티가 사용자에게 보이지 않게 되면{@code onStop()}이 뒤에 따라옵니다. +</td> + <td align="center"><strong style="color:#800000">예</strong></td> + <td align="center">{@code onResume()} <br/>또는<br/> {@code onStop()}</td> +</tr> + +<tr> + <td colspan="2" align="left"><code>{@link android.app.Activity#onStop onStop()}</code></td> + <td>액티비티가 더 이상 사용자에게 표시되지 않게 되면 호출됩니다. 이것은 + 액티비티가 소멸되고 있기 때문에 일어날 수도 있고, 다른 액티비티 +(기존 것이든 새로운 것이든)가 재개되어 이것을 덮고 있기 때문일 수도 있습니다. + <p>액티비티가 다시 사용자와 상호 작용하면 +{@code onRestart()}가 뒤에 따라오고 액티비티가 사라지면 +{@code onDestroy()}가 뒤에 따라옵니다.</p></td> + <td align="center"><strong style="color:#800000">예</strong></td> + <td align="center">{@code onRestart()} <br/>또는<br/> {@code onDestroy()}</td> +</tr> + +<tr> + <td colspan="3" align="left"><code>{@link android.app.Activity#onDestroy +onDestroy()}</code></td> + <td>액티비티가 소멸되기 전에 호출됩니다. 이것이 액티비티가 받는 +마지막 호출입니다. 이것이 호출될 수 있는 경우는 액티비티가 +완료되는 중이라서(누군가가 여기에 <code>{@link android.app.Activity#finish + finish()}</code>를 호출해서)일 수도 있고, 시스템이 공간을 절약하기 위해 액티비티의 이 인스턴스를 일시적으로 소멸시키는 +중이기 때문일 수도 있습니다. 이와 같은 +두 가지 시나리오는 <code>{@link + android.app.Activity#isFinishing isFinishing()}</code> 메서드로 구분할 수 있습니다.</td> + <td align="center"><strong style="color:#800000">예</strong></td> + <td align="center"><em>없음</em></td> +</tr> +</tbody> +</table> + +<p>"완료 후 중단 가능?"이라는 레이블이 붙은 열은 + 시스템이 <em>메서드가 반환된 후</em> 액티비티 코드의 다른 줄을 실행하지 않고도 +언제든 이 액티비티를 호스팅하는 프로세스를 중단시킬 수 있는지 여부를 나타냅니다. 다음 세 가지 메서드가 "예"로 표시되어 있습니다({@link +android.app.Activity#onPause +onPause()}, {@link android.app.Activity#onStop onStop()} 및 {@link android.app.Activity#onDestroy +onDestroy()}). {@link android.app.Activity#onPause onPause()}가 세 가지 메서드 중에서 +첫 번째이므로, 액티비티가 생성되면 {@link android.app.Activity#onPause onPause()}는 프로세스가 <em>지워지기 전에</em> +반드시 호출되는 마지막 메서드입니다. +시스템이 비상 시에 메모리를 복구해야 할 경우, {@link +android.app.Activity#onStop onStop()}와 {@link android.app.Activity#onDestroy onDestroy()}는 +호출되지 않을 수도 있습니다. 따라서, 중요한 영구적 데이터(사용자 편집 등)를 보관하기 위해 작성하는 경우에는 {@link android.app.Activity#onPause onPause()}를 사용해야 +합니다. 그러나, {@link android.app.Activity#onPause onPause()} 중에 +어느 정보를 유지해야 할지는 조심해서 선택해야 합니다. 이 메서드에 차단 절차가 있으면 +다음 액티비티로의 전환을 차단하고 사용자 경험을 느려지게 할 수 있기 +때문입니다.</p> + +<p> <b>중단 가능한</b> 열에 "아니요"로 표시된 메서드는 액티비티를 호스팅하는 프로세스를 +보호하여 호출된 즉시 중단되지 않도록 방지합니다. 따라서 액티비티는 +{@link android.app.Activity#onPause onPause()}가 반환되는 시기부터 +{@link android.app.Activity#onResume onResume()}이 호출되는 시기 사이에 중단시킬 수 있습니다. 다시 중단시킬 수 있는 상태가 되려면 +{@link android.app.Activity#onPause onPause()}가 다시 호출되어 반환되어야 합니다. </p> + +<p class="note"><strong>참고:</strong> 표 1에 나타난 이런 정의에 +따르면 엄밀히 말해 "중단 가능한" 것이 아닌 액티비티라도 시스템이 중단시킬 수는 있습니다. 다만 이것은 다른 리소스가 없는 +극단적인 상황에서만 발생합니다. 액티비티가 중단될 수 있는 시기가 +언제인지는 <a href="{@docRoot}guide/components/processes-and-threads.html">프로세스 및 + 스레딩</a> 문서에서 보다 자세히 논의합니다.</p> + + +<h3 id="SavingActivityState">액티비티 상태 저장하기</h3> + +<p><a href="#Lifecycle">액티비티 수명 주기 관리하기</a> 도입부에서는 액티비티가 +일시중지되거나 +중단되었더라도 액티비티의 상태는 그대로 유지된다고 잠시 언급한 바 있습니다. 이것은 +{@link android.app.Activity} 개체가 일시중지되거나 중단된 경우에도 +메모리 안에 그대로 보관되었기 때문에 가능합니다. 즉 이 액티비티의 구성원과 현재 상태에 대한 모든 정보가 아직 살아 있다는 뜻입니다. 따라서, 사용자가 +액티비티 내에서 변경한 모든 내용도 그대로 유지되어 액티비티가 전경으로 +돌아갈 때("재개"될 때) 그와 같은 변경 사항도 그대로 존재하게 됩니다.</p> + +<p>그러나 시스템이 메모리를 복구하기 위해 액티비티를 소멸시키는 경우에는 {@link +android.app.Activity} 개체가 소멸되므로 시스템이 액티비티의 상태를 온전히 유지한 채로 간단하게 재개할 수 없게 +됩니다. 대신, 사용자가 다시 이 액티비티로 이동해 오면 시스템이 {@link android.app.Activity} 개체를 +다시 생성해야 합니다. 하지만, 사용자는 시스템이 +해당 액티비티를 소멸시켰다가 다시 생성했다는 것을 모릅니다. +따라서 액티비티가 예전과 똑같을 것이라고 예상할 것입니다. 이런 상황에서는 +액티비티 상태에 관한 정보를 저장할 수 있는 추가 콜백 메서드 +{@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}를 구현하여 액티비티 상태에 관한 중요한 정보를 보존할 수 있습니다.</p> + +<p>시스템이 {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} +를 호출한 다음에 액티비티를 소멸되기 쉽게 만듭니다. 시스템은 {@link +android.os.Bundle#putString putString()}와 {@link +android.os.Bundle#putInt putInt()} 같은 메서드를 사용하여, 이 메서드에 +액티비티에 관한 정보를 이름-값 쌍으로 저장할 수 있는 {@link android.os.Bundle}을 전달합니다. + 그리고 시스템이 애플리케이션 프로세스를 지우고 +사용자가 액티비티로 다시 돌아오면, 시스템이 액티비티를 다시 생성하고 +{@link android.os.Bundle}을 {@link android.app.Activity#onCreate onCreate()}와 {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()}에게 전달합니다. 이들 메서드 중 +하나를 사용하여 {@link android.os.Bundle}에서 저장된 상태를 추출하고 액티비티 상태 +를 복원할 수 있습니다. 복구할 상태 정보가 없는 경우, 여러분에게 전달되는 {@link +android.os.Bundle}은 null입니다(액티비티가 처음 생성되었을 때 이런 경우가 +발생합니다).</p> + +<img src="{@docRoot}images/fundamentals/restore_instance.png" alt="" /> +<p class="img-caption"><strong>그림 2.</strong> 액티비티의 상태가 온전한 채로 사용자의 +초점에 다시 돌아오는 데에는 두 가지 방식이 있습니다. 하나는 액티비티가 소멸되었다가 다시 생성되어 액티비티가 +이전에 저장된 상태를 복구해야 하는 경우, 다른 하나는 액티비티가 중단되었다가 재개되었으며 +액티비티 상태가 온전히 유지된 경우입니다.</p> + +<p class="note"><strong>참고:</strong> 상태를 저장할 필요가 없는 경우도 있으므로 액티비티가 소멸되기 전에 {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}가 호출된다는 보장은 없습니다 + +(예컨대 사용자가 +명시적으로 +액티비티를 닫기 위해 <em>뒤로</em> 버튼을 눌러서 액티비티를 떠날 때가 이에 해당합니다). 시스템이 {@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()}를 호출하는 경우, {@link +android.app.Activity#onStop onStop()} 전에 호출하는 것이 일반적이며 {@link android.app.Activity#onPause +onPause()} 전에 호출할 가능성도 높습니다.</p> + +<p>그러나 아무것도 하지 않고 {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}를 구현하지 않더라도, +{@link android.app.Activity} 클래스의 기본 구현 {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}가 일부 액티비티 상태를 복구합니다. 특히, 기본 +구현은 레이아웃에서 {@link +android.view.View}가 나올 때마다 해당하는 {@link +android.view.View#onSaveInstanceState onSaveInstanceState()} 메서드를 호출하고, 이 때문에 각 보기가 저장해야 하는 자체 관련 정보를 제공할 수 +있게 해줍니다. Android 프레임워크를 사용하는 위젯은 거의 대부분 이 메서드를 +필요에 따라 구현하므로, UI에 눈에 보이는 변경이 있으면 모두 자동으로 저장되며 액티비티를 다시 +생성하면 복구됩니다. 예를 들어, {@link android.widget.EditText} 위젯은 사용자가 입력한 모든 텍스트 +를 저장하고 {@link android.widget.CheckBox} 위젯은 확인 여부를 저장합니다. + 여러분이 해야 할 유일한 작업은 상태를 저장하고자 하는 각 위젯에 고유 ID(<a href="{@docRoot}guide/topics/resources/layout-resource.html#idvalue">{@code android:id}</a> +속성 포함)를 제공하는 것입니다. 위젯에 ID가 없으면 시스템이 그 위젯의 상태를 +저장할 수 없습니다.</p> + +<div class="sidebox-wrapper"> +<div class="sidebox"> +<p>또한, +{@link android.R.attr#saveEnabled android:saveEnabled} 속성을 {@code "false"}로 설정하거나 +{@link android.view.View#setSaveEnabled setSaveEnabled()} 메서드를 호출해서 레이아웃의 보기가 상태를 저장하지 못하도록 명시적으로 막을 수 있습니다. 보통은 이것을 비활성화해서는 +안 되지만, 액티비티 UI의 상태를 다르게 복구하고자 하는 경우 그렇게 할 수도 있습니다.</p> +</div> +</div> + +<p>{@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}의 기본 구현이 액티비티 UI의 유용한 정보를 저장하지만 +추가 정보를 저장하려면 이를 재설정해야 할 수도 있습니다. +예를 들어, 액티비티 수명에서 변경된 구성원 값을 변경해야 할 수도 있습니다( +UI에서 복구된 값과 상관관계가 있을 수 있지만 이런 UI 값을 보유한 구성원은 기본적으로 복구되지 않습니다 +).</p> + +<p>{@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}의 기본 구현이 UI 상태를 저장하는 데 도움이 되기 때문에, +추가 상태 정보를 저장하기 위해 이 메서드를 재정의하려면 +작업을 하기 전에 항상{@link android.app.Activity#onSaveInstanceState onSaveInstanceState()}의 슈퍼클래스 구현 +을 호출해야 합니다. 이와 마찬가지로 {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()}를 재정의하는 경우, 이것의 슈퍼클래스 구현을 호출해야 하기도 합니다. +이렇게 해야 기본 구현이 보기 상태를 복구할 수 있습니다.</p> + +<p class="note"><strong>참고:</strong> {@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()}의 호출이 보장되지 않으므로 + 이것은 액티비티의 일시적 상태(UI의 상태 +)를 기록하는 데에만 사용하고, 영구 데이터를 보관하는 데 사용해서는 안 됩니다. 대신, 사용자가 액티비티를 떠날 때 영구적인 데이터(데이터베이스에 저장되어야 +하는 데이터 등)를 저장하려면 {@link +android.app.Activity#onPause onPause()}를 사용해야 합니다.</p> + +<p>애플리케이션의 상태 저장 기능을 시험하는 좋은 방법은 기기를 회전해보고 화면 방향이 +바뀌는지 확인하는 것입니다. 화면 방향이 바뀌면 시스템은 액티비티를 +소멸시켰다가 다시 생성하여 새 화면 구성에서 이용할 수 있을지 모르는 대체 +리소스를 적용합니다. 이 이유 하나만으로도 액티비티가 다시 생성되었을 때 상태를 +완전히 복구할 수 있어야 한다는 점이 대단히 중요합니다. 사용자는 애플리케이션을 사용하면서 화면을 +자주 돌리기 때문입니다.</p> + + +<h3 id="ConfigurationChanges">구성 변경 처리하기</h3> + +<p>몇몇 기기 구성은 런타임 중에 변경될 수 있습니다(예: 화면 방향, 키보드 가용성 + 및 언어 등). 이러한 변경이 발생하면 Android는 실행 중인 액티비티를 다시 생성합니다 +(시스템이 {@link android.app.Activity#onDestroy}를 호출하고 즉시 {@link +android.app.Activity#onCreate onCreate()}를 호출합니다). 이런 동작은 +여러분이 제공한 대체 리소스로 애플리케이션을 자동으로 다시 로딩함으로써 새로운 구성에 애플리케이션이 적응하는 것을 돕도록 +설계되었습니다 +(예: 다양한 화면 방향과 크기에 대한 다양한 레이아웃).</p> + +<p>액티비티를 적절히 설계하여 화면 방향 변경으로 인한 재시작을 감당할 수 있으며 +위에 설명한 것처럼 액티비티 상태를 복구할 수 있도록 하면, 애플리케이션이 액티비티 수명 주기에서 +예기치 못한 이벤트가 일어나도 더욱 탄력적으로 복구될 수 있습니다.</p> + +<p>이러한 재시작을 처리하는 가장 좋은 방법은 이전 섹션에서 논의한 바와 같이 +{@link + android.app.Activity#onSaveInstanceState onSaveInstanceState()}와 {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()}(또는 {@link +android.app.Activity#onCreate onCreate()})를 사용하여 액티비티 상태를 저장하고 복구하는 것입니다.</p> + +<p>런타임에 발생하는 구성 변경과 그 처리 방법에 대한 자세한 정보는 +<a href="{@docRoot}guide/topics/resources/runtime-changes.html">런타임 변경 +처리하기</a>에 대한 가이드를 읽어보십시오.</p> + + + +<h3 id="CoordinatingActivities">액티비티 조정하기</h3> + + <p>한 액티비티가 다른 액티비티를 시작하면, 둘 모두 수명 주기 전환을 겪습니다. 첫 번째 액티비티는 +일시중지하고 중단되는 반면(배경에서 계속 보이는 경우에는 중단되지 않습니다만), 다른 액티비티는 +생성되는 것입니다. 이와 같은 액티비티가 디스크 또는 다른 속에 저장된 데이터를 공유하는 경우, +첫 번째 액티비티는 두 번째 액티비티가 생성되기 전에 완전히 중단되지 않는다는 점을 이해하는 것이 중요합니다. +그렇다기보다는, 두 번째 액티비티의 시작 과정이 첫 번째 액티비티 중단 과정과 겹쳐 일어납니다. +</p> + +<p>수명 주기 콜백은 분명히 정의된 순서가 있으며 특히 두 개의 액티비티가 +같은 프로세스 안에 있으면서 하나가 다른 하나를 시작하는 경우 순서가 더욱 확실합니다. 다음은 액티비티 A가 +액티비티 B를 시작할 때 발생하는 작업 순서입니다. </p> + +<ol> +<li>액티비티 A의 {@link android.app.Activity#onPause onPause()} 메서드가 실행됩니다.</li> + +<li>액티비티 B의 {@link android.app.Activity#onCreate onCreate()}, {@link +android.app.Activity#onStart onStart()}, {@link android.app.Activity#onResume onResume()} +메서드가 순차적으로 실행됩니다 (이제 사용자는 액티비티 B에 시선을 집중합니다).</li> + +<li>그런 다음, 액티비티 A가 더 이상 화면에 표시되지 않는 경우 이 액티비티의 {@link +android.app.Activity#onStop onStop()} 메서드가 실행됩니다.</li> +</ol> + + <p>이처럼 수명 주기 콜백의 순서를 예측할 수 있기 때문에 한 액티비티에서 다른 액티비티로 전환되는 + 정보를 관리할 수 있습니다. 예를 들어 첫 번째 액티비티가 중단될 때 데이터베이스에 +내용을 작성해서 다음 액티비티가 그 내용을 읽을 수 있도록 하려면, 데이터베이스에는 +{@link android.app.Activity#onPause onPause()} 중에({@link +android.app.Activity#onStop onStop()} 중이 아니라) 쓰기 작업을 해야 합니다.</p> + +<!-- +<h2>Beginner's Path</h2> + +<p>For more information about how Android maintains a history of activities and +enables user multitasking, continue with the <b><a +href="{@docRoot}guide/components/tasks-and-back-stack.html">Tasks and Back +Stack</a></b> document.</p> +--> diff --git a/docs/html-intl/intl/ko/guide/components/bound-services.jd b/docs/html-intl/intl/ko/guide/components/bound-services.jd new file mode 100644 index 000000000000..bf97b260a7da --- /dev/null +++ b/docs/html-intl/intl/ko/guide/components/bound-services.jd @@ -0,0 +1,658 @@ +page.title=바인딩된 서비스 +parent.title=서비스 +parent.link=services.html +@jd:body + + +<div id="qv-wrapper"> +<ol id="qv"> +<h2>이 문서의 내용</h2> +<ol> + <li><a href="#Basics">기본 정보</a></li> + <li><a href="#Creating">바인딩된 서비스 생성</a> + <ol> + <li><a href="#Binder">바인더 클래스 확장</a></li> + <li><a href="#Messenger">메신저 사용</a></li> + </ol> + </li> + <li><a href="#Binding">서비스에 바인딩</a></li> + <li><a href="#Lifecycle">바인딩된 서비스 수명 주기 관리</a></li> +</ol> + +<h2>Key 클래스</h2> +<ol> + <li>{@link android.app.Service}</li> + <li>{@link android.content.ServiceConnection}</li> + <li>{@link android.os.IBinder}</li> +</ol> + +<h2>샘플</h2> +<ol> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.html">{@code + RemoteService}</a></li> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalService.html">{@code + LocalService}</a></li> +</ol> + +<h2>참고 항목</h2> +<ol> + <li><a href="{@docRoot}guide/components/services.html">서비스</a></li> +</ol> +</div> + + +<p>바인딩된 서비스란 클라이언트 서버 인터페이스 안의 서버를 말합니다. 바인딩된 서비스를 사용하면 구성 요소(활동 등)를 +서비스에 바인딩되게 하거나, 요청을 보내고 응답을 수신하며 심지어는 +프로세스간 통신(IPC)까지 수행할 수 있게 됩니다. 일반적으로 바인딩된 서비스는 다른 애플리케이션 +구성 요소를 도울 때까지만 살고 배경에서 무한히 실행되지 않습니다.</p> + +<p>이 문서는 다른 애플리케이션 구성 요소의 +서비스에 바인딩하는 방법을 포함하여 바인딩된 서비스를 만드는 방법을 보여줍니다. 하지만 일반적인 서비스에 관한 정보도 알아두는 것이 좋습니다. +서비스에서 알림을 전달하는 방법이나 서비스를 전경에서 실행되도록 설정하는 방법 등 여러 가지 +추가 정보를 알아보려면 <a href="{@docRoot}guide/components/services.html">서비스</a> 문서를 참조하십시오.</p> + + +<h2 id="Basics">기본 정보</h2> + +<p>바인딩된 서비스란 일종의 {@link android.app.Service} 클래스 구현으로, +이를 통해 다른 애플리케이션이 이 서비스에 바인딩하여 상호 작용할 수 있도록 해주는 것입니다. 한 서비스에 대한 바인딩을 제공하려면, +{@link android.app.Service#onBind onBind()} 콜백 메서드를 구현해야 합니다. +이 메서드는 클라이언트가 서비스와 상호 작용하는 데 사용하는 프로그래밍 인터페이스를 정의하는 {@link android.os.IBinder} 개체를 +반환합니다.</p> + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h3>시작된 서비스에 바인딩</h3> + +<p><a href="{@docRoot}guide/components/services.html">서비스</a> +문서에서 논의된 바와 같이, 시작되었으면서도 바인딩된 서비스를 만들 수 있습니다. 다시 말해, +{@link android.content.Context#startService startService()}를 호출하여 서비스를 시작할 수 있으며 +이를 통해 서비스가 무한히 실행되도록 할 수 있으며, {@link +android.content.Context#bindService bindService()}를 호출하면 클라이언트가 해당 서비스에 바인딩되도록 할 수 있다는 것입니다. + <p>서비스를 시작되고 바인딩되도록 허용한 다음 서비스가 시작되면 +시스템은 클라이언트가 모두 바인딩을 해제해도 서비스를 소멸시키지 <em>않습니다</em>. 대신, +직접 서비스를 확실히 중단시켜야 합니다. 그러려면 {@link android.app.Service#stopSelf stopSelf()} 또는 {@link +android.content.Context#stopService stopService()}를 호출하면 됩니다.</p> + +<p>보통은 {@link android.app.Service#onBind onBind()} +<em>또는</em> {@link android.app.Service#onStartCommand onStartCommand()} +중 한 가지만 구현하지만, 둘 모두 구현해야 할 때도 있습니다. 예를 들어, 음악 플레이어의 경우 서비스를 무한히 실행하면서 +바인딩도 제공하도록 허용하는 것이 유용하다는 결론을 내릴 수 있습니다. 이렇게 하면, 한 액티비티가 서비스로 하여금 음악을 재생하도록 +시작한 다음 사용자가 애플리케이션을 떠나더라도 음악을 계속 재생하도록 할 수 있습니다. 그런 다음, 사용자가 애플리케이션으로 +다시 돌아오면 이 액티비티가 서비스를 바인딩하여 재생 제어권을 다시 획득할 수 있습니다.</p> + +<p><a href="#Lifecycle">바인딩된 서비스 수명 주기 관리 +</a> 관련 섹션을 꼭 읽어보십시오. 시작된 서비스에 바인딩을 +추가할 때 서비스 수명 주기에 관한 자세한 정보를 얻을 수 있습니다.</p> +</div> +</div> + +<p>클라이언트가 서비스에 바인딩하려면 {@link android.content.Context#bindService +bindService()}를 호출하면 됩니다. 이 때, 반드시 {@link +android.content.ServiceConnection}의 구현을 제공해야 하며 이것이 서비스와의 연결을 모니터링합니다. {@link +android.content.Context#bindService bindService()} 메서드는 값 없이 즉시 반환됩니다. +그러나 Android 시스템이 클라이언트와 서비스 사이의 +연결을 만드는 경우, 시스템은 {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()}를 {@link +android.content.ServiceConnection}에서 호출하여 클라이언트가 서비스와 통신하는 데 사용할 수 있도록 {@link android.os.IBinder}를 +전달하게 됩니다.</p> + +<p>여러 클라이언트가 한 번에 서비스에 연결될 수 있습니다. 그러나, 시스템이 서비스의 +{@link android.app.Service#onBind onBind()} 메서드를 호출하여 {@link android.os.IBinder}를 검색하는 경우는 첫 번째 클라이언트가 +바인딩되는 경우뿐입니다. 시스템은 그 후 같은 {@link android.os.IBinder}를 바인딩되는 추가 +클라이언트 모두에 전달하며 이때는 {@link android.app.Service#onBind onBind()}를 다시 호출하지 않습니다.</p> + +<p>마지막 클라이언트가 서비스에서 바인딩을 해제하면 시스템은 서비스를 소멸시킵니다( +{@link android.content.Context#startService startService()}가 서비스를 시작했을 경우 제외).</p> + +<p>바인딩된 서비스를 구현할 때 가장 중요한 부분은 +{@link android.app.Service#onBind onBind()} 콜백 메서드가 반환하는 인터페이스를 정의하는 것입니다. +서비스의 {@link android.os.IBinder} 인터페이스를 정의하는 방법에는 몇 가지가 있고, 다음 +섹션에서는 각 기법에 관해 논의합니다.</p> + + + +<h2 id="Creating">바인딩된 서비스 생성</h2> + +<p>바인딩을 제공하는 서비스를 생성할 때는 클라이언트가 서비스와 상호 작용하는 데 사용할 수 있는 프로그래밍 인터페이스를 제공하는 {@link android.os.IBinder} +를 제공해야 합니다. +인터페이스를 정의하는 방법은 세 가지가 있습니다.</p> + +<dl> + <dt><a href="#Binder">바인더 클래스 확장</a></dt> + <dd>서비스가 본인의 애플리케이션 전용이며 클라이언트와 같은 과정으로 실행되는 +경우(이런 경우가 흔함), 인터페이스를 생성할 때 {@link android.os.Binder} 클래스를 + 확장하고 그 인스턴스를 +{@link android.app.Service#onBind onBind()}에서 반환하는 방식을 사용해야 합니다. 클라이언트가 {@link android.os.Binder}를 받으며, +이를 사용하여 {@link android.os.Binder} 구현 또는 심지어 {@link android.app.Service}에서 +사용할 수 있는 공개 메서드에 직접 액세스할 수 있습니다. + <p>이것은 서비스가 본인의 애플리케이션을 위해 단순히 배경에서 작동하는 요소에 그치는 경우 +선호되는 기법입니다. 인터페이스를 생성할 때 이 방식을 사용하지 않는 유일한 이유는 +서비스를 다른 애플리케이션에서나 별도의 프로세스에 걸쳐 사용하고 있는 경우뿐입니다.</dd> + + <dt><a href="#Messenger">메신저 사용</a></dt> + <dd>인터페이스를 여러 프로세스에 걸쳐 적용되도록 해야 하는 경우, 서비스에 대한 +인터페이스를 {@link android.os.Messenger}로 생성할 수 있습니다. +이 방식을 사용하면 서비스가 여러 가지 유형의 {@link +android.os.Message} 개체에 응답하는 {@link android.os.Handler}를 정의합니다. 이 {@link android.os.Handler} +가 {@link android.os.Messenger}의 기초이며, 이를 통해 클라이언트와 함께 {@link android.os.IBinder} +를 공유할 수 있게 되어 클라이언트가 {@link +android.os.Message} 개체를 사용해 서비스에 명령을 보낼 수 있게 해줍니다. 이외에도, 클라이언트가 자체적으로 {@link android.os.Messenger}를 +정의하여 서비스가 메시지를 돌려보낼 수 있도록 할 수도 있습니다. + <p>이것이 프로세스간 통신(IPC)을 수행하는 가장 간단한 방법입니다. {@link +android.os.Messenger}가 모든 요청을 단일 스레드에 대기하게 해서, 서비스를 스레드로부터 안전하게 +설계하지 않아도 되기 때문입니다.</p> + </dd> + + <dt>AIDL 사용하기</dt> + <dd>AIDL(Android Interface Definition Language)은 개체를 운영 체제가 이해할 수 있는 +원시 데이터로 구성 해제한 다음 여러 프로세스에 걸쳐 집결하여 IPC를 수행하기 위해 +필요한 모든 작업을 수행합니다. 이전 기법은 {@link android.os.Messenger}를 사용했는데, +사실 그 기본 구조가 AIDL을 기반으로 하고 있는 것입니다. 위에서 언급한 바와 같이 {@link android.os.Messenger}는 단일 스레드에 모든 클라이언트 요청 +대기열을 생성하므로 서비스는 한 번에 요청을 하나씩 수신합니다. 그러나, +서비스가 동시에 여러 요청을 처리하도록 하고 싶은 경우에는 AIDL을 직접 사용해도 +됩니다. 이 경우, 서비스가 다중 스레딩을 할 수 있어야 하며 스레드로부터 안전하게 구축되었어야 합니다. + <p>AIDL을 직접 사용하려면 +프로그래밍 인터페이스를 정의하는 {@code .aidl} 파일을 생성해야 합니다. Android SDK 도구는 +이 파일을 사용하여 인터페이스를 구현하고 IPC를 처리하는 추상 클래스를 생성하고, +그러면 개발자가 직접 이것을 서비스 내에서 확장하면 됩니다.</p> + </dd> +</dl> + + <p class="note"><strong>참고:</strong> 대부분의 애플리케이션의 경우, +바인딩된 서비스를 생성하기 위해 AIDL를 사용해서는 <strong>안 됩니다</strong>. +그러려면 다중 스레딩 기능이 필요할 수 있고, 따라서 더 복잡한 구현을 초래할 수 있기 때문입니다. 따라서 AIDL은 +대부분의 애플리케이션에 적합하지 않으므로 이 문서에서는 여러분의 서비스에 이를 이용하는 방법에 대해 다루지 않습니다. AIDL을 직접 사용해야 한다는 확신이 드는 경우, +<a href="{@docRoot}guide/components/aidl.html">AIDL</a> 문서를 참조하십시오. +</p> + + + + +<h3 id="Binder">바인더 클래스 확장</h3> + +<p>서비스를 사용하는 것이 로컬 애플리케이션뿐이고 여러 프로세스에 걸쳐 작동할 필요가 없는 경우, +나름의 {@link android.os.Binder} 클래스를 구현하여 +클라이언트로 하여금 서비스 내의 공개 메서드에 직접 액세스할 수 있도록 할 수도 있습니다.</p> + +<p class="note"><strong>참고:</strong> 이것은 클라이언트와 서비스가 +같은 애플리케이션 및 프로세스에 있는 경우에만 통하며, 이 경우가 가장 보편적입니다. 이 방식이 잘 통하는 경우를 예로 들면, +음악 애플리케이션에서 자체 서비스에 액티비티를 바인딩하여 배경에서 음악을 재생하도록 해야 하는 +경우가 있습니다.</p> + +<p>이렇게 설정하는 방법은 다음과 같습니다.</p> +<ol> + <li>서비스에서 다음 중 한 가지에 해당하는 {@link android.os.Binder}의 인스턴스를 생성합니다. + <ul> + <li>클라이언트가 호출할 수 있는 공개 메서드 포함</li> + <li>클라이언트가 호출할 수 있는 공개 메서드가 있는 현재{@link android.app.Service} +인스턴스를 반환</li> + <li>클라이언트가 호출할 수 있는 공개 메서드가 포함된 서비스가 호스팅하는 다른 클래스의 인스턴스를 반환 +</li> + </ul> + <li>{@link +android.app.Service#onBind onBind()} 콜백 메서드에서 이 {@link android.os.Binder}의 인스턴스를 반환합니다.</li> + <li>클라이언트의 경우, {@link android.os.Binder}를 {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} +콜백 메서드에서 받아 제공된 메서드를 사용해 서비스를 바인딩하기 위해 호출합니다.</li> +</ol> + +<p class="note"><strong>참고:</strong> 서비스와 클라이언트가 같은 애플리케이션에 +있어야 하는 것은 그래야만 클라이언트가 반환된 개체를 캐스팅하여 해당 API를 적절하게 호출할 수 있기 때문입니다. 또한 +서비스와 클라이언트는 같은 프로세스에 있어야 하기도 합니다. 이 기법에서는 +여러 프로세스에 걸친 집결 작업을 전혀 수행하지 않기 때문입니다.</p> + +<p>예컨대, 어떤 서비스가 클라이언트에게 {@link android.os.Binder} 구현을 통해 서비스 내의 +메서드에 액세스할 수 있도록 한다고 합시다.</p> + +<pre> +public class LocalService extends Service { + // Binder given to clients + private final IBinder mBinder = new LocalBinder(); + // Random number generator + private final Random mGenerator = new Random(); + + /** + * Class used for the client Binder. Because we know this service always + * runs in the same process as its clients, we don't need to deal with IPC. + */ + public class LocalBinder extends Binder { + LocalService getService() { + // Return this instance of LocalService so clients can call public methods + return LocalService.this; + } + } + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + /** method for clients */ + public int getRandomNumber() { + return mGenerator.nextInt(100); + } +} +</pre> + +<p>{@code LocalBinder}는 클라이언트에게 {@code LocalService}의 현재 인스턴스를 검색하기 위한 {@code getService()} 메서드 +를 제공합니다. 이렇게 하면 클라이언트가 서비스 내의 공개 메서드를 호출할 수 있습니다. + 클라이언트는 예컨대 서비스에서 {@code getRandomNumber()}를 호출할 수 있습니다.</p> + +<p>다음은 버튼을 클릭했을 때 {@code LocalService}에 바인딩되며 {@code getRandomNumber()} +를 호출하는 액티비티를 나타낸 것입니다.</p> + +<pre> +public class BindingActivity extends Activity { + LocalService mService; + boolean mBound = false; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + } + + @Override + protected void onStart() { + super.onStart(); + // Bind to LocalService + Intent intent = new Intent(this, LocalService.class); + bindService(intent, mConnection, Context.BIND_AUTO_CREATE); + } + + @Override + protected void onStop() { + super.onStop(); + // Unbind from the service + if (mBound) { + unbindService(mConnection); + mBound = false; + } + } + + /** Called when a button is clicked (the button in the layout file attaches to + * this method with the android:onClick attribute) */ + public void onButtonClick(View v) { + if (mBound) { + // Call a method from the LocalService. + // However, if this call were something that might hang, then this request should + // occur in a separate thread to avoid slowing down the activity performance. + int num = mService.getRandomNumber(); + Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show(); + } + } + + /** Defines callbacks for service binding, passed to bindService() */ + private ServiceConnection mConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName className, + IBinder service) { + // We've bound to LocalService, cast the IBinder and get LocalService instance + LocalBinder binder = (LocalBinder) service; + mService = binder.getService(); + mBound = true; + } + + @Override + public void onServiceDisconnected(ComponentName arg0) { + mBound = false; + } + }; +} +</pre> + +<p>위 예시는 클라이언트가 +{@link android.content.ServiceConnection} 구현과 {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} 콜백을 사용하여 서비스에 바인딩하는 방법을 보여줍니다. 다음 +섹션에서는 서비스에 바인딩하는 이러한 과정에 대해 좀 더 자세한 정보를 제공합니다.</p> + +<p class="note"><strong>참고:</strong> 위 예시에서는 서비스에서 분명히 바인딩을 해제하지는 않습니다. +그러나 모든 클라이언트는 적절한 시점에 바인딩을 해제해야 합니다(액티비티가 일시 중지될 때 등).</p> + +<p>더 많은 샘플 코드를 보려면 <a href="{@docRoot}resources/samples/ApiDemos/index.html">ApiDemos</a>에서 <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalService.html">{@code +LocalService.java}</a> 클래스와 <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalServiceActivities.html">{@code +LocalServiceActivities.java}</a> 클래스를 참조하십시오.</p> + + + + + +<h3 id="Messenger">메신저 사용</h3> + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h4>AIDL과 비교</h4> + <p>IPC를 수행해야 할 경우, 인터페이스에 대해 {@link android.os.Messenger}를 사용하는 것이 +AIDL로 구현하는 것보다 간단합니다. 왜냐하면 {@link android.os.Messenger}는 +모든 호출을 서비스에 대기시키지만 순수한 AIDL 인터페이스는 +서비스에 동시에 요청을 전송하여 다중 스레딩을 처리해야 하기 때문입니다.</p> + <p>대부분의 애플리이션에서는 서비스가 다중 스레딩을 수행할 필요가 없으므로 {@link +android.os.Messenger}를 사용하면 호출을 한 번에 하나씩 처리할 수 있습니다. 서비스가 +다중 스레딩되는 것이 중요한 경우, 인터페이스를 정의하는 데 <a href="{@docRoot}guide/components/aidl.html">AIDL</a>을 사용해야 합니다.</p> +</div> +</div> + +<p>서비스가 원격 프로세스와 통신해야 한다면 서비스에 인터페이스를 제공하는 데 +{@link android.os.Messenger}를 사용하면 됩니다. 이 기법을 사용하면 +AIDL을 쓰지 않고도 프로세스간 통신(IPC)을 수행할 수 있게 해줍니다.</p> + +<p>다음은 {@link android.os.Messenger} 사용 방법을 간략하게 요약한 것입니다.</p> + +<ul> + <li>서비스가 클라이언트로부터 각 호출에 대해 콜백을 받는 {@link android.os.Handler}를 +구현합니다.</li> + <li>{@link android.os.Handler}를 사용하여 {@link android.os.Messenger} 개체를 생성합니다 +(이것은 {@link android.os.Handler}에 대한 참조입니다).</li> + <li>{@link android.os.Messenger}가 {@link android.os.IBinder}를 생성하여 서비스가 +{@link android.app.Service#onBind onBind()}로부터 클라이언트에게 반환하도록 합니다.</li> + <li>클라이언트는 {@link android.os.IBinder}를 사용하여 {@link android.os.Messenger} +(서비스의 {@link android.os.Handler}를 참조)를 인스턴트화하고, 이를 이용하여 +{@link android.os.Message} 개체를 서비스에 전송합니다.</li> + <li>서비스가 각 {@link android.os.Message}를 {@link +android.os.Handler}로 수신합니다. 구체적으로는 {@link android.os.Handler#handleMessage +handleMessage()} 메서드를 사용합니다.</li> +</ul> + + +<p>이렇게 하면, 클라이언트가 서비스에서 호출할 "메서드"가 없습니다. 대신 클라이언트는 +"메시지"({@link android.os.Message} 개체)를 전달하여 서비스가 +{@link android.os.Handler}로 받을 수 있도록 합니다.</p> + +<p>다음은 {@link android.os.Messenger} 인터페이스를 사용하는 간단한 예시 서비스입니다.</p> + +<pre> +public class MessengerService extends Service { + /** Command to the service to display a message */ + static final int MSG_SAY_HELLO = 1; + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_SAY_HELLO: + Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show(); + break; + default: + super.handleMessage(msg); + } + } + } + + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + final Messenger mMessenger = new Messenger(new IncomingHandler()); + + /** + * When binding to the service, we return an interface to our messenger + * for sending messages to the service. + */ + @Override + public IBinder onBind(Intent intent) { + Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show(); + return mMessenger.getBinder(); + } +} +</pre> + +<p>{@link android.os.Handler}의 {@link android.os.Handler#handleMessage handleMessage()} 메서드에서 +서비스가 수신되는 {@link android.os.Message}를 받고 +{@link android.os.Message#what} 구성원에 기초하여 무엇을 할지 결정한다는 점을 눈여겨 보십시오.</p> + +<p>클라이언트는 서비스가 반환한 {@link +android.os.IBinder}에 기초하여 {@link android.os.Messenger}를 생성하고 {@link +android.os.Messenger#send send()}로 메시지를 전송하기만 하면 됩니다. 예를 들어, 다음은 +서비스에 바인딩하여 {@code MSG_SAY_HELLO} 메시지를 서비스에 전달하는 간단한 액티비티입니다.</p> + +<pre> +public class ActivityMessenger extends Activity { + /** Messenger for communicating with the service. */ + Messenger mService = null; + + /** Flag indicating whether we have called bind on the service. */ + boolean mBound; + + /** + * Class for interacting with the main interface of the service. + */ + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + // This is called when the connection with the service has been + // established, giving us the object we can use to + // interact with the service. We are communicating with the + // service using a Messenger, so here we get a client-side + // representation of that from the raw IBinder object. + mService = new Messenger(service); + mBound = true; + } + + public void onServiceDisconnected(ComponentName className) { + // This is called when the connection with the service has been + // unexpectedly disconnected -- that is, its process crashed. + mService = null; + mBound = false; + } + }; + + public void sayHello(View v) { + if (!mBound) return; + // Create and send a message to the service, using a supported 'what' value + Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0); + try { + mService.send(msg); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + } + + @Override + protected void onStart() { + super.onStart(); + // Bind to the service + bindService(new Intent(this, MessengerService.class), mConnection, + Context.BIND_AUTO_CREATE); + } + + @Override + protected void onStop() { + super.onStop(); + // Unbind from the service + if (mBound) { + unbindService(mConnection); + mBound = false; + } + } +} +</pre> + +<p>이 예시에는 서비스가 클라이언트에 응답하는 방식이 나타나 있지 않다는 것을 유념하십시오. 서비스가 응답하게 하려면 + 클라이언트에도 {@link android.os.Messenger}를 생성해야 합니다. +클라이언트가 {@link android.content.ServiceConnection#onServiceConnected +onServiceConnected()} 콜백을 받으면 {@link android.os.Messenger#send send()}메서드의 {@link android.os.Message#replyTo} 매개변수에서 클라이언트의 {@link android.os.Messenger}를 포함하는 {@link android.os.Message}를 +서비스에 전송합니다. +</p> + +<p>양방향 메시지를 제공하는 방법의 예시는 <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/MessengerService.html">{@code +MessengerService.java}</a>(서비스) 및 <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/MessengerServiceActivities.html">{@code +MessengerServiceActivities.java}</a>(클라이언트) 예시를 참조하십시오.</p> + + + + + +<h2 id="Binding">서비스에 바인딩</h2> + +<p>애플리케이션 구성 요소(클라이언트)를 서비스에 바인딩하려면 +{@link android.content.Context#bindService bindService()}를 호출하면 됩니다. 그러면 Android +system이 서비스의 {@link android.app.Service#onBind +onBind()} 메서드를 호출하고, 이는 서비스와의 상호 작용을 위해 {@link android.os.IBinder}를 반환합니다.</p> + +<p>바인딩은 비동기식입니다. {@link android.content.Context#bindService +bindService()}는 즉시 반환하고 클라이언트에게 {@link android.os.IBinder}를 반환하지 <em>않습니다</em>. + {@link android.os.IBinder}를 수신하려면 클라이언트는 {@link +android.content.ServiceConnection}의 인스턴스를 생성하여 이를 {@link android.content.Context#bindService +bindService()}에 전달해야 합니다. {@link android.content.ServiceConnection}에는 +{@link android.os.IBinder}를 전달하기 위해 시스템이 호출하는 콜백 메서드가 포함됩니다.</p> + +<p class="note"><strong>참고:</strong> 서비스에 바인딩할 수 있는 것은 액티비티, 서비스 및 +콘텐츠 제공자뿐입니다. 브로드캐스트 수신자로부터는 서비스에 바인딩할 수 <strong>없습니다</strong>.</p> + +<p>따라서, 클라이언트로부터 서비스에 바인딩하려면 다음과 같이 해야 합니다. </p> +<ol> + <li>{@link android.content.ServiceConnection}을 구현합니다. + <p>이 구현으로 두 가지 콜백 메서드를 재정의해야 합니다.</p> + <dl> + <dt>{@link android.content.ServiceConnection#onServiceConnected onServiceConnected()}</dt> + <dd>시스템이 이것을 호출하여 서비스의 +{@link android.app.Service#onBind onBind()} 메서드가 반환한 {@link android.os.IBinder}를 전달합니다.</dd> + <dt>{@link android.content.ServiceConnection#onServiceDisconnected +onServiceDisconnected()}</dt> + <dd>Android 시스템이 이것을 호출하는 경우는 서비스로의 연결이 +예기치 못하게 끊어졌을 때, 즉 서비스가 충돌했거나 중단되었을 때 등입니다. +클라이언트가 바인딩을 해제한다고 이것이 호출되지는 <em>않습니다</em>.</dd> + </dl> + </li> + <li>{@link +android.content.Context#bindService bindService()}를 호출하고 {@link +android.content.ServiceConnection} 구현을 전달합니다. </li> + <li>시스템이 {@link android.content.ServiceConnection#onServiceConnected +onServiceConnected()} 콜백 메서드를 호출하면, 인터페이스가 정의한 메서드를 사용하여 +서비스에 호출을 시작해도 됩니다.</li> + <li>서비스로부터 연결을 해제하려면 {@link +android.content.Context#unbindService unbindService()}를 호출합니다. + <p>클라이언트가 소멸되면 이는 서비스에서 바인딩을 해제하게 되지만, +서비스와 상호 작용을 마쳤을 때 또는 액티비티가 일시 중지되었을 때에는 항상 바인딩을 해제해야 합니다. +이렇게 해야 서비스가 사용 중이 아닐 때에는 중지할 수 있습니다 +(바인딩과 바인딩 해제의 적절한 시기는 아래에서 좀 더 논의합니다).</p> + </li> +</ol> + +<p>예를 들어, 다음 코드 조각은 위와 같이 +<a href="#Binder">바인더 클래스를 확장해서</a> 생성한 서비스와 클라이언트를 연결합니다. 그러므로 이것이 해야 하는 일은 반환된 +{@link android.os.IBinder}를 {@code LocalService} 클래스에 캐스팅하고 {@code +LocalService} 인스턴스를 요청하는 것뿐입니다.</p> + +<pre> +LocalService mService; +private ServiceConnection mConnection = new ServiceConnection() { + // Called when the connection with the service is established + public void onServiceConnected(ComponentName className, IBinder service) { + // Because we have bound to an explicit + // service that is running in our own process, we can + // cast its IBinder to a concrete class and directly access it. + LocalBinder binder = (LocalBinder) service; + mService = binder.getService(); + mBound = true; + } + + // Called when the connection with the service disconnects unexpectedly + public void onServiceDisconnected(ComponentName className) { + Log.e(TAG, "onServiceDisconnected"); + mBound = false; + } +}; +</pre> + +<p>이 {@link android.content.ServiceConnection}이 있으면 클라이언트는 +이것을 {@link android.content.Context#bindService bindService()}에 전달하여 서비스에 바인딩할 수 있습니다. 예:</p> + +<pre> +Intent intent = new Intent(this, LocalService.class); +bindService(intent, mConnection, Context.BIND_AUTO_CREATE); +</pre> + +<ul> + <li>{@link android.content.Context#bindService bindService()}의 첫 번째 매개변수는 바인딩할 서비스를 명시적으로 명명하는 +{@link android.content.Intent}입니다(인텐트는 +암시적일 수 있음).</li> +<li>두 번째 매개변수는 {@link android.content.ServiceConnection} 개체입니다.</li> +<li>세 번째 매개변수는 바인딩 옵션을 나타내는 플래그입니다. 서비스를 생성하기 위해 보통은 {@link +android.content.Context#BIND_AUTO_CREATE}를 사용합니다(이미 살아 있는 상태가 아닌 경우). +가능한 기타 값은 {@link android.content.Context#BIND_DEBUG_UNBIND} + 및 {@link android.content.Context#BIND_NOT_FOREGROUND}, 또는 값이 없는 경우 {@code 0}입니다.</li> +</ul> + + +<h3>추가 참고 사항</h3> + +<p>다음은 서비스에 바인딩하는 데 관한 몇 가지 중요한 참고 사항입니다.</p> +<ul> + <li>항상 {@link android.os.DeadObjectException} 예외를 트래핑해야 합니다. +이것은 연결이 끊어지면 발생합니다. 원격 메서드에 의해 발생하는 예외는 이것뿐입니다.</li> + <li>개체는 여러 프로세스에 걸쳐 감안된 참조입니다. </li> + <li>일반적으로, 클라이언트의 수명 주기를 +결합하고 분해하는 순간을 일치시키면서 바인딩과 바인딩 해제를 페어링해야 합니다. 예: + <ul> + <li>액티비티가 눈에 보이는 동안에만 서비스와 상호 작용해야 한다면 +{@link android.app.Activity#onStart onStart()}에는 바인딩하고 {@link +android.app.Activity#onStop onStop()}에는 바인딩을 해제해야 합니다.</li> + <li> +배경에서 중단되었을 때도 액티비티가 응답을 받게 하고 싶다면 {@link android.app.Activity#onCreate onCreate()}에는 바인딩하고 +{@link android.app.Activity#onDestroy onDestroy()} 중에는 바인딩을 해제합니다. +이때, 사용자의 액티비티가 서비스가 실행되는 시간 전체에서(배경에서라도) 서비스를 사용한다는 것을 유념해야 합니다. +서비스가 다른 프로세스에 있을 경우, 사용자가 프로세스의 가중치를 높이면 시스템이 +이를 중단할 가능성이 높아집니다.</li> + </ul> + <p class="note"><strong>참고:</strong> 일반적으로는, 액티비티의 {@link android.app.Activity#onResume onResume()}와 {@link +android.app.Activity#onPause onPause()}에는 바인딩하거나 바인딩을 해제하지 <strong>말아야</strong> 합니다. 이러한 콜백은 모든 수명 주기 전환에서 발생하고 +이런 전환에서 발생하는 처리는 +최소한으로 유지해야 하기 때문입니다. 또한, +사용자 애플리케이션의 여러 액티비티가 동일한 서비스에 바인딩되었고 +두 액티비티 사이에 전환이 있을 경우, 현재 액티비티의 바인딩이 해제된 후(일시중지 중) 다음 액티비티가 바인딩하기 전(재개 중)에 +서비스가 제거되었다가 다시 생성될 수 있습니다 (수명 주기를 조절하기 위한 이러한 액티비티 전환은 +<a href="{@docRoot}guide/components/activities.html#CoordinatingActivities">액티비티</a> +문서에 설명되어 있습니다).</p> +</ul> + +<p>더 많은 샘플 코드를 보려면 <a href="{@docRoot}resources/samples/ApiDemos/index.html">ApiDemos</a>의 <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.html">{@code +RemoteService.java}</a> 클래스를 참조하십시오.</p> + + + + + +<h2 id="Lifecycle">바인딩된 서비스 수명 주기 관리</h2> + +<p>서비스가 모든 클라이언트로부터 바인딩 해제되면, Android 시스템이 이를 소멸시킵니다(다만 +{@link android.app.Service#onStartCommand onStartCommand()}와도 함께 시작된 경우는 예외). +따라서, 서비스가 순전히 바인딩된 서비스일 경우에는 해당 서비스의 수명 주기를 관리하지 않아도 됩니다. +클라이언트에 바인딩되었는지를 근거로 Android 시스템이 대신 관리해주기 때문입니다.</p> + +<p>그러나 {@link android.app.Service#onStartCommand +onStartCommand()} 콜백 메서드를 구현하기로 선택하는 경우라면 서비스를 확실히 중지해야 합니다. +서비스가 현재 <em>시작된 것</em>으로 간주되기 때문입니다. 이런 경우, 서비스는 클라이언트에 바인딩되었는지 여부와 관계없이 +{@link android.app.Service#stopSelf()}와 함께 스스로 중단되거나 다른 구성 요소가{@link +android.content.Context#stopService stopService()}를 호출할 때까지 실행됩니다. +</p> + +<p>또한, 서비스가 시작되고 바인딩을 허용할 경우 시스템이 +{@link android.app.Service#onUnbind onUnbind()} 메서드를 호출하면 +{@code true}를 선택적으로 반환할 수 있습니다. 다음에 클라이언트가 서비스에 바인딩할 때({@link +android.app.Service#onBind onBind()}에 대한 호출을 수신하지 않고) {@link android.app.Service#onRebind +onRebind()}에 대한 호출을 받을지 여부에 따라 결정됩니다. {@link android.app.Service#onRebind +onRebind()}가 void를 반환하였지만 클라이언트가 +{@link android.content.ServiceConnection#onServiceConnected onServiceConnected()} 콜백에서 {@link android.os.IBinder}를 여전히 수신합니다. +아래 그림 1은 이런 수명 주기의 논리를 나타냅니다.</p> + + +<img src="{@docRoot}images/fundamentals/service_binding_tree_lifecycle.png" alt="" /> +<p class="img-caption"><strong>그림 1.</strong> 시작되었으며 바인딩도 허용하는 서비스의 수명 주기입니다. +</p> + + +<p>시작된 서비스의 수명 주기에 관한 자세한 정보는 <a href="{@docRoot}guide/components/services.html#Lifecycle">서비스</a> 문서를 참조하십시오.</p> + + + + diff --git a/docs/html-intl/intl/ko/guide/components/fragments.jd b/docs/html-intl/intl/ko/guide/components/fragments.jd new file mode 100644 index 000000000000..a41250c38d9f --- /dev/null +++ b/docs/html-intl/intl/ko/guide/components/fragments.jd @@ -0,0 +1,812 @@ +page.title=프래그먼트 +parent.title=액티비티 +parent.link=activities.html +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + <h2>이 문서의 내용</h2> + <ol> + <li><a href="#Design">디자인 철학</a></li> + <li><a href="#Creating">프래그먼트 생성</a> + <ol> + <li><a href="#UI">사용자 인터페이스 추가하기</a></li> + <li><a href="#Adding">액티비티에 프래그먼트 추가</a></li> + </ol> + </li> + <li><a href="#Managing">프래그먼트 관리</a></li> + <li><a href="#Transactions">프래그먼트 트랜잭션 수행</a></li> + <li><a href="#CommunicatingWithActivity">액티비티와 통신</a> + <ol> + <li><a href="#EventCallbacks">액티비티로의 이벤트 콜백 생성</a></li> + <li><a href="#ActionBar">작업 모음에 항목 추가</a></li> + </ol> + </li> + <li><a href="#Lifecycle">프래그먼트 수명 주기 처리</a> + <ol> + <li><a href="#CoordinatingWithActivity">액티비티 수명 주기와 조화</a></li> + </ol> + </li> + <li><a href="#Example">예</a></li> + </ol> + + <h2>Key 클래스</h2> + <ol> + <li>{@link android.app.Fragment}</li> + <li>{@link android.app.FragmentManager}</li> + <li>{@link android.app.FragmentTransaction}</li> + </ol> + + <h2>참고 항목</h2> + <ol> + <li><a href="{@docRoot}training/basics/fragments/index.html">프래그먼트로 동적 UI 구축하기</a></li> + <li><a href="{@docRoot}guide/practices/tablets-and-handsets.html">태블릿 +및 핸드셋 지원</a></li> + </ol> +</div> +</div> + +<p>{@link android.app.Fragment}는 동작 또는 +{@link android.app.Activity} 내에서 사용자 인터페이스의 일부를 나타냅니다. 여러 개의 프래그먼트를 하나의 액티비티에 +조합하여 창이 여러 개인 UI를 구축할 수 있으며, 하나의 프래그먼트를 여러 액티비티에서 재사용할 수 있습니다. 프래그먼트는 자체 수명 주기를 가지고, 자체 입력 이벤트를 받으며, +액티비티 실행 중에 추가 및 제거가 가능한 액티비티의 모듈식 섹션이라고 +생각하면 됩니다(다른 액티비티에 +재사용할 수 있는 "하위 액티비티"와 같은 개념).</p> + +<p>프래그먼트는 항상 액티비티 내에 포함되어 있어야 하며 해당 프래그먼트의 수명 주기는 +호스트 액티비티의 수명 주기에 직접적으로 영향을 받습니다. 예를 들어 액티비티가 일시정지되는 경우, 그 안의 모든 프래그먼트도 +일시정지되며 액티비티가 소멸되면 모든 프래그먼트도 마찬가지로 소멸됩니다. 그러나 액티비티가 실행 중인 +동안에는(<em>재개됨</em> <a href="{@docRoot}guide/components/activities.html#Lifecycle">수명 주기 상태</a>에 있을 때를 말합니다) +각 프래그먼트를 추가 또는 제거하는 등 개별적으로 조작할 수 있습니다. 그와 같은 프래그먼트 트랜잭션을 +수행할 때에는 이를 액티비티가 관리하는 백 스택에도 +추가할 수 있습니다. 각 백 스택 항목이 발생한 프래그먼트 트랜잭션의 +기록이 됩니다. 이 백 스택을 사용하면 사용자가 프래그먼트 트랜잭션을 거꾸로 돌릴 수 있습니다(뒤로 이동). +이때 <em>뒤로</em> 버튼을 누르면 됩니다.</p> + +<p>프래그먼트를 액티비티 레이아웃의 일부로 추가하는 경우, 이는 액티비티의 보기 계층 내부의 {@link +android.view.ViewGroup} 안에 살며, 해당 프래그먼트가 자신의 보기 +레이아웃을 정의합니다. +프래그먼트를 액티비티 레이아웃에 삽입하려면 해당 프래그먼트를 +액티비티의 레이아웃 파일에서 {@code <fragment>} 요소로 선언하거나, 애플리케이션 코드에서 이를 +기존의 {@link android.view.ViewGroup}에 추가하면 됩니다. 그러나 프래그먼트가 +액티비티 레이아웃의 일부분이어야만 하는 것은 아닙니다. 나름의 UI가 없는 프래그먼트도 액티비티를 위한 +보이지 않는 작업자로 사용할 수 있습니다.</p> + +<p>이 문서에서는 프래그먼트를 사용하도록 애플리케이션을 구축하는 법을 +설명합니다. 그중에는 프래그먼트를 액티비티의 백 스택에 추가했을 때 프래그먼트가 자신의 상태를 유지하는 방법, +액티비티 및 액티비티 내의 다른 프래그먼트와 이벤트를 공유하는 방법과 액티비티의 +작업 모음에 참가하는 법 등등 여러 가지가 포함됩니다.</p> + + +<h2 id="Design">디자인 철학</h2> + +<p>Android가 프래그먼트를 처음 도입한 것은 Android 3.0(API 레벨 11)부터입니다. 기본적으로 +태블릿과 같은 큰 화면에서 보다 역동적이고 유연한 UI 디자인을 지원하는 것이 목적이었습니다. 태블릿의 화면은 +핸드셋 화면보다 훨씬 크기 때문에 UI 구성 요소를 조합하고 상호 교환할 공간이 +더 많습니다. 프래그먼트는 개발자가 보기 계층에 복잡한 변경 내용을 관리하지 않아도 +그러한 디자인을 사용할 수 있도록 해주는 것입니다. 한 액티비티의 레이아웃을 여러 프래그먼트로 나누면 +런타임에 액티비티의 외관을 수정할 수도 있고 그러한 변경 내용을 해당 액티비티가 관리하는 +백 스택에 보존할 수도 있습니다.</p> + +<p>예를 들어 뉴스 애플리케이션이라면 하나의 프래그먼트를 사용하여 +왼쪽에 기사 목록을 표시하도록 하고 또 다른 프래그먼트로 오른쪽에 기사 내용을 표시하도록 할 수 있습니다. 두 프래그먼트 모두 +한 액티비티에서 양쪽으로 나란히 나타나며, 각 프래그먼트에 나름의 수명 주기 콜백 메서드가 있고 +각자 사용자 입력 이벤트를 따로 처리하게 됩니다. 따라서, 사용자는 기사를 선택하는 데 한 액티비티를 쓰고 +기사를 읽는 데 또 다른 액티비티를 선택하는 대신에 같은 액티비티 안에서 기사를 선택하고 읽는 과정을 +모두 끝낼 수 있는 것입니다. 이것은 그림 1에 나타낸 태블릿 레이아웃과 같습니다.</p> + +<p>프래그먼트를 디자인할 때에는 각 프래그먼트를 모듈식이며 재사용 가능한 액티비티 구성 요소로 만들어야 합니다. +다시 말해, 각 프래그먼트가 나름의 레이아웃을 따로 정의하고 자기만의 수명 주기 콜백으로 자기 나름의 동작을 정의하기 때문에 +한 프래그먼트를 여러 액티비티에 포함시킬 수 있습니다. 그러므로 재사용을 염두에 두고 디자인하며 +한 프래그먼트를 또 다른 프래그먼트로부터 직접 조작하는 것은 삼가야 합니다. 이것은 특히 모듈식 프래그먼트를 사용하면 +프래그먼트 조합을 여러 가지 화면 크기에 맞춰 변경할 수 있도록 해주기 때문에 중요한 요점입니다. 태블릿과 핸드셋을 모두 지원하는 +애플리케이션을 디자인하는 경우, 사용 가능한 화면 공간을 토대로 사용자 경험을 최적화하도록 프래그먼트를 +여러 레이아웃 구성에 재사용할 수 있습니다. 예를 들어 핸드셋에서의 경우 +프래그먼트를 분리해서 단일 창 UI를 제공하도록 해야할 수 있습니다. 같은 액티비티 안에 하나 이상이 들어가지 않을 수 +있기 때문입니다.</p> + +<img src="{@docRoot}images/fundamentals/fragments.png" alt="" /> +<p class="img-caption"><strong>그림 1.</strong> 프래그먼트가 정의한 두 가지 UI 모듈이 +태블릿 디자인에서는 하나의 액티비티로 조합될 수 있는 반면 핸드셋 디자인에서는 분리될 수 있다는 것을 +예시로 나타낸 것입니다.</p> + +<p>예를 들어—뉴스 애플리케이션 예시를 계속 사용하겠습니다—이 애플리케이션을 태블릿 크기의 기기에서 실행하는 경우, +애플리케이션 내의 <em>액티비티 A</em> 안에 두 개의 프래그먼트를 포함시킬 수 있습니다. 그러나 +핸드셋 크기의 화면에서라면 두 프래그먼트를 모두 쓸 만큼 공간이 충분치 않습니다. +따라서 <em>액티비티 A</em>에는 기사 목록에 해당되는 프래그먼트만 포함하고, 사용자가 기사를 하나 선택하면 이것이 +<em>액티비티 B</em>를 시작합니다. 여기에 기사를 읽을 두 번째 프래그먼트가 포함되어 있습니다. 따라서 이 애플리케이션은 +서로 다른 조합으로 프래그먼트를 재사용함으로써 태블릿과 핸드셋을 둘 다 지원하는 +것입니다(그림 1 참조).</p> + +<p>여러 가지 화면 구성에 맞게 여러 가지 프래그먼트 조합으로 애플리케이션을 디자인하는 법에 대한 자세한 정보는 +<a href="{@docRoot}guide/practices/tablets-and-handsets.html">태블릿 및 핸드셋 지원</a>에 대한 가이드를 참조하십시오.</p> + + + +<h2 id="Creating">프래그먼트 생성</h2> + +<div class="figure" style="width:327px"> +<img src="{@docRoot}images/fragment_lifecycle.png" alt="" /> +<p class="img-caption"><strong>그림 2.</strong> 프래그먼트의 수명 주기( +소속 액티비티가 실행 중일 때).</p> +</div> + +<p>프래그먼트를 생성하려면 {@link android.app.Fragment}의 하위 클래스(또는 이의 기존 +하위 클래스)를 생성해야 합니다. {@link android.app.Fragment} 클래스에는 +{@link android.app.Activity}와 아주 유사해 보이는 코드가 있습니다. 여기에는 액티비티와 비슷한 콜백 메서드가 들어 있습니다. +예를 들어 {@link android.app.Fragment#onCreate onCreate()}, {@link android.app.Fragment#onStart onStart()}, +{@link android.app.Fragment#onPause onPause()} 및 {@link android.app.Fragment#onStop onStop()} 등입니다. 사실, +기존 Android 애플리케이션을 변환하여 프래그먼트를 사용하도록 하려면 그저 +액티비티의 콜백 메서드에서 프래그먼트에 해당되는 콜백 메서드로 코드를 옮기기만 하면 +될 수도 있습니다.</p> + +<p>보통은 최소한 다음과 같은 수명 주기 메서드를 구현해야 합니다.</p> + +<dl> + <dt>{@link android.app.Fragment#onCreate onCreate()}</dt> + <dd>시스템은 프래그먼트를 생성할 때 이것을 호출합니다. 구현 내에서 프래그먼트의 기본 구성 요소 중 +프래그먼트가 일시정지되거나 중단되었다가 재개되었을 때 유지하고자 하는 것을 +초기화해야 합니다.</dd> + <dt>{@link android.app.Fragment#onCreateView onCreateView()}</dt> + <dd>시스템은 프래그먼트가 자신의 사용자 인터페이스를 처음으로 그릴 시간이 되면 +이것을 호출합니다. 프래그먼트에 맞는 UI를 그리려면 메서드에서 {@link android.view.View}를 반환해야 합니다. +이 메서드는 프래그먼트 레이아웃의 루트입니다. 프래그먼트가 UI를 제공하지 않는 경우 null을 반환하면 +됩니다.</dd> + <dt>{@link android.app.Activity#onPause onPause()}</dt> + <dd>시스템이 이 메서드를 호출하는 것은 사용자가 프래그먼트를 떠난다는 +첫 번째 신호입니다(다만 이것이 항상 프래그먼트가 소멸 중이라는 뜻은 아닙니다). 현재 사용자 세션을 넘어서 +지속되어야 하는 변경 사항을 커밋하려면 보통 이곳에서 해아 합니다(사용자가 +돌아오지 않을 수 있기 때문입니다).</dd> +</dl> + +<p>대부분의 애플리케이션은 각각의 프래그먼트에 이와 같은 메서드를 최소한 세 개씩 +구현해야 하지만, 프래그먼트 수명 주기의 여러 단계를 처리하려면 사용해야 하는 다른 콜백 메서드도 +많이 있습니다. 모든 수명 주기 콜백 메서드는 나중에 +<a href="#Lifecycle">프래그먼트 수명 주기 처리</a> 섹션에서 더욱 상세히 논의할 것입니다.</p> + + +<p>이외에도, 기본적인 {@link +android.app.Fragment} 클래스 대신 확장하고자 하는 하위 클래스도 몇 개 있을 수 있습니다.</p> + +<dl> + <dt>{@link android.app.DialogFragment}</dt> + <dd>부동 대화 창을 표시합니다. 이 클래스를 사용하여 대화를 생성하면 +{@link android.app.Activity} 클래스의 대화 도우미 메서드를 사용하는 것의 +좋은 대안책이 됩니다. 이렇게 하면 프래그먼트 대화를 액티비티가 관리하는 프래그먼트의 백 스택에 통합시킬 수 있어, +사용자가 무시된 프래그먼트를 반환할 수 있도록 해주기 때문입니다.</dd> + + <dt>{@link android.app.ListFragment}</dt> + <dd>어댑터가 관리하는 항목의 목록(예: {@link +android.widget.SimpleCursorAdapter})을 표시하며, {@link android.app.ListActivity}와 비슷합니다. +이것은 목록 보기를 관리하는 데 쓰는 몇 가지 메서드를 제공합니다. 예를 들어 {@link +android.app.ListFragment#onListItemClick(ListView,View,int,long) onListItemClick()} 콜백을 +제공하여 클릭 이벤트를 처리하는 것 등입니다.</dd> + + <dt>{@link android.preference.PreferenceFragment}</dt> + <dd>{@link android.preference.Preference} 객체의 계층을 목록으로 표시하며, +{@link android.preference.PreferenceActivity}와 비슷합니다. 이것은 +애플리케이션에 대한 "설정" 액티비티를 생성할 때 유용합니다.</dd> +</dl> + + +<h3 id="UI">사용자 인터페이스 추가하기</h3> + +<p>프래그먼트는 일반적으로 액티비티에 속한 사용자 인터페이스의 일부분으로 사용되며 +자기 나름의 레이아웃으로 액티비티에 참가합니다.</p> + +<p>프래그먼트에 대해 레이아웃을 제공하려면 반드시 {@link +android.app.Fragment#onCreateView onCreateView()} 콜백 메서드를 구현해야 합니다. +이것은 프래그먼트가 자신의 레이아웃을 그릴 때가 되면 Android 시스템이 호출하는 것입니다. 이 메서드의 구현은 반드시 +{@link android.view.View}를 반환해야 합니다. 이것은 프래그먼트 레이아웃의 루트입니다.</p> + +<p class="note"><strong>참고:</strong> 프래그먼트가 {@link +android.app.ListFragment}의 하위 클래스인 경우, 기본 구현이 +{@link android.app.Fragment#onCreateView onCreateView()}로부터 {@link android.widget.ListView}를 반환하므로 이를 구현하지 않아도 됩니다.</p> + +<p>{@link +android.app.Fragment#onCreateView onCreateView()}로부터 레이아웃을 반환하려면 이를 XML에서 정의된 <a href="{@docRoot}guide/topics/resources/layout-resource.html">레이아웃 리소스</a>로부터 팽창시키면 됩니다. 이를 돕기 위해 +{@link android.app.Fragment#onCreateView onCreateView()}가 +{@link android.view.LayoutInflater} 객체를 제공합니다.</p> + +<p>예를 들어 다음은 {@link android.app.Fragment}의 하위 클래스입니다. 이것이 +{@code example_fragment.xml} 파일로부터 레이아웃을 로딩합니다.</p> + +<pre> +public static class ExampleFragment extends Fragment { + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.example_fragment, container, false); + } +} +</pre> + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h3>레이아웃 생성</h3> + <p>위의 샘플에서 {@code R.layout.example_fragment}는 +애플리케이션 리소스에 저장된 {@code example_fragment.xml}이라는 레이아웃 리소스에 대한 참조입니다. 레이아웃을 +XML로 생성하는 방법에 대한 정보는 <a href="{@docRoot}guide/topics/ui/index.html">사용자 인터페이스</a> + 문서를 참조하십시오.</p> +</div> +</div> + +<p>{@link android.app.Fragment#onCreateView +onCreateView()}로 전달된 {@code container} 매개변수가 상위 {@link android.view.ViewGroup}이며(액티비티의 레이아웃으로부터), +이 안에 프래그먼트 레이아웃이 삽입됩니다. + {@code savedInstanceState} 매개변수는 일종의 {@link android.os.Bundle}로, +이것은 프래그먼트가 재개되는 중인 경우 프래그먼트의 이전 인스턴스에 대한 데이터를 +제공합니다(상태를 복원하는 것은 <a href="#Lifecycle">프래그먼트 수명 주기 +처리</a>에서 좀 더 논의합니다).</p> + +<p>{@link android.view.LayoutInflater#inflate(int,ViewGroup,boolean) inflate()} 메서드는 +다음과 같은 세 개의 인수를 취합니다.</p> +<ul> + <li>팽창시키고자 하는 레이아웃의 리소스 ID.</li> + <li>팽창된 레이아웃의 상위가 될 {@link android.view.ViewGroup}. {@code +container}를 전달해야 시스템이 레이아웃 매개변수를 팽창된 레이아웃의 루트 보기에 실행 중인 상위 보기에서 지정한 것과 같이 +적용할 수 있으므로 이는 중요한 부분입니다.</li> + <li>팽창된 레이아웃이 팽창 중에 {@link +android.view.ViewGroup}(두 번째 매개변수)에 첨부되어야 하는지를 나타내는 부울 값 (이 경우, +이것은 거짓입니다. 시스템이 이미 팽창된 레이아웃을 {@code +container} 안에 삽입하고 있기 때문입니다. 참을 전달하면 최종 레이아웃에 중복된 보기 그룹을 생성하게 됩니다).</li> +</ul> + +<p>이제 레이아웃을 제공하는 프래그먼트 생성하는 법을 알게 되셨습니다. 다음은 프래그먼트를 +액티비티에 추가해야 합니다.</p> + + + +<h3 id="Adding">액티비티에 프래그먼트 추가</h3> + +<p>프래그먼트는 보통 UI의 일부분으로 호스트 액티비티에 참가합니다. 이는 해당 액티비티의 +전반적인 보기 계층의 일부분으로 포함되게 됩니다. 프래그먼트를 액티비티 레이아웃에 추가하는 데에는 두 가지 방법이 +있습니다.</p> + +<ul> + <li><b>프래그먼트를 액티비티의 레이아웃 파일 안에서 선언합니다.</b> +<p>이 경우, 프래그먼트에 대한 레이아웃 속성을 마치 +보기인 것처럼 나타낼 수 있습니다. 예를 들어 다음은 프래그먼트가 두 개 있는 +한 액티비티에 대한 레이아웃 파일을 나타낸 것입니다.</p> +<pre> +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <fragment android:name="com.example.news.ArticleListFragment" + android:id="@+id/list" + android:layout_weight="1" + android:layout_width="0dp" + android:layout_height="match_parent" /> + <fragment android:name="com.example.news.ArticleReaderFragment" + android:id="@+id/viewer" + android:layout_weight="2" + android:layout_width="0dp" + android:layout_height="match_parent" /> +</LinearLayout> +</pre> + <p>{@code <fragment>} 안의 {@code android:name} 속성이 레이아웃 안에서 인스턴트화할 {@link +android.app.Fragment} 클래스를 나타냅니다.</p> + +<p>시스템은 이 액티비티 레이아웃을 생성할 때 레이아웃에서 지정된 각 프래그먼트를 인스턴트화하며 각각에 대해 +{@link android.app.Fragment#onCreateView onCreateView()} 메서드를 +호출하여 각 프래그먼트의 레이아웃을 검색합니다. 시스템은 프래그먼트가 반환한 {@link android.view.View}를 +{@code <fragment>} 요소 자리에 곧바로 삽입합니다.</p> + +<div class="note"> + <p><strong>참고:</strong> 각 프래그먼트에는 액티비티가 재시작되는 경우 +프래그먼트를 복구하기 위해 시스템이 사용할 수 있는 고유한 식별자가 필요합니다(그리고, 개발자는 이것을 사용하여 프래그먼트를 캡처해 +이를 제거하는 등 여러 가지 트랜잭션을 수행할 수 있습니다). 프래그먼트에 ID를 제공하는 데에는 +다음과 같은 세 가지 방법이 있습니다.</p> + <ul> + <li>고유한 ID와 함께 {@code android:id} 속성을 제공합니다.</li> + <li>고유한 문자열과 함께 {@code android:tag} 속성을 제공합니다.</li> + <li>위의 두 가지 중 어느 것도 제공하지 않으면, 시스템은 컨테이너 보기의 ID를 +사용합니다.</li> + </ul> +</div> + </li> + + <li><b>또는, 프로그래밍 방식으로 프래그먼트를 기존의 {@link android.view.ViewGroup}에 추가합니다.</b> +<p>액티비티가 실행 중인 동안에는 언제든 액티비티 레이아웃에 프래그먼트를 추가할 수 있습니다. 그저 프래그먼트를 배치할 +{@link +android.view.ViewGroup}를 지정하기만 하면 됩니다.</p> + <p>액티비티 내에서 프래그먼트 트랜잭션을 수행하려면(프래그먼트 추가, 제거 또는 +교체 등), {@link android.app.FragmentTransaction}에서 가져온 API를 사용해야 합니다. +{@link android.app.FragmentTransaction}의 인스턴스를 {@link android.app.Activity}에서 가져오는 방법은 다음과 같습니다.</p> + +<pre> +FragmentManager fragmentManager = {@link android.app.Activity#getFragmentManager()} +FragmentTransaction fragmentTransaction = fragmentManager.{@link android.app.FragmentManager#beginTransaction()}; +</pre> + +<p>그런 다음 {@link +android.app.FragmentTransaction#add(int,Fragment) add()} 메서드를 사용하여 프래그먼트를 추가하고, 추가할 프래그먼트와 이를 삽입할 +보기를 지정하면 됩니다. 예:</p> + +<pre> +ExampleFragment fragment = new ExampleFragment(); +fragmentTransaction.add(R.id.fragment_container, fragment); +fragmentTransaction.commit(); +</pre> + + <p>{@link android.app.FragmentTransaction#add(int,Fragment) add()}에 +전달되는 첫 인수가 {@link android.view.ViewGroup}입니다. +여기에 프래그먼트가 리소스 ID가 지정한 바와 같이 배치되어야 하며, 두 번째 매개변수는 추가할 프래그먼트입니다.</p> + <p> +{@link android.app.FragmentTransaction}을 변경하고 나면, 반드시 +{@link android.app.FragmentTransaction#commit}을 호출해야 변경 내용이 적용됩니다.</p> + </li> +</ul> + + +<h4 id="AddingWithoutUI">UI 없이 프래그먼트 추가</h4> + +<p>위의 예시에서는 UI를 제공하기 위해 프래그먼트를 액티비티에 추가하는 방법을 보여드렸습니다. 하지만 +추가로 UI를 제시하지 않고 액티비티에 대한 배경 동작을 제공하는 데에도 프래그먼트를 사용할 수 +있습니다.</p> + +<p>UI 없이 프래그먼트를 추가하려면 액티비티로부터 가져온 프래그먼트를 {@link +android.app.FragmentTransaction#add(Fragment,String)}을 사용하여 추가합니다(이때, 프래그먼트에 대해 +보기 ID보다는 고유한 문자열 "태그"를 제공합니다). 이렇게 하면 프래그먼트가 추가되지만, +이것은 액티비티 레이아웃 안에 있는 보기와 연관되어 있지 않기 때문에 {@link +android.app.Fragment#onCreateView onCreateView()}로의 호출은 받지 않습니다. 따라서 그 메서드는 구현하지 않아도 됩니다.</p> + +<p>프래그먼트에 대해 문자열 태그를 제공하는 것은 엄밀히 말해 비 UI 프래그먼트에만 해당되는 것은 아닙니다. UI가 있는 +프래그먼트에도 문자열 태그를 제공할 수 있습니다. 하지만 프래그먼트에 +UI가 없는 경우라면 이를 식별할 방법은 문자열 태그뿐입니다. 액티비티에서 나중에 +프래그먼트를 가져오고자 하는 경우, {@link android.app.FragmentManager#findFragmentByTag +findFragmentByTag()}를 사용해야 합니다.</p> + +<p>예를 들어 어떤 액티비티에서 UI 없이 프래그먼트를 배경 작업자로 사용한다고 가정해봅시다. 이것의 예로 {@code +FragmentRetainInstance.java} 샘플을 들 수 있으며 +이는 SDK 샘플에 포함되어 있고(Android SDK Manager를 통해 이용 가능), 시스템에는 +<code><sdk_root>/APIDemos/app/src/main/java/com/example/android/apis/app/FragmentRetainInstance.java</code>로 찾을 수 있습니다.</p> + + + +<h2 id="Managing">프래그먼트 관리</h2> + +<p>액티비티 내의 프래그먼트를 관리하려면 {@link android.app.FragmentManager}를 사용해야 합니다. 이것을 +가져오려면 액티비티에서 {@link android.app.Activity#getFragmentManager()}를 호출하십시오.</p> + +<p>{@link android.app.FragmentManager}를 가지고 할 수 있는 여러 가지 일 중에 예를 들면 다음과 같습니다.</p> + +<ul> + <li>액티비티 내에 존재하는 프래그먼트를 {@link +android.app.FragmentManager#findFragmentById findFragmentById()}로 가져오기(액티비티 레이아웃 내에서 +UI를 제공하는 프래그먼트의 경우) 또는 {@link android.app.FragmentManager#findFragmentByTag +findFragmentByTag()}로 가져오기(UI를 제공하거나 하지 않는 프래그먼트의 경우).</li> + <li>백 스택에서 프래그먼트를 {@link +android.app.FragmentManager#popBackStack()}을 사용하여 튀어나오게 하기(사용자가 내린 <em>뒤로</em> 명령을 시뮬레이트함).</li> + <li>백 스택에 변경 내용이 있는지 알아보기 위해 {@link +android.app.FragmentManager#addOnBackStackChangedListener addOnBackStackChangedListener()}로 수신기 등록하기.</li> +</ul> + +<p>이와 같은 메서드와 그 외 다른 메서드에 대한 자세한 정보는 {@link +android.app.FragmentManager} 클래스 관련 문서를 참조하십시오.</p> + +<p>이전 섹션에서 설명한 바와 같이 {@link android.app.FragmentManager}를 +사용해서도 {@link android.app.FragmentTransaction} +을 열 수 있습니다. 이렇게 하면 프래그먼트 추가 및 제거 등 트랜잭션을 수행할 수 있게 해줍니다.</p> + + +<h2 id="Transactions">프래그먼트 트랜잭션 수행</h2> + +<p>액티비티에서 프래그먼트를 사용하는 경우 특히 유용한 점은 사용자 상호 작용에 응답하여 추가, +제거, 교체 및 다른 작업을 수행할 수 있다는 것입니다. 액티비티에 적용한 +변경 내용의 집합을 하나의 트랜잭션이라 칭합니다. 이것을 수행하려면 {@link +android.app.FragmentTransaction} 내의 API를 사용하면 됩니다. 해당 액티비티가 관리하는 백 스택에 행해진 각 트랜잭션을 +저장할 수도 있습니다. 이렇게 하면 사용자가 프래그먼트 변경 내역을 거쳐 뒤로 탐색할 수 있습니다(액티비티를 통과해 +뒤로 탐색하는 것과 비슷합니다).</p> + +<p>{@link android.app.FragmentTransaction}의 인스턴스를 {@link +android.app.FragmentManager}로부터 가져오는 방법은 다음과 같습니다.</p> + +<pre> +FragmentManager fragmentManager = {@link android.app.Activity#getFragmentManager()}; +FragmentTransaction fragmentTransaction = fragmentManager.{@link android.app.FragmentManager#beginTransaction()}; +</pre> + +<p>각 트랜잭션은 동시에 수행하고자 하는 여러 변경을 집합적으로 일컫는 말입니다. 주어진 +트랜잭션에 대해 수행하고자 하는 모든 변경 사항을 설정하려면 {@link +android.app.FragmentTransaction#add add()}, {@link android.app.FragmentTransaction#remove remove()}, +및 {@link android.app.FragmentTransaction#replace replace()}와 같은 메서드를 사용하면 됩니다. 그런 다음, +트랜잭션을 액티비티에 적용하려면 반드시 {@link android.app.FragmentTransaction#commit()}을 호출해야 합니다.</p> +</dl> + +<p>하지만 {@link +android.app.FragmentTransaction#commit()}을 호출하기 전에 먼저 호출해야 할 것이 있습니다. 바로 {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()}입니다. +이렇게 해야 트랜잭션을 프래그먼트 트랜잭션의 백 스택에 추가할 수 있습니다. 이 백 스택을 액티비티가 관리하며, +이를 통해 사용자가 이전 프래그먼트 상태로 되돌아갈 수 있습니다. 이때 <em>뒤로</em> 버튼을 누르면 됩니다.</p> + +<p>예를 들어 다음은 한 프래그먼트를 다른 것으로 교체하고 이전 상태를 백 스택에 보존하는 법을 +나타낸 것입니다.</p> + +<pre> +// Create new fragment and transaction +Fragment newFragment = new ExampleFragment(); +FragmentTransaction transaction = getFragmentManager().beginTransaction(); + +// Replace whatever is in the fragment_container view with this fragment, +// and add the transaction to the back stack +transaction.replace(R.id.fragment_container, newFragment); +transaction.addToBackStack(null); + +// Commit the transaction +transaction.commit(); +</pre> + +<p>이 예시에서 {@code newFragment}가 현재 레이아웃 컨테이너에 있는 +프래그먼트(있는 경우)를 교체합니다. 이는 {@code R.id.fragment_container} ID로 식별할 수 있습니다. {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()}를 호출하면 교체 트랜잭션이 +백 스택에 저장되고, 따라서 사용자가 트랜잭션을 거꾸로 수행하여 +이전 프래그먼트를 도로 가져올 수 있습니다. <em>뒤로</em> 버튼을 사용하면 됩니다.</p> + +<p>트랜잭션에 여러 개의 변경을 추가하고(예를 들어 또 다른 {@link +android.app.FragmentTransaction#add add()} 또는 {@link android.app.FragmentTransaction#remove +remove()}) {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()}을 호출하면, {@link android.app.FragmentTransaction#commit commit()}을 호출하기 전에 적용된 모든 변경 내용이 +백 스택에 하나의 트랜잭션으로 추가되며, <em>뒤로</em> 버튼을 누르면 +모두 한꺼번에 역행하게 됩니다.</p> + +<p>{@link android.app.FragmentTransaction}에 변경 내용을 추가하는 순서는 중요하지 않습니다. +다만 다음과 같은 예외가 있습니다.</p> +<ul> + <li>{@link android.app.FragmentTransaction#commit()}을 마지막으로 호출해야만 합니다.</li> + <li>같은 컨테이너에 여러 개의 프래그먼트를 추가하는 경우, 이를 추가하는 순서가 이들이 +보기 계층에 나타나는 순서를 결정 짓습니다.</li> +</ul> + +<p>프래그먼트를 제거하는 트랜잭션을 수행하면서 {@link android.app.FragmentTransaction#addToBackStack(String) +addToBackStack()}을 호출하지 않는 경우, +해당 프래그먼트는 트랜잭션이 적용되면 소멸되고 사용자가 이를 되짚어 탐색할 수 없게 됩니다. 반면에 +프래그먼트를 제거하면서 {@link android.app.FragmentTransaction#addToBackStack(String) addToBackStack()}을 호출하면, +해당 프래그먼트는 <em>중단</em>되고 사용자가 뒤로 탐색하면 +재개됩니다.</p> + +<p class="note"><strong>팁:</strong> 각 프래그먼트 트랜잭션에 대해 전환 애니메이션을 적용하려면 +커밋하기 전에 {@link android.app.FragmentTransaction#setTransition setTransition()}을 +호출하면 됩니다.</p> + +<p>{@link android.app.FragmentTransaction#commit()}을 호출해도 그 즉시 트랜잭션을 수행하지는 +않습니다. 그보다는, 액티비티의 UI 스레드("주요" 스레드)를 스레드가 할 수 있는 한 빨리 +이 트랜잭션을 수행하도록 일정을 예약하는 것에 가깝습니다. 하지만 필요한 경우 UI 스레드로부터 {@link +android.app.FragmentManager#executePendingTransactions()}를 호출하면 +{@link android.app.FragmentTransaction#commit()}이 제출한 트랜잭션을 즉시 실행할 수 있습니다. 트랜잭션이 +다른 스레드의 작업에 대한 종속성이 아니라면 굳이 이렇게 해야만 하는 것은 아닙니다.</p> + +<p class="caution"><strong>주의:</strong> 트랜잭션을 적용할 때 {@link +android.app.FragmentTransaction#commit commit()}을 사용해도 되는 것은 액티비티가 <a href="{@docRoot}guide/components/activities.html#SavingActivityState">그 상태를 +저장</a>하기 전뿐입니다(사용자가 액티비티를 떠날 때). 그 시점 이후에 적용하려고 하면 예외가 +발생합니다. 이것은 액티비티를 복원해야 하는 경우 적용 이후의 상태가 손실될 수 +있기 때문입니다. 적용이 손실되어도 괜찮은 상황이라면, {@link +android.app.FragmentTransaction#commitAllowingStateLoss()}를 사용하십시오.</p> + + + + +<h2 id="CommunicatingWithActivity">액티비티와 통신</h2> + +<p>{@link android.app.Fragment}는 +{@link android.app.Activity}로부터 독립적인 객체로 구현되었고 여러 개의 액티비티 안에서 사용할 수 있는 것이 사실이지만, +프래그먼트의 주어진 인스턴스는 그것을 포함하고 있는 액티비티에 직접적으로 연결되어 있습니다.</p> + +<p>구체적으로 말하면, 이 프래그먼트는 {@link +android.app.Fragment#getActivity()}를 사용하여 {@link android.app.Activity} 인스턴스에 액세스하여 +액티비티 레이아웃에서 보기를 찾는 것과 같은 작업을 손쉽게 수행할 수 있습니다.</p> + +<pre> +View listView = {@link android.app.Fragment#getActivity()}.{@link android.app.Activity#findViewById findViewById}(R.id.list); +</pre> + +<p>이와 마찬가지로, 액티비티도 프래그먼트 안의 메서드를 호출할 수 있습니다. 그러려면 {@link android.app.FragmentManager}로부터의 +{@link android.app.Fragment}에 대한 참조를 가져와야 하며, 이때 {@link +android.app.FragmentManager#findFragmentById findFragmentById()} 또는 {@link +android.app.FragmentManager#findFragmentByTag findFragmentByTag()}를 사용합니다. 예:</p> + +<pre> +ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment); +</pre> + + +<h3 id="EventCallbacks">액티비티로의 이벤트 콜백 생성</h3> + +<p>어떤 경우에는 프래그먼트로 하여금 액티비티와 이벤트를 공유하게 해야 할 수 있습니다. 이렇게 하기 위한 +한 가지 좋은 방법은 프래그먼트 내부의 콜백 인터페이스를 정의한 다음 해당 호스트 액티비티가 이를 구현하도록 +하는 것입니다. 액티비티가 인터페이스를 통해 콜백을 수신하면, 필요에 따라 그 정보를 레이아웃 내의 +다른 프래그먼트와 공유할 수 있습니다.</p> + +<p>예를 들어 어떤 뉴스 애플리케이션에서 액티비티 하나에 프래그먼트가 두 개 있습니다. + 하나는 기사 목록을 표시(프래그먼트 A)하고 다른 하나는 기사 하나를 표시(프래그먼트 B)하는 경우 목록 항목이 선택되면 +프래그먼트 A가 액티비티에 알려야 프래그먼트 B에 해당 기사를 표시하라고 알릴 수 있습니다. 이 경우, +{@code OnArticleSelectedListener} 인터페이스는 프래그먼트 A 내부에 선언됩니다.</p> + +<pre> +public static class FragmentA extends ListFragment { + ... + // Container Activity must implement this interface + public interface OnArticleSelectedListener { + public void onArticleSelected(Uri articleUri); + } + ... +} +</pre> + +<p>그러면 프래그먼트를 호스팅하는 액티비티가 {@code OnArticleSelectedListener} + 인터페이스를 +구현하고 {@code onArticleSelected()}를 재정의하여 프래그먼트 A로부터 일어난 이벤트를 +프래그먼트 B에 알립니다. 호스트 액티비티가 이 인터페이스를 구현하도록 +확실히 하려면 프래그먼트 A의 {@link +android.app.Fragment#onAttach onAttach()} 콜백 메서드(프래그먼트를 액티비티에 추가할 때 시스템이 호출하는 것)가 {@code OnArticleSelectedListener}의 인스턴스를 인스턴트화해야 합니다. 이때 {@link android.app.Fragment#onAttach +onAttach()} 안으로 전달된 {@link android.app.Activity}를 +캐스팅하는 방법을 씁니다.</p> + +<pre> +public static class FragmentA extends ListFragment { + OnArticleSelectedListener mListener; + ... + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + try { + mListener = (OnArticleSelectedListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener"); + } + } + ... +} +</pre> + +<p>액티비티가 인터페이스를 구현하지 않은 경우, 프래그먼트가 +{@link java.lang.ClassCastException}을 발생시킵니다. +성공 시, {@code mListener} 구성원이 액티비티의 +{@code OnArticleSelectedListener} 구현에 대한 참조를 보유하므로, 프래그먼트 A가 액티비티와 이벤트를 공유할 수 있습니다. +이때 {@code OnArticleSelectedListener} 인터페이스가 정의한 메서드를 호출하는 방법을 씁니다. 예를 들어 프래그먼트 A가 +{@link android.app.ListFragment}의 확장인 경우, +사용자가 목록 항목을 클릭할 때마다 시스템이 프래그먼트 안의 {@link android.app.ListFragment#onListItemClick +onListItemClick()}을 호출하고, 그러면 이것이 {@code onArticleSelected()}를 호출하여 +해당 이벤트를 액티비티와 공유하는 것입니다.</p> + +<pre> +public static class FragmentA extends ListFragment { + OnArticleSelectedListener mListener; + ... + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + // Append the clicked item's row ID with the content provider Uri + Uri noteUri = ContentUris.{@link android.content.ContentUris#withAppendedId withAppendedId}(ArticleColumns.CONTENT_URI, id); + // Send the event and Uri to the host activity + mListener.onArticleSelected(noteUri); + } + ... +} +</pre> + +<p>{@link +android.app.ListFragment#onListItemClick onListItemClick()}에 전달된 {@code id} 매개변수가 클릭한 항목의 행 ID이며, +액티비티(또는 다른 프래그먼트)가 이것을 사용해 애플리케이션의 {@link +android.content.ContentProvider}에서 기사를 가져옵니다.</p> + +<p><!--To see a complete implementation of this kind of callback interface, see the <a +href="{@docRoot}resources/samples/NotePad/index.html">NotePad sample</a>. -->콘텐츠 제공자 사용법에 대한 자세한 정보는 +<a href="{@docRoot}guide/topics/providers/content-providers.html">콘텐츠 제공자</a> 문서에서 이용하실 수 있습니다.</p> + + + +<h3 id="ActionBar">작업 모음에 항목 추가</h3> + +<p>프래그먼트는 액티비티의 <a href="{@docRoot}guide/topics/ui/menus.html#options-menu">옵션 메뉴</a>에(결과적으로 <a href="{@docRoot}guide/topics/ui/actionbar.html">작업 모음</a>에도) 메뉴 항목으로 참가할 수 있습니다. 이때 +{@link android.app.Fragment#onCreateOptionsMenu(Menu,MenuInflater) onCreateOptionsMenu()}를 구현하는 방법을 씁니다. 이 메서드가 +호출을 수신하도록 하려면, {@link +android.app.Fragment#onCreate(Bundle) onCreate()} 중에 {@link +android.app.Fragment#setHasOptionsMenu(boolean) setHasOptionsMenu()}를 호출하여 프래그먼트가 +옵션 메뉴에 항목을 추가하고자 한다는 것을 나타내야 합니다(그렇지 않으면 해당 프래그먼트가 +{@link android.app.Fragment#onCreateOptionsMenu onCreateOptionsMenu()}로의 호출을 받지 못하게 됩니다).</p> + +<p>그런 다음 프래그먼트로부터 옵션 메뉴에 추가하는 모든 항목은 기존의 메뉴 항목에 +추가됩니다. 해당 프래그먼트는 메뉴 항목을 선택하면 {@link +android.app.Fragment#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}로의 콜백도 +수신하게 됩니다.</p> + +<p>또한 프래그먼트 레이아웃에 보기를 등록하여 컨텍스트 메뉴를 제공하도록 할 수도 있습니다. 이때 {@link +android.app.Fragment#registerForContextMenu(View) registerForContextMenu()}를 호출하면 됩니다. 사용자가 컨텍스트 메뉴를 열면, +해당 프래그먼트가 {@link +android.app.Fragment#onCreateContextMenu(ContextMenu,View,ContextMenu.ContextMenuInfo) +onCreateContextMenu()}로의 호출을 받습니다. 사용자가 항목을 하나 선택하면, 해당 프래그먼트는 {@link +android.app.Fragment#onContextItemSelected(MenuItem) onContextItemSelected()}로의 호출을 받습니다.</p> + +<p class="note"><strong>참고:</strong> 프래그먼트는 추가한 각 메뉴 항목에 대해 '항목 선택됨' 콜백을 +하나씩 받게 되지만, 사용자가 메뉴 항목을 선택할 때 그에 상응하는 콜백을 가장 처음 받는 것은 +액티비티입니다. 액티비티가 구현한 '항목 선택됨' 콜백이 선택된 항목을 다루지 않는 경우, +해당 이벤트는 프래그먼트의 콜백으로 전달됩니다. 이것은 +옵션 메뉴와 컨텍스트 메뉴에 모두 참입니다.</p> + +<p>메뉴에 대한 더 자세한 정보는 <a href="{@docRoot}guide/topics/ui/menus.html">메뉴</a> 및 <a href="{@docRoot}guide/topics/ui/actionbar.html">작업 모음</a> 개발자 가이드를 참조하십시오.</p> + + + + +<h2 id="Lifecycle">프래그먼트 수명 주기 처리</h2> + +<div class="figure" style="width:350px"> +<img src="{@docRoot}images/activity_fragment_lifecycle.png" alt="" /> +<p class="img-caption"><strong>그림 3.</strong> 액티비티 수명 주기가 프래그먼트 수명 주기에 미치는 +영향입니다.</p> +</div> + +<p>프래그먼트의 수명 주기를 관리하는 것은 액티비티의 수명 주기를 관리하는 것과 매우 비슷합니다. 액티비티와 마찬가지로 +프래그먼트는 세 가지 상태로 존재할 수 있습니다.</p> + +<dl> + <dt><i>재개됨</i></dt> + <dd>프래그먼트가 실행 중인 액티비티에 표시됩니다.</dd> + + <dt><i>일시정지됨</i></dt> + <dd>또 다른 액티비티가 전경에 나와 있고 사용자가 이에 초점을 맞추고 있지만, +이 프래그먼트가 있는 액티비티도 여전히 표시되어 있습니다(전경의 액티비티가 부분적으로 투명하거나 +전체 화면을 뒤덮지 않습니다).</dd> + + <dt><i>정지됨</i></dt> + <dd>프래그먼트가 표시되지 않습니다. 호스트 액티비티가 정지되었거나 +프래그먼트가 액티비티에서 제거되었지만 백 스택에 추가되었습니다. 정지된 프래그먼트도 +여전히 표시는 됩니다(모든 상태 및 구성원 정보를 시스템이 보존합니다). 하지만, 사용자에게는 +더 이상 표시되지 않으며 액티비티를 종료하면 이것도 종료됩니다.</dd> +</dl> + +<p>이번에도 액티비티와 마찬가지로, 프래그먼트의 상태를 보존하려면 {@link +android.os.Bundle}을 사용합니다. 이는 혹시나 액티비티의 프로세스가 종료되고 액티비티를 +다시 만들 때 해당 프래그먼트의 상태를 복구해야 할 필요가 있을 때를 대비하는 것입니다. 상태를 저장하려면 프래그먼트의 {@link +android.app.Fragment#onSaveInstanceState onSaveInstanceState()} 콜백 중에 저장할 수 있고, 복구는 +{@link android.app.Fragment#onCreate onCreate()}, {@link +android.app.Fragment#onCreateView onCreateView()} 또는 {@link +android.app.Fragment#onActivityCreated onActivityCreated()} 중 한 가지가 진행되는 동안 할 수 있습니다. 상태 저장에 관한 자세한 정보는 +<a href="{@docRoot}guide/components/activities.html#SavingActivityState">액티비티</a> +문서를 참조하십시오.</p> + +<p>액티비티와 프래그먼트의 수명 주기에서 가장 중대한 차이점은 +해당되는 백 스택에 저장되는 방법입니다. 액티비티는 중단되었을 때 시스템이 관리하는 +액티비티 백 스택 안에 배치되는 것이 기본입니다(따라서 사용자가 <em>뒤로</em> 버튼을 사용하여 다시 이 액티비티로 +뒤로 탐색할 수 있습니다. 이 내용은 <a href="{@docRoot}guide/components/tasks-and-back-stack.html">작업 및 백 스택</a>에서 설명하였습니다). +하지만, 프래그먼트가 호스트 액티비티가 관리하는 백 스택 안에 배치되는 것은 해당 인스턴스를 저장하라고 명시적으로 요청하는 경우뿐입니다. +이때 프래그먼트를 제거하는 트랜잭션 중 {@link +android.app.FragmentTransaction#addToBackStack(String) addToBackStack()}을 +호출합니다.</p> + +<p>이것만 제외하면, 프래그먼트 수명 주기를 관리하는 것은 액티비티의 수명 주기를 관리하는 것과 +아주 비슷합니다. 따라서, <a href="{@docRoot}guide/components/activities.html#Lifecycle">액티비티 +수명 주기 관리</a>에 쓰이는 실례가 프래그먼트에도 똑같이 적용되는 것입니다. 하지만 또 한 가지 이해해두어야 하는 것이 있습니다. 즉, +액티비티의 수명이 프래그먼트의 수명에 어떤 영향을 미치는지를 알아두어야 합니다.</p> + +<p class="caution"><strong>주의:</strong> {@link android.app.Fragment} 내에서 {@link android.content.Context} +객체가 필요한 경우, {@link android.app.Fragment#getActivity()}를 호출하면 됩니다. +그러나 {@link android.app.Fragment#getActivity()}를 호출하는 것은 프래그먼트가 액티비티에 + 첨부되어 있는 경우뿐이니 유의하십시오. 프래그먼트가 아직 첨부되지 않았거나 수명 주기가 끝날 무렵 분리된 경우, +{@link android.app.Fragment#getActivity()}가 null을 반환합니다.</p> + + +<h3 id="CoordinatingWithActivity">액티비티 수명 주기와 조화</h3> + +<p>프래그먼트가 있는 액티비티의 수명 주기는 해당 프래그먼트의 수명 주기에 직접적인 +영향을 미칩니다. 따라서 액티비티에 대한 각 수명 주기 콜백이 각 프래그먼트에 대한 비슷한 콜백을 +유발합니다. 예를 들어 액티비티가 {@link android.app.Activity#onPause}를 받으면, +해당 액티비티 내의 각 프래그먼트가 {@link android.app.Fragment#onPause}를 받습니다.</p> + +<p>하지만 프래그먼트에는 몇 가지 수명 주기 콜백이 더 있습니다. 이것은 액티비티와의 +고유한 상호 작용을 다루어 프래그먼트의 UI를 구축하고 소멸시키는 것과 같은 +작업을 수행합니다. 이러한 추가적인 콜백 메서드를 예로 들면 다음과 같습니다.</p> + +<dl> + <dt>{@link android.app.Fragment#onAttach onAttach()}</dt> + <dd>프래그먼트가 액티비티와 연관되어 있었던 경우 호출됩니다(여기에서 {@link +android.app.Activity}가 전달됩니다).</dd> + <dt>{@link android.app.Fragment#onCreateView onCreateView()}</dt> + <dd>프래그먼트와 연관된 보기 계층을 생성하기 위해 호출됩니다.</dd> + <dt>{@link android.app.Fragment#onActivityCreated onActivityCreated()}</dt> + <dd>액티비티의 {@link android.app.Activity#onCreate +onCreate()} 메서드가 반환되면 호출됩니다.</dd> + <dt>{@link android.app.Fragment#onDestroyView onDestroyView()}</dt> + <dd>프래그먼트와 연관된 보기 계층이 제거되는 중일 때 호출됩니다.</dd> + <dt>{@link android.app.Fragment#onDetach onDetach()}</dt> + <dd>프래그먼트가 액티비티와 연결이 끊어지는 중일 때 호출됩니다.</dd> +</dl> + +<p>호스트 액티비티의 영향을 받을 프래그먼트 수명 주기의 흐름은 그림 3에서 +확인하십시오. 이 그림을 보면 액티비티의 각 연속된 상태가 프래그먼트가 어느 +콜백 메서드를 받게 되는지 결정 짓는다는 것을 볼 수 있습니다. 예를 들어 액티비티가 자신의 {@link +android.app.Activity#onCreate onCreate()} 콜백을 받은 경우, 해당 액티비티 안에 있는 프래그먼트는 +{@link android.app.Fragment#onActivityCreated onActivityCreated()} 콜백을 받을 뿐입니다.</p> + +<p>액티비티가 재개된 상태에 도달하면 자유자재로 프래그먼트를 액티비티에 추가하거나 액티비티에서 +제거해도 됩니다. 따라서, 액티비티가 재개된 상태에 있는 동안에만 프래그먼트의 수명 주기를 +독립적으로 변경할 수 있는 것입니다.</p> + +<p>그러나 액티비티가 재개된 상태를 떠나면 액티비티는 다시 프래그먼트를 그 수명 주기 안으로 +밀어넣습니다.</p> + + + + +<h2 id="Example">예</h2> + +<p>이 문서에서 논의한 모든 것을 한 번에 모아 보기 위해, 다음은 두 개의 프래그먼트를 사용하여 +창이 두 개인 레이아웃을 생성하는 액티비티를 예시로 나타낸 것입니다. 아래의 액티비티에 포함된 +한 프래그먼트는 셰익스피어 희곡 제목 목록을 표시하고, 또 다른 하나는 목록에서 선택했을 때 +해당 희곡의 요약을 표시합니다. 또한 화면 구성을 근거로 프래그먼트를 여러 가지로 구성하여 제공하는 방법도 +보여줍니다.</p> + +<p class="note"><strong>참고:</strong> 이 액티비티에 대한 완전한 소스 코드는 +<a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.html">{@code +FragmentLayout.java}</a>에서 이용하실 수 있습니다.</p> + +<p>주요 액티비티는 {@link +android.app.Activity#onCreate onCreate()} 중에 일반적인 방식으로 레이아웃을 적용합니다.</p> + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java main} + +<p>적용된 레이아웃은 {@code fragment_layout.xml}입니다.</p> + +{@sample development/samples/ApiDemos/res/layout-land/fragment_layout.xml layout} + +<p>시스템은 이 레이아웃을 사용하여 액티비티가 레이아웃을 로딩하자마자 {@code TitlesFragment}를 초기화합니다(이것이 희곡 제목을 +목록으로 나열합니다). 반면 {@link android.widget.FrameLayout} +(희곡 요약을 표시하는 프래그먼트가 배치될 곳)은 화면 오른쪽에 있는 +공간을 차지하기는 하지만 처음에는 텅 빈 상태로 유지됩니다. 아래에서 볼 수 있듯이, 사용자가 해당 목록에서 +항목을 하나 선택해야만 프래그먼트가 {@link android.widget.FrameLayout} 안에 배치됩니다.</p> + +<p>그러나 희곡 목록과 요약을 둘 다 나란히 표시할 만큼 너비가 넓지 않은 +화면 구성도 있습니다. 따라서 위의 레이아웃은 가로 방향 화면 구성에만 사용되며, +이를 {@code res/layout-land/fragment_layout.xml}에 저장하여 씁니다.</p> + +<p>그러므로 화면이 세로 방향으로 구성된 경우, 시스템은 다음 레이아웃을 적용합니다. 이것은 +{@code res/layout/fragment_layout.xml}에 저장되어 있습니다.</p> + +{@sample development/samples/ApiDemos/res/layout/fragment_layout.xml layout} + +<p>이 레이아웃에는 {@code TitlesFragment}만 포함되어 있습니다. 이는 다시 말해 기기가 세로 방향인 경우에는 +희곡 제목 목록만 표시된다는 뜻입니다. 따라서 사용자가 이 구성에서 목록 항목을 하나 클릭하면, +애플리케이션이 두 번째 프래그먼트를 로딩하는 대신 새 액티비티를 시작하여 요약을 +표시하게 됩니다.</p> + +<p>다음으로, 프래그먼트 클래스에서 이것을 달성하는 방법을 보시겠습니다. 첫 번째가 {@code +TitlesFragment}로, 셰익스피어 희곡 제목 목록을 표시하는 것입니다. 이 프래그먼트는 {@link +android.app.ListFragment}를 확장하며 목록 보기 작업의 대부분을 처리하기 위해 여기에 의존합니다.</p> + +<p>이 코드를 살펴보면서 사용자가 목록 항목을 클릭하면 일어날 수 있는 두 가지 동작이 +있다는 점을 눈여겨 보십시오. 두 레이아웃 중 어느 것이 활성화 상태인지에 따라 +같은 액티비티 내에서 세부 사항을 표시하기 위해 새 프래그먼트를 생성하거나 표시할 수도 있고(프래그먼트를 {@link +android.widget.FrameLayout}에 추가함으로써), 새 액티비티를 시작할 수도 있습니다(프래그먼트를 표시할 수 있는 곳).</p> + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java titles} + +<p>두 번째 프래그먼트인 {@code DetailsFragment}는 {@code TitlesFragment}에서 가져온 목록에서 선택한 항목에 대한 희곡 요약을 +표시하는 것입니다.</p> + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java details} + +<p>{@code TitlesFragment} 클래스에서 다룬 것을 되살려 보면, 사용자가 목록 항목을 클릭하고 +현재 레이아웃이 {@code R.id.details} 보기를 포함하지 <em>않는</em> 경우(이 보기가 +{@code DetailsFragment}가 속하는 곳임), 애플리케이션은 항목의 내용을 표시하기 위해 {@code DetailsActivity} + 액티비티를 시작하게 됩니다.</p> + +<p>다음은 화면이 세로 방향으로 구성되어 있을 때 선택한 희곡의 요약을 표시하기 위해 단순히 {@code DetailsFragment}를 + 포함할 뿐인 {@code DetailsActivity}입니다.</p> + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java +details_activity} + +<p>이 액티비티는 구성이 가로 방향인 경우 알아서 종료한다는 점을 눈여겨 보십시오. 따라서 +주요 액티비티가 작업을 인계 받아 {@code DetailsFragment}를 {@code TitlesFragment}와 함께 표시할 수 있는 것입니다. +이것은 사용자가 세로 방향 구성에서 {@code DetailsActivity}를 시작했지만 +그런 다음 가로 방향으로 돌리는 경우(현재 액티비티를 다시 시작함) 일어날 수 있습니다.</p> + + +<p>프래그먼트 사용에 대한 더 많은 샘플(및 이 예시에 대한 완전한 소스 파일)을 보시려면 +<a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/index.html#Fragment"> +ApiDemos</a>에서 이용할 수 있는 API Demos 샘플 앱을 참조하십시오(<a href="{@docRoot}resources/samples/get.html">샘플 SDK 구성 요소</a>에서 다운로드할 수 있습니다).</p> + + diff --git a/docs/html-intl/intl/ko/guide/components/fundamentals.jd b/docs/html-intl/intl/ko/guide/components/fundamentals.jd new file mode 100644 index 000000000000..608b5a2cba1b --- /dev/null +++ b/docs/html-intl/intl/ko/guide/components/fundamentals.jd @@ -0,0 +1,480 @@ +page.title=애플리케이션 기본 항목 +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + +<h2>이 문서의 내용</h2> +<ol> +<li><a href="#Components">앱 구성 요소</a> + <ol> + <li><a href="#ActivatingComponents">구성 요소 활성화</a></li> + </ol> +</li> +<li><a href="#Manifest">매니페스트 파일</a> + <ol> + <li><a href="#DeclaringComponents">구성 요소 선언</a></li> + <li><a href="#DeclaringRequirements">앱 요구 사항 선언</a></li> + </ol> +</li> +<li><a href="#Resources">앱 리소스</a></li> +</ol> +</div> +</div> + +<p>Android 앱은 Java 프로그래밍 언어로 작성됩니다. Android SDK 도구는 +코드를 컴파일링하여 모든 데이터 및 리소스 파일과 함께 하나의 APK로 만듭니다. 이것은 즉, <i>Android 패키지</i> +를 뜻하며, 이는 일종의 {@code .apk} 접미사가 있는 아카이브 파일입니다. 한 개의 APK 파일에는 +Android 앱의 모든 콘텐츠가 들어 있으며 이 파일이 바로 Android로 구동하는 기기가 앱을 설치할 때 사용하는 파일입니다.</p> + +<p>Android 앱은 일단 기기에 설치되고 나면 각자 나름의 보안 샌드박스 안에 살게 됩니다. </p> + +<ul> + <li>Android 운영 체제는 멀티 사용자 Linux 시스템으로, 여기서 각 앱은 각기 다른 사용자와 +같습니다.</li> + +<li>기본적으로 시스템이 각 앱에 고유한 Linux ID를 할당합니다(이 ID는 시스템만 +사용할 수 있으며 앱은 이것을 알지 못합니다). 시스템은 앱 안의 모든 파일에 대해 권한을 설정하여 +해당 앱에 할당된 사용자 ID만 이에 액세스할 수 있도록 합니다. </li> + +<li>각 프로세스에는 나름의 가상 머신(VM)이 있고, 그렇기 때문에 한 앱의 코드가 다른 여러 앱과는 격리된 상태로 +실행됩니다.</li> + +<li>기본적으로 모든 앱이 나름의 Linux 프로세스에서 실행됩니다. Android는 앱의 구성 요소 중 +어느 것이라도 실행해야 하는 경우 프로세스를 시작하고, 이것이 더 이상 필요 없어지거나 시스템이 다른 앱을 위해 +메모리를 회복해야 하는 경우 해당 프로세스를 종료합니다.</li> +</ul> + +<p>Android 시스템은 이런 방식으로 <em>최소 특권의 원리</em>를 구현하는 것입니다. 다시 말해, +각 앱은 기본적으로 자신의 작업을 수행하기 위해 필요한 구성 요소에만 액세스 권한을 가지고 +그 이상은 허용되지 않습니다. 이렇게 하면 대단히 안전한 환경이 만들어져 앱이 시스템에서 +자신이 권한을 부여 받지 못한 부분에는 액세스할 수 없게 됩니다.</p> + +<p>그러나, 앱이 다른 여러 앱과 데이터를 공유하는 것과 앱이 시스템 서비스에 액세스하는 데에는 +여러 가지 방법이 있습니다.</p> + +<ul> + <li>두 개의 앱이 같은 Linux 사용자 ID를 공유하도록 설정할 수도 있습니다. 이 경우 +두 앱은 서로의 파일에 액세스할 수 있게 됩니다. 시스템 리소스를 절약하려면, 같은 사용자 ID를 가진 앱이 +같은 Linux 프로세스에서 실행되도록 설정하고 같은 VM을 공유하도록 할 수도 있습니다(이들 앱은 같은 인증서로 +서명해야 합니다).</li> + <li>앱은 사용자의 연락처, SMS 메시지, 마운트 가능한 저장소(SD 카드), +카메라, Bluetooth를 비롯하여 이외에도 여러 가지 기기 데이터에 액세스할 권한을 요청할 수 있습니다. 모든 +앱 권한은 설치 시점에 사용자가 허용해야 합니다.</li> +</ul> + +<p>이렇게 해서 Android 앱이 시스템 내에 어떤 식으로 존재하는지 기본 정보를 알아보았습니다. 이 문서의 +나머지 부분에서 소개될 내용은 다음과 같습니다.</p> +<ul> + <li>앱을 정의하는 핵심 프레임워크 구성 요소.</li> + <li>구성 요소를 선언하고 앱에 맞는 필수 기기 특징을 선언할 수 있는 매니페스트 +파일.</li> + <li>앱 코드로부터 별도로 분리되어 있으며 앱이 다양한 기기 구성에 맞게 자신의 행동을 +안정적으로 최적화할 수 있도록 해주는 리소스.</li> +</ul> + + + +<h2 id="Components">앱 구성 요소</h2> + +<p>앱 구성 요소는 Android 앱을 이루는 가장 기본적인 구성 단위입니다. 각 +구성 요소는 시스템이 앱으로 들어올 수 있는 각기 다른 통과 지점을 나타냅니다. 구성 요소 중에는 +실제 사용자가 쓸 수 있는 진입 지점이 아닌 것도 있고, 일부는 서로에게 의존하지만, +각각의 구성 요소는 따로 떨어진 엔티티로서 존재하며 각기 특정한 역할을 수행합니다. 즉 하나하나가 +앱의 전반적인 행동을 정의하는 데 유용한 고유한 구성 단위인 것입니다.</p> + +<p>앱 구성 요소에는 네 가지 서로 다른 유형이 있습니다. 각 유형이 뚜렷한 목적을 가지고 있으며 +각자 나름의 수명 주기가 있어 구성 요소의 생성 및 소멸 방식을 정의합니다.</p> + +<p>다음은 네 가지 유형의 앱 구성 요소를 나타낸 것입니다.</p> + +<dl> + +<dt><b>액티비티</b></dt> + +<dd>통상 <i>액티비티</i> 라고 하면, 사용자 인터페이스가 있는 화면 하나를 나타냅니다. 예를 들어 +이메일 앱이라면 새 이메일 목록을 표시하는 액티비티가 하나 있고, +이메일을 작성하는 액티비티가 또 하나, 그리고 이메일을 읽는 데 쓰는 액티비티가 또 하나 있을 수 있습니다. 여러 +액티비티가 함께 작동하여 해당 이메일 앱에서 짜임새 있는 사용자 환경을 형성하는 것은 사실이지만, 각자 서로와는 +독립적인 형태입니다. 따라서, 다른 앱이 이와 같은 액티비티 중 어느 것이라도 하나만 +시작할 수 있습니다(이메일 앱이 그렇게 하도록 허용하는 경우). 예를 들어, 카메라 앱이라면 이메일 앱 안의 +액티비티를 시작하여 새 메일을 작성하도록 해서 사용자가 사진을 공유하도록 할 수 있습니다. + +<p>액티비티는 {@link android.app.Activity}의 하위 클래스로 구현되며 이에 대한 더 자세한 내용은 +<a href="{@docRoot}guide/components/activities.html">액티비티</a> +개발자 가이드에서 확인하실 수 있습니다.</p> +</dd> + + +<dt><b>서비스</b></dt> + +<dd>통상 <i>서비스</i> 라고 하면 배경에서 실행되는 구성 요소로, 오랫동안 실행되는 +작업을 수행하거나 원격 프로세스를 위한 작업을 수행하는 것입니다. 서비스는 +사용자 인터페이스를 제공하지 않습니다. 예를 들어 서비스는 사용자가 다른 앱에 있는 동안에 배경에서 음악을 재생할 수도 있고, +아니면 사용자와 액티비티 사이의 상호 작용을 차단하지 않고 네트워크를 가로질러 +데이터를 가져올 수도 있습니다. 또 다른 구성 요소(예: 액티비티)가 서비스를 시작한 다음 +실행되도록 두거나 자신에게 바인딩하여 상호 작용하도록 할 수도 있습니다. + +<p>서비스는 {@link android.app.Service}의 하위 클래스로 구현되며 이에 대한 더 자세한 내용은 +<a href="{@docRoot}guide/components/services.html">서비스</a> +개발자 가이드에서 확인하실 수 있습니다.</p> +</dd> + + +<dt><b>콘텐츠 제공자</b></dt> + +<dd>통상 <i>콘텐츠 제공자</i> 는 공유된 앱 데이터 집합을 관리합니다. 데이터는 파일 시스템이나 SQLite 데이터베이스, +또는 웹이나 기타 영구적인 저장소 위치 중 앱이 액세스할 수 있는 곳이라면 어디에든 저장할 수 +있습니다. 다른 여러 앱은 콘텐츠 제공자를 통해 해당 데이터를 쿼리하거나, 심지어는 수정할 수도 +있습니다(콘텐츠 제공자가 그렇게 하도록 허용하는 경우). 예를 들어, Android 시스템은 사용자의 연락처 정보를 +관리하는 콘텐츠 제공자를 제공합니다. 따라서, 적절한 권한을 가진 앱이라면 +어떤 것이든 해당 콘텐츠 제공자의 일부를 쿼리하여(예를 들어 {@link +android.provider.ContactsContract.Data} 등) 특정한 사람에 대한 정보를 읽고 쓸 수 있습니다. + +<p>콘텐츠 제공자는 앱에서 비공개이며 공유되지 않는 데이터를 읽고 쓰는 데에도 +유용합니다. 예를 들어 <a href="{@docRoot}resources/samples/NotePad/index.html">메모장</a> 샘플 앱은 메모한 내용을 저장하는 데 +콘텐츠 제공자를 사용합니다.</p> + +<p>콘텐츠 제공자는 {@link android.content.ContentProvider}의 +하위 클래스로 구현되며, 다른 앱이 트랜잭션을 수행할 수 있도록 활성화하는 표준 API 집합을 +구현해야 합니다. 자세한 내용은 <a href="{@docRoot}guide/topics/providers/content-providers.html">콘텐츠 제공자</a> 개발자 +가이드를 참조하십시오.</p> +</dd> + + +<dt><b>브로드캐스트 수신기</b></dt> + +<dd>통상 <i>브로드캐스트 수신기</i> 는 시스템 전체에 대한 브로드캐스트 공지에 응답하는 구성 요소를 +말합니다. 대다수의 브로드캐스트는 시스템에서 시작합니다. 예를 들어, 화면이 꺼졌다거나 +배터리 잔량이 부족하다거나, 사진을 캡처했다는 것을 알리는 브로드캐스트가 있습니다. +앱도 브로드캐스트를 시작합니다. 예를 들어, 기기에 몇 가지 데이터를 다운로드하여 다른 앱도 사용할 수 있다는 +사실을 다른 여러 앱에게 알리는 것입니다. 브로드캐스트 수신기는 사용자 인터페이스를 표시하지 않지만, +<a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">상태 표시줄 알림을 생성</a>하여 +사용자에게 브로드캐스트 이벤트가 발생했다고 알릴 수 있습니다. 다만 브로드캐스트 수신기는 +그저 다른 구성 요소로의 "게이트웨이"인 경우가 더 보편적이고, 극소량의 작업만 수행하도록 만들어진 경우가 많습니다. 예컨대 +서비스를 시작하여 이벤트를 근거로 한 어떤 작업을 수행하도록 할 수 있습니다. + +<p>브로드캐스트 수신기는 {@link android.content.BroadcastReceiver}의 +하위 클래스로 구현되며 각 브로드캐스트는 {@link android.content.Intent} 객체로 전달됩니다. 자세한 정보는 +{@link android.content.BroadcastReceiver} 클래스를 참조하십시오.</p> +</dd> + +</dl> + + + +<p>Android 시스템 디자인의 독특한 점으로 어떤 앱이든 다른 앱의 구성 요소를 시작할 수 있다는 점을 +들 수 있습니다. 예를 들어 사용자가 기기 카메라로 사진을 캡처하기를 바라는 경우, +그런 작업을 수행하는 또 다른 앱이 있을 가능성이 높습니다. 그러면 사진을 캡처하는 액티비티를 직접 개발하는 대신 +여러분의 앱이 그 앱을 사용하도록 하면 됩니다. 카메라 앱에 +통합하기는커녕 카메라 앱의 코드에 연결시킬 필요조차도 없습니다. +그 대신, 그저 사진을 캡처하는 카메라 앱 안의 해당 액티비티를 시작하기만 하면 +됩니다. 작업이 완료되면 사진이 앱으로 반환되기까지 하여 바로 사용할 수 있습니다. 사용자에게는, +마치 카메라가 여러분의 앱의 일부분인 것처럼 보입니다.</p> + +<p>시스템이 구성 요소를 시작하는 경우, 그 앱에 대한 프로세스를 시작하는 것이며(이미 +실행 중이지 않은 경우), 해당 구성 요소에 필요한 클래스를 인스턴트화하는 것입니다. 예를 들어 여러분의 앱이 +카메라 앱 내에서 사진을 캡처하는 액티비티를 시작한다고 하면, 해당 액티비티는 +여러분 앱의 프로세스가 아니라 카메라 앱에 속한 프로세스에서 실행됩니다. +따라서 대부분의 다른 시스템에서와는 달리 Android 앱에는 단일한 진입 +지점이 없습니다(예를 들어 {@code main()} 기능이 없습니다).</p> + +<p>시스템이 각 앱을 별도의 프로세스에서 실행하며 다른 앱에 대한 액세스를 제한하는 +파일 권한을 가지고 실행하기 때문에 여러분의 앱은 또 다른 앱에서 곧바로 구성 요소를 +활성화할 수는 없습니다. 하지만 Android 시스템은 할 수 있습니다. 그래서 또 다른 앱에 있는 +구성 요소를 활성화하려면 시스템에 메시지를 전달하여 특정 구성 요소를 시작하고자 하는 <em>인텐트</em>를 +밝혀야 합니다. 그러면 시스템이 대신 해당 구성 요소를 활성화해줍니다.</p> + + +<h3 id="ActivatingComponents">구성 요소 활성화</h3> + +<p>네 가지 구성 요소 중 세 가지—액티비티, 서비스 및 +브로드캐스트 수신기—는 일명 <em>인텐트</em>라고 하는 비동기식 메시지가 활성화합니다. +인텐트는 각각의 구성 요소를 런타임에 서로 바인딩하며(다른 구성 요소로부터 작업을 요청하는 +일종의 메신저로 생각하면 됩니다), 이는 구성 요소가 여러분의 앱에 속하든 아니든 +무관합니다.</p> + +<p>인텐트는 {@link android.content.Intent} 객체로 생성되며, 이것이 +특정 구성 요소를 활성화할지 아니면 구성 요소의 특정 <em>유형</em>을 활성화할지를 나타내는 메시지를 정의합니다. 인텐트는 +각각 명시적이거나 암시적일 수 있습니다.</p> + +<p>액티비티와 서비스의 경우, 인텐트는 수행할 작업을 정의하며(예를 들어 무언가를 '보기" 또는 +"보내기"), 작업을 수행할 데이터의 URI를 나타낼 수 있습니다(시작되는 구성 요소가 알아야 할 것은 +이외에도 많이 있습니다). 예를 들어, 인텐트는 액티비티에 이미지를 표시하거나 웹 페이지를 열라는 요청을 +전달할 수 있습니다. 어떤 경우에는 액티비티를 시작하여 +결과를 받아오도록 할 수 있습니다. 이런 경우 이 액티비티는 +{@link android.content.Intent}로 결과를 반환하기도 합니다(예를 들어, 사용자가 +개인적인 연락처를 선택하도록 한 다음 그것을 반환하도록 하는 인텐트를 발행할 수 있습니다—반환 인텐트에 +선택한 연락처를 가리키는 URI가 포함됩니다).</p> + +<p>브로드캐스트 수신기의 경우, 인텐트는 단순히 브로드캐스트될 알림을 +정의할 뿐입니다(예를 들어, 기기 배터리 잔량이 낮다는 것을 나타내는 브로드캐스트에는 +"배터리 부족"을 나타내는 알려진 작업 문자열만 포함됩니다).</p> + +<p>남은 하나의 구성 요소 유형, 즉 콘텐츠 제공자는 인텐트가 활성화하지 않습니다. 그보다는 +{@link android.content.ContentResolver}로부터의 요청으로 지정되면 활성화됩니다. 콘텐츠 +확인자는 콘텐츠 제공자와의 모든 직접적인 트랜잭션을 처리하여 +제공자와의 트랜잭션을 수행하는 구성 요소가 그런 일을 하지 않아도 되게 하고, 그 대신 {@link +android.content.ContentResolver} 객체에서 메서드를 호출합니다. 이렇게 되면 콘텐츠 제공자와 +정보를 요청하는 구성 요소 사이에 추상화 계층이 하나 남습니다(보안 목적).</p> + +<p>각 유형의 구성 요소를 활성화하는 데에는 각기 별도의 메서드가 있습니다.</p> +<ul> + <li>액티비티를 시작하려면(아니면 무언가 새로운 할 일을 주려면) +{@link android.content.Intent}를 {@link android.content.Context#startActivity +startActivity()} 또는 {@link android.app.Activity#startActivityForResult startActivityForResult()}에 +전달하면 됩니다(액티비티가 결과를 반환하기를 원하는 경우).</li> + <li>서비스를 시작하려면(또는 진행 중인 서비스에 새로운 지침을 주려면) +{@link android.content.Intent}를 {@link android.content.Context#startService +startService()}에 전달하면 됩니다. 아니면 {@link android.content.Intent}를 +{@link android.content.Context#bindService bindService()}에 전달하여 서비스에 바인딩할 수도 있습니다.</li> + <li>브로드캐스트를 시작하려면 {@link android.content.Intent}를 +{@link android.content.Context#sendBroadcast(Intent) sendBroadcast()}, {@link +android.content.Context#sendOrderedBroadcast(Intent, String) sendOrderedBroadcast()} 또는 {@link +android.content.Context#sendStickyBroadcast sendStickyBroadcast()}와 같은 메서드에 전달하면 됩니다.</li> + <li>콘텐츠 제공자에 쿼리를 수행하려면 {@link android.content.ContentResolver}에서 {@link +android.content.ContentProvider#query query()}를 호출하면 됩니다.</li> +</ul> + +<p>인텐트 사용에 관한 자세한 정보는 <a href="{@docRoot}guide/components/intents-filters.html">인텐트 및 +인텐트 필터</a>문서를 참조하십시오. 특정 구성 요소를 활성화하는 데 관한 자세한 정보 또한 다음 문서에 +제공되어 있습니다. <a href="{@docRoot}guide/components/activities.html">액티비티</a>, <a href="{@docRoot}guide/components/services.html">서비스</a>, {@link +android.content.BroadcastReceiver} 및 <a href="{@docRoot}guide/topics/providers/content-providers.html">콘텐츠 제공자</a>.</p> + + +<h2 id="Manifest">매니페스트 파일</h2> + +<p>Android 시스템이 앱 구성 요소를 시작하려면 시스템은 우선 해당 구성 요소가 +존재하는지 알아야 합니다. 그러기 위해 앱의 {@code AndroidManifest.xml} 파일을 읽습니다(즉 "매니페스트" +파일). 앱은 이 파일 안에 모든 구성 요소를 선언해야 하며, 이 파일은 앱 프로젝트 디렉터리의 루트에 +있어야 합니다.</p> + +<p>매니페스트는 앱의 구성 요소를 선언하는 것 이외에도 수많은 역할을 합니다. +예를 들면 다음과 같습니다.</p> +<ul> + <li>앱이 요구하는 모든 사용자 권한 식별(예: 인터넷 액세스 또는 사용자의 연락처로의 +읽기 액세스)</li> + <li>앱이 어느 API를 사용하는지를 근거로 하여 앱에서 요구하는 최소 <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#ApiLevels">API 레벨</a> +선언</li> + <li>앱에서 사용하거나 필요로 하는 하드웨어 및 소프트웨어 기능 선언(예: 카메라, +블루투스 서비스 또는 멀티터치 화면 등)</li> + <li>앱이 링크되어야 하는 API 라이브러리(Android 프레임워크 +API 제외)(예: <a href="http://code.google.com/android/add-ons/google-apis/maps-overview.html">Google Maps +라이브러리</a>)</li> + <li>그 외 기타 등등</li> +</ul> + + +<h3 id="DeclaringComponents">구성 요소 선언</h3> + +<p>매니페스트의 주요 작업은 시스템에 앱의 구성 요소에 대해 알리는 것입니다. 예를 들어 +매니페스트 파일은 액티비티를 다음과 같이 선언할 수 있습니다. </p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> +<manifest ... > + <application android:icon="@drawable/app_icon.png" ... > + <activity android:name="com.example.project.ExampleActivity" + android:label="@string/example_label" ... > + </activity> + ... + </application> +</manifest></pre> + +<p><code><a +href="{@docRoot}guide/topics/manifest/application-element.html"><application></a></code> +요소에서 {@code android:icon} 속성은 앱을 식별하는 아이콘에 대한 리소스를 +가리킵니다.</p> + +<p><code><a +href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> 요소에서는, +{@code android:name} 속성이 {@link +android.app.Activity} 하위 클래스의 완전히 정규화된 클래스 이름을 나타내며 {@code android:label} 속성은 액티비티의 +사용자에게 표시되는 레이블로 사용할 문자열을 나타냅니다.</p> + +<p>모든 앱 구성 요소를 이렇게 선언해야 합니다.</p> +<ul> + <li>액티비티는 +<code><a +href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> 요소</li> + <li>서비스는 +<code><a +href="{@docRoot}guide/topics/manifest/service-element.html"><service></a></code> 요소</li> + <li>브로드캐스트 수신기는 +<code><a +href="{@docRoot}guide/topics/manifest/receiver-element.html"><receiver></a></code> 요소</li> + <li>콘텐츠 제공자는 +<code><a +href="{@docRoot}guide/topics/manifest/provider-element.html"><provider></a></code> 요소</li> +</ul> + +<p>액티비티, 서비스를 비롯하여 소스에는 포함시키지만 매니페스트에서는 선언하지 않는 +콘텐츠 제공자는 시스템에 표시되지 않으며, 따라서 실행될 수 없습니다. 그러나 +브로드캐스트 + 수신기는 매니페스트에서 선언해도 되고 코드를 사용해( +{@link android.content.BroadcastReceiver} 객체로) 동적으로 생성한 다음 시스템에 등록해도 됩니다. 이때 +{@link android.content.Context#registerReceiver registerReceiver()}를 호출하는 방법을 씁니다.</p> + +<p>앱에 맞는 매니페스트 파일을 구성하는 방법에 대한 자세한 내용은 <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml 파일</a>을 + 참조하십시오. </p> + + + +<h3 id="DeclaringComponentCapabilities">구성 요소 기능 선언</h3> + +<p>위에서 논한 바와 같이, <a href="#ActivatingComponents">활성화 상태의 구성 요소</a>에서는 +{@link android.content.Intent}를 사용하여 액티비티, 서비스 및 브로드캐스트 수신기를 시작할 수 있습니다. 그렇게 하려면 +대상 구성 요소를 인텐트 내에서 명시적으로 명명하면 됩니다(구성 요소 클래스 이름을 사용). 그러나, +인텐트의 진정한 힘은 <em>암시적 인텐트</em>의 개념에서 발휘됩니다. 암시적 인텐트는 +그저 수행할 작업의 유형을 설명할 뿐이며(또한, 선택 사항으로, 해당 작업을 수행하고자 하는 +데이터 위치도) 시스템에 기기에서 작업을 수행할 수 있는 구성 요소를 찾아 +시작하도록 해줍니다. 인텐트가 설명한 작업을 수행할 수 있는 구성 요소가 여러 개인 경우, +어느 것을 사용할지 사용자가 선택합니다.</p> + +<p>시스템이 인텐트에 응답할 수 있는 구성 요소를 식별하는 방법은 수신한 인텐트를 + <i>인텐트 필터</i> 와 비교하는 것입니다. 이 인텐트 필터는 기기의 다른 여러 앱의 매니페스트 +파일이 제공합니다.</p> + +<p>앱의 매니페스트에서 액티비티를 선언하는 경우, 선택 사항으로 +해당 액티비티의 기능을 선언하는 인텐트 필터를 포함시켜서 다른 앱으로부터의 인텐트에 +응답할 수 있도록 할 수 있습니다. 구성 요소에 대한 인텐트 필터를 선언하려면 +<a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a> 요소를 해당 구성 요소의 선언 요소 하위로 추가하면 됩니다.</p> + +<p>예를 들어, 새 이메일을 작성하는 데 쓰는 액티비티가 있는 이메일 앱을 구축했다고 가정합시다. 이때 "전송" 인텐트에 +응답하는 인텐트 필터를 선언하려면(새 이메일을 전송하기 위해) 다음과 같이 하면 됩니다.</p> +<pre> +<manifest ... > + ... + <application ... > + <activity android:name="com.example.project.ComposeEmailActivity"> + <intent-filter> + <action android:name="android.intent.action.SEND" /> + <data android:type="*/*" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + </application> +</manifest> +</pre> + +<p>그런 다음, 다른 앱이 {@link +android.content.Intent#ACTION_SEND} 작업을 가진 인텐트를 생성하여 그것을 {@link android.app.Activity#startActivity +startActivity()}로 전달하면 시스템이 여러분의 액티비티를 시작하여 사용자가 이메일을 임시 보관하고 전송할 수 +있습니다.</p> + +<p>인텐트 필터 생성에 관한 자세한 내용은 <a href="{@docRoot}guide/components/intents-filters.html">인텐트 및 인텐트 필터</a> 문서를 참조하십시오. +</p> + + + +<h3 id="DeclaringRequirements">앱 요구 사항 선언</h3> + +<p>Android로 구동되는 기기는 수없이 많지만 모두 똑같은 특징을 갖고 같은 +기능을 제공하는 것은 아닙니다. 앱이 필요로 하는 기능이 부족한 기기에 앱을 설치하게 되는 불상사를 방지하려면, +앱이 지원하는 기기 유형에 대한 프로필을 명확하게 정의하는 것이 중요합니다. +그러려면 매니페스트 파일에 기기와 소프트웨어 요구 사항을 +선언하면 됩니다. 이와 같은 선언은 대부분 정보성일 뿐이며 시스템은 이를 읽지 않는 것이 일반적이지만, +Google Play와 같은 외부 서비스는 사용자가 본인의 기기에서 앱을 검색할 때 필터링을 제공하기 위해 +이와 같은 선언도 읽습니다.</p> + +<p>예를 들어, 앱에 카메라가 필요하고 Android 2.1부터 도입된 API를 사용하는 경우(<a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#ApiLevels">API 레벨</a> 7) +이과 같은 내용을 매니페스트 파일에 요구 사항으로 선언하려면 다음과 같이 합니다.</p> + +<pre> +<manifest ... > + <uses-feature android:name="android.hardware.camera.any" + android:required="true" /> + <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" /> + ... +</manifest> +</pre> + +<p>이제 카메라가 <em>없고</em> Android 버전이 2.1 <em>이하</em>인 기기는 +Google Play에서 여러분의 앱을 설치할 수 없습니다.</p> + +<p>그러나, 앱이 카메라를 사용하기는 하지만 꼭 +<em>필요한</em> 것은 아니라고 선언할 수도 있습니다. 이 경우에는, 앱이 <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html#required">{@code required}</a> + 속성을 {@code "false"}에 설정하고 런타임을 확인하여 +해당 기기에 카메라가 있는지, 경우에 따라 모든 카메라 기능을 비활성화할 수 있는지 알아봅니다.</p> + +<p>여러 가지 기기와 앱의 호환성을 관리하는 방법에 대한 자세한 정보는 +<a href="{@docRoot}guide/practices/compatibility.html">기기 호환성</a> + 문서를 참조하십시오.</p> + + + +<h2 id="Resources">앱 리소스</h2> + +<p>Android 앱을 이루는 것은 코드만이 아닙니다. 소스 코드와는 별개인 여러 리소스가 필요합니다. +예를 들어 이미지, 오디오 파일과 앱을 시각적으로 표현하는 것과 관련된 모든 것들이 있습니다. +예컨대 애니메이션, 메뉴, 스타일, 색상과 액티비티 사용자 인터페이스의 레이아웃을 XML 파일로 +정의해야 합니다. 앱 리소스를 사용하면 앱의 다양한 특성을 +쉽게 업데이트할 수 있으며 코드를 수정하지 않아도 되고 일련의 대체 리소스를 +제공함으로써 다양한 기기 구성에 맞게 앱을 +최적화할 수도 있습니다(예: 여러 가지 언어 및 화면 크기).</p> + +<p>Android 프로젝트에 포함시키는 리소스마다 SDK 빌드 도구가 고유한 +정수 ID를 정의하므로, 이를 사용하여 앱 코드에서의 리소스나 XML로 정의된 +다른 리소스에서 참조할 수 있습니다. 예를 들어 앱에 {@code +logo.png}라는 이름의 이미지 파일이 들어 있다고 하면({@code res/drawable/} 디렉터리에 저장됨) SDK 도구가 +{@code R.drawable.logo}라는 리소스 ID를 생성합니다. 이것을 사용하여 이미지를 참조하고 사용자 인터페이스에 +삽입할 수 있습니다.</p> + +<p>소스 코드와는 별개로 리소스를 제공하는 것의 가장 중요한 측면 중 하나는 +여러 가지 기기 구성에 맞게 대체 리소스를 제공할 능력을 갖추게 +됩니다. 예를 들어 UI 문자열을 XML로 정의하면 이러한 문자열을 다른 언어로 변환한 뒤 +그러한 문자열을 별개의 파일에 저장할 수 있습니다. 그런 다음, 리소스 디렉터리 이름에 추가한 언어 <em>한정자</em> +(예를 들어 프랑스어 문자열 값의 경우 {@code res/values-fr/}) 및 +사용자의 언어 설정을 근거로 하여 Android 시스템이 적절한 언어 문자열을 UI에 +적용하는 것입니다.</p> + +<p>Android는 대체 리소스에 대해 다양한 <em>한정자</em>를 지원합니다. 한정자란 + 리소스 디렉터리의 이름에 포함시키는 짧은 문자열로, 이를 사용해 해당 리소스를 사용할 기기 구성을 +정의합니다. 또 다른 예를 들자면, +기기의 화면 방향과 크기에 따라 액티비티에 여러 가지 레이아웃을 생성해야 할 때가 +많습니다. 예를 들어 기기 화면이 세로 +방향(키가 큼)인 경우, 버튼이 세로 방향으로 되어 있는 레이아웃을 사용하는 것이 좋지만 화면이 +가로 방향(폭이 넓음)인 경우, 버튼이 가로 방향으로 정렬되어야 합니다. 방향에 따라 레이아웃을 변경하려면, +서로 다른 두 가지 레이아웃을 정의하여 적절한 한정자를 각각의 레이아웃의 디렉터리 이름에 +적용하면 됩니다. 그러면 시스템이 현재 기기 방향에 따라 적절한 레이아웃을 +자동으로 적용합니다.</p> + +<p>애플리케이션에 포함할 수 있는 여러 가지 종류의 리소스와, 각기 다른 기기 구성에 따라 +대체 리소스를 생성하는 방법에 대한 자세한 내용은 <a href="{@docRoot}guide/topics/resources/providing-resources.html">리소스 제공</a>을 읽어보십시오.</p> + + + +<div class="next-docs"> +<div class="col-6"> + <h2 class="norule">계속 읽기:</h2> + <dl> + <dt><a href="{@docRoot}guide/components/intents-filters.html">인텐트 및 인텐트 필터</a> + </dt> + <dd>{@link android.content.Intent} API를 사용하여 +앱 구성 요소(예: 액티비티 및 서비스 등)를 활성화하는 방법, 앱 구성 요소를 다른 여러 앱이 사용할 수 있도록 하는 방법 +등에 관한 정보입니다.</dd> + <dt><a href="{@docRoot}guide/components/activities.html">액티비티</a></dt> + <dd>{@link android.app.Activity} 클래스의 인스턴스를 생성하는 방법에 관한 정보로, +애플리케이션에 사용자 인터페이스가 있는 독특한 화면을 제공합니다.</dd> + <dt><a href="{@docRoot}guide/topics/resources/providing-resources.html">리소스 제공</a></dt> + <dd>Android 앱이 앱 코드와는 별개의 앱 리소스에 대해 구조화된 방식에 관한 정보로, +특정 기기 구성에 맞게 대체 리소스를 제공하는 방법도 포함되어 +있습니다. + </dd> + </dl> +</div> +<div class="col-6"> + <h2 class="norule">혹시 다음과 같은 내용에도 흥미가 있으신가요?</h2> + <dl> + <dt><a href="{@docRoot}guide/practices/compatibility.html">기기 호환성</a></dt> + <dd>여러 가지 유형의 기기에서 Android의 작동 방식과 앱을 각 기기에 맞춰 최적화하는 방법 +또는 여러 가지 기기에 대해 앱의 가용성을 제한하는 방법 등에 관한 +정보입니다.</dd> + <dt><a href="{@docRoot}guide/topics/security/permissions.html">시스템 권한</a></dt> + <dd>Android가 특정 API에 대한 앱의 액세스를 제한하기 위해 권한 시스템을 +사용하는 방법으로, 그러한 API를 사용하려면 앱에 대해 사용자의 승인이 필요합니다.</dd> + </dl> +</div> +</div> + diff --git a/docs/html-intl/intl/ko/guide/components/index.jd b/docs/html-intl/intl/ko/guide/components/index.jd new file mode 100644 index 000000000000..36626324e817 --- /dev/null +++ b/docs/html-intl/intl/ko/guide/components/index.jd @@ -0,0 +1,57 @@ +page.title=앱 구성 요소 +page.landing=true +page.landing.intro=Android의 애플리케이션 프레임워크는 일련의 재사용 가능한 구성 요소를 사용하여 풍성하고 혁신적인 앱을 생성할 수 있습니다. 이 섹션에서는 앱의 구성 단위를 정의 내리는 구성 요소를 구축하는 방법과 인텐트를 사용하여 이와 같은 구성 요소를 연결시키는 법을 설명합니다. +page.metaDescription=Android의 애플리케이션 프레임워크는 일련의 재사용 가능한 구성 요소를 사용하여 풍성하고 혁신적인 앱을 생성할 수 있습니다. 이 섹션에서는 앱의 구성 단위를 정의 내리는 구성 요소를 구축하는 방법과 인텐트를 사용하여 이와 같은 구성 요소를 연결시키는 법을 설명합니다. +page.landing.image=images/develop/app_components.png +page.image=images/develop/app_components.png + +@jd:body + +<div class="landing-docs"> + + <div class="col-6"> + <h3>블로그 문서</h3> + + <a href="http://android-developers.blogspot.com/2012/05/using-dialogfragments.html"> + <h4>DialogFragment 사용하기</h4> + <p>이 포스트에서는 v4 지원 라이브러리와 함께 DialogFragment를 사용하여(Honeycomb 이전 기기에서 이전 버전과의 호환성을 위해) 간단한 편집 대화를 표시하고 인터페이스를 사용하여 호출 중인 액티비티에 결과를 반환하는 법을 보여드립니다.</p> + </a> + + <a href="http://android-developers.blogspot.com/2011/03/fragments-for-all.html"> + <h4>모두를 위한 프래그먼트</h4> + <p>Google에서는 오늘 같은 프래그먼트 API를 노출하는 정적 라이브러리를 출시했습니다(새로운 LoaderManager와 몇 가지 다른 클래스도 포함). 이 덕분에 Android 1.6 이후 버전과 호환되는 애플리케이션이 프래그먼트를 사용하여 태블릿과 호환되는 사용자 인터페이스를 생성할 수 있게 되었습니다. </p> + </a> + + <a href="http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html"> + <h4>성능을 위한 다중 스레딩</h4> + <p>반응형 애플리케이션을 생성할 때에는 주요 UI 스레드가 최소한의 작업만 +하도록 하는 것이 좋습니다. 애플리케이션을 중단시킬 수 있는, +길어질 수 있는 작업은 모두 다른 스레드에서 처리해야 합니다.</p> + </a> + </div> + + <div class="col-6"> + <h3>교육</h3> + + <a href="http://developer.android.com/training/basics/activity-lifecycle/index.html"> + <h4>액티비티 수명 주기 관리하기</h4> + <p>이 클래스에서는 각각의 액티비티 +인스턴스가 수신하는 중요한 수명 주기 콜백 메서드를 설명합니다. 또한 이러한 콜백 메서드를 사용하여 액티비티가 +사용자가 원하는 작업을 하고, 액티비티가 필요로 하지 않을 때 시스템 리소스 사용을 방지하는 방법에 대해서도 설명합니다.</p> + </a> + + <a href="http://developer.android.com/training/basics/fragments/index.html"> + <h4>프래그먼트로 동적 UI 구축하기</h4> + <p>이 클래스에서는 Android 1.6만큼 오래된 버전을 실행하는 기기도 +계속 지원하면서 프래그먼트로 동적 사용자 경험을 생성하고, 기기의 화면 크기에 따라 앱의 사용자 환경을 +최적화할 수 있는 방법을 보여줍니다.</p> + </a> + + <a href="http://developer.android.com/training/sharing/index.html"> + <h4>콘텐츠 공유하기</h4> + <p>이 클래스는 인텐트 API와 ActionProvider 객체를 사용하여 여러 애플리케이션 사이에서 +콘텐츠를 전송하고 수신하는 몇 가지 보편적인 방법을 다룹니다.</p> + </a> + </div> + +</div> diff --git a/docs/html-intl/intl/ko/guide/components/intents-filters.jd b/docs/html-intl/intl/ko/guide/components/intents-filters.jd new file mode 100644 index 000000000000..2586200bcc2f --- /dev/null +++ b/docs/html-intl/intl/ko/guide/components/intents-filters.jd @@ -0,0 +1,901 @@ +page.title=인텐트 및 인텐트 필터 +page.tags="IntentFilter" +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + +<h2>이 문서의 내용</h2> +<ol> + <li><a href="#Types">인텐트 유형</a></li> + <li><a href="#Building">인텐트 구축</a> + <ol> + <li><a href="#ExampleExplicit">명시적 인텐트 예시</a></li> + <li><a href="#ExampleSend">암시적 인텐트 예시</a></li> + <li><a href="#ForceChooser">앱 선택기 강제 적용하기</a></li> + </ol> + </li> + <li><a href="#Receiving">암시적 인텐트 수신하기</a> + <ol> + <li><a href="#ExampleFilters">필터 예시</a></li> + </ol> + </li> + <li><a href="#PendingIntent">보류 인텐트 사용하기</a></li> + <li><a href="#Resolution">인텐트 확인</a> + <ol> + <li><a href="#ActionTest">작업 테스트</a></li> + <li><a href="#CategoryTest">카테고리 테스트</a></li> + <li><a href="#DataTest">데이터 테스트</a></li> + <li><a href="#imatch">인텐트 일치</a></li> + </ol> + </li> +</ol> + +<h2>참고 항목</h2> +<ol> +<li><a href="{@docRoot}training/basics/intents/index.html">다른 앱과 상호 작용하기</a></li> +<li><a href="{@docRoot}training/sharing/index.html">콘텐츠 공유하기</a></li> +</ol> + +</div> +</div> + + + + +<p>{@link android.content.Intent}는 일종의 메시지 객체입니다. 이것을 사용해 다른 +<a href="{@docRoot}guide/components/fundamentals.html#Components">앱 구성 요소</a>로부터 작업을 요청할 수 있습니다. +인텐트가 여러 구성 요소 사이의 통신을 용이하게 하는 데에는 몇 가지 방법이 있지만, +기본적인 사용 사례는 다음과 같은 세 가지입니다.</p> + +<ul> +<li><b>액티비티 시작하기:</b> +<p>{@link android.app.Activity}는 앱 안의 화면 하나를 나타냅니다. +{@link android.app.Activity}의 새 인스턴스를 시작하려면 {@link android.content.Intent}를 +{@link android.content.Context#startActivity startActivity()}로 전달하면 됩니다. {@link android.content.Intent}는 +시작할 액티비티를 설명하고 모든 필수 데이터를 담고 있습니다.</p> + +<p>액티비티가 완료되었을 때 결과를 수신하려면, +{@link android.app.Activity#startActivityForResult +startActivityForResult()}를 호출합니다. 액티비티는 해당 결과를 액티비티의 {@link +android.app.Activity#onActivityResult onActivityResult()} 콜백에서 별도의 +{@link android.content.Intent} 객체로 수신합니다. +자세한 정보는 <a href="{@docRoot}guide/components/activities.html">액티비티</a> 가이드를 참조하십시오.</p></li> + +<li><b>서비스 시작하기:</b> +<p>{@link android.app.Service}는 사용자 인터페이스 없이 +배경에서 작업을 수행하는 구성 요소입니다. 서비스를 시작하여 일회성 작업을 수행하도록 하려면(예: 파일 다운로드) +{@link android.content.Intent}를 +{@link android.content.Context#startService startService()}에 전달하면 됩니다. {@link android.content.Intent}는 + 시작할 서비스를 설명하고 모든 필수 데이터를 담고 있습니다.</p> + +<p>서비스가 클라이언트-서버 인터페이스로 설계된 경우, 다른 구성 요소로부터 +서비스에 바인딩하려면 {@link android.content.Intent}를 {@link +android.content.Context#bindService bindService()}에 전달하면 됩니다.</code> 자세한 정보는 <a href="{@docRoot}guide/components/services.html">서비스</a> 가이드를 참조하십시오.</p></li> + +<li><b>브로드캐스트 전달하기:</b> +<p>브로드캐스트는 모든 앱이 수신할 수 있는 메시지입니다. 시스템은 여러 시스템 이벤트에 대해 다양한 +브로드캐스트를 전달합니다. 예를 들어 시스템이 부팅될 때 또는 기기가 변경되기 시작할 때 등이 해당됩니다. +다른 여러 앱에 브로드캐스트를 전달하려면 {@link android.content.Intent}를 +{@link android.content.Context#sendBroadcast(Intent) sendBroadcast()}, +{@link android.content.Context#sendOrderedBroadcast(Intent, String) +sendOrderedBroadcast()} 또는 {@link +android.content.Context#sendStickyBroadcast sendStickyBroadcast()}에 전달하면 됩니다.</p> +</li> +</ul> + + + + +<h2 id="Types">인텐트 유형</h2> + +<p>인텐트에는 두 가지 유형이 있습니다.</p> + +<ul> +<li><b>명시적 인텐트</b>는 시작할 구성 요소를 이름으로 지정합니다(완전히 +정규화된 클래스 이름). 명시적 인텐트는 일반적으로 본인의 앱 안에서 구성 요소를 시작할 때 씁니다. +시작하고자 하는 액티비티 또는 서비스의 클래스 이름을 알고 있기 때문입니다. 예를 들어, +사용자 작업에 응답하여 새 액티비티를 시작하거나 배경에서 파일을 다운로드하기 위해 +서비스를 시작하는 것 등이 여기에 해당됩니다.</li> + +<li><b>암시적 인텐트</b>는 특정 구성 요소의 이름을 대지 않지만, 그 대신 수행할 일반적일 작업을 +선언하여 또 다른 앱의 구성 요소가 이를 처리할 수 있도록 해줍니다. 예를 들어, 사용자에게 지도에 있는 한 위치를 +표시해주고자 하는 경우, 암시적 인텐트를 사용하여 다른, 해당 기능을 갖춘 앱이 +지정된 위치를 지도에 표시하도록 요청할 수 있습니다.</li> +</ul> + +<p>명시적 인텐트를 생성하여 액티비티나 서비스를 시작하도록 하면, 시스템이 즉시 +{@link android.content.Intent} 객체에서 지정된 앱 구성 요소를 시작합니다.</p> + +<div class="figure" style="width:446px"> +<img src="{@docRoot}images/components/intent-filters@2x.png" width="446" alt="" /> +<p class="img-caption"><strong>그림 1.</strong> 암시적 인텐트가 시스템을 통해 전달되어 +다른 액티비티를 시작하게 하는 방법을 그림으로 나타낸 것입니다. <b>[1]</b> <em>액티비티 A</em>가 작업 설명이 들어 있는 +{@link android.content.Intent}를 생성하여 이를 {@link +android.content.Context#startActivity startActivity()}에 전달합니다. <b>[2]</b> Android 시스템이 +해당 인텐트와 일치하는 인텐트 필터를 찾아 모든 앱을 검색합니다. 일치하는 것을 찾으면, <b>[3]</b> 시스템이 +일치하는 액티비티(<em>액티비티 B</em>)를 시작하기 위해 그 액티비티의 {@link +android.app.Activity#onCreate onCreate()} 메서드를 호출하여 이를 {@link android.content.Intent}에 전달합니다. +</p> +</div> + +<p>암시적 인텐트를 생성하면 Android 시스템이 시작시킬 적절한 구성 요소를 찾습니다. +이때 인텐트의 내용을 기기에 있는 다른 여러 앱의 <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">매니페스트 파일</a>에서 선언된 <em>인텐트 필터</em>와 비교하는 방법을 +씁니다. 해당 인텐트와 일치하는 인텐트 필터가 있으면 시스템이 해당 구성 요소를 시작하고 이에 +{@link android.content.Intent} 객체를 전달합니다. 호환되는 인텐트 필터가 여러 개인 경우, 시스템은 +대화를 표시하여 사용자가 어느 앱을 사용할지 직접 선택할 수 있게 합니다.</p> + +<p>인텐트 필터란 앱의 매니페스트 파일에 들어 있는 표현으로, +해당 구성 요소가 수신하고자 하는 인텐트의 유형을 +나타낸 것입니다. 예를 들어 액티비티에 대한 인텐트 필터를 선언하면 +다른 여러 앱이 특정한 종류의 인텐트를 가지고 여러분의 액티비티를 직접 시작할 수 있습니다. +이와 마찬가지로, 액티비티에 대한 인텐트 필터를 전혀 선언하지 <em>않으면</em> 명시적 인텐트로만 +시작할 수 있습니다.</p> + +<p class="caution"><strong>주의:</strong> 앱의 보안을 보장하려면 +{@link android.app.Service}를 시작할 때에는 항상 명시적 인텐트만 사용하고 서비스에 대한 인텐트 필터는 +선언하지 마십시오. 암시적 인텐트를 사용하여 서비스를 시작하면 +보안 위험을 초래합니다. 인텐트에 어느 서비스가 응답할 것인지 확신할 수 없고, 사용자가 +어느 서비스가 시작되는지 볼 수 없기 때문입니다. Android 5.0(API 레벨 21)부터 시스템은 개발자가 암시적 인텐트로 +{@link android.content.Context#bindService bindService()}를 +호출하면 예외를 발생시킵니다.</p> + + + + + +<h2 id="Building">인텐트 구축</h2> + +<p>{@link android.content.Intent} 객체에는 Android 시스템이 +어느 구성 요소를 시작할지 판별하는 데 사용하는 정보가 담겨 있습니다(예를 들어 정확한 구성 요소 이름 또는 인텐트를 +수신해야 하는 구성 요소 카테고리 등). 또한 수신자 구성 요소가 작업을 적절히 수행하기 위해 +사용할 정보(예: 수행할 작업 및 조치를 취할 데이터 위치 등)도 이 안에 담겨 있습니다.</p> + + +<p>{@link android.content.Intent} 내에 들어 있는 기본 정보는 다음과 같습니다.</p> + +<dl> + +<dt><b>구성 요소 이름</b></dt> +<dd>시작할 구성 요소의 이름입니다. + +<p>이것은 선택 항목이지만, 이것이 바로 인텐트를 +<b>명시적</b>인 것으로 만들어주는 중요한 정보입니다. 다시 말해 이 인텐트는 구성 요소 이름이 정의한 앱 구성 요소에만 +전달되어야 한다는 뜻입니다. 구성 요소 이름이 없으면 해당 인텐트는 <b>암시적</b>이며, +인텐트를 수신해야 하는 구성 요소는 다른 인텐트 정보를 기반으로 시스템이 +결정합니다(예를 들어 작업, 데이터 및 카테고리 — 아래 설명 참조). 따라서 앱에서 특정한 구성 요소를 +시작해야 하는 경우에는, 구성 요소 이름을 지정해야 합니다.</p> + +<p class="note"><strong>참고:</strong> {@link android.app.Service}를 시작하는 경우, +<strong>항상 구성 요소 이름을 지정</strong>해야 합니다. 그렇지 않으면 인텐트에 어느 서비스가 응답할지 확신할 수 없고, +사용자도 어느 서비스가 시작되는지 볼 수 없게 됩니다.</p> + +<p>{@link android.content.Intent}의 필드는 +{@link android.content.ComponentName} 객체로, 이것을 지정하려면 대상 구성 요소의 완전히 +정규화된 클래스 이름(앱의 패키지 이름 포함)을 사용해야 합니다. 예를 들면 +{@code com.example.ExampleActivity}를 쓰십시오. 구성 요소 이름을 설정하려면 {@link +android.content.Intent#setComponent setComponent()}, {@link android.content.Intent#setClass +setClass()}, {@link android.content.Intent#setClassName(String, String) setClassName()}을 사용하거나, 아니면 +{@link android.content.Intent} 생성자를 사용하면 됩니다.</p> + +</dd> + +<p><dt><b>작업</b></dt> +<dd>수행할 일반적인 작업을 나타내는 문자열입니다(예: <em>보기</em> 또는 <em>선택</em>). + +<p>브로드캐스트 인텐트의 경우, 이것이 발생한 작업이자 보고되는 것이기도 합니다. +인텐트의 나머지 부분이 어떻게 구조화될지는 대부분 작업이 결정합니다. 특히, +데이터 안에 들어 있는 것과 추가 항목 등이 해당됩니다. + +<p>본인의 앱 내에 있는 인텐트가 사용할 작업(또는 다른 앱이 사용하여 +본인의 앱 안의 구성 요소를 호출하도록 함)을 직접 지정할 수도 있지만, 보통은 +{@link android.content.Intent} 클래스나 다른 프레임워크 클래스가 정의한 작업 상수를 써야 합니다. 다음은 +액티비티를 시작하는 데 쓰이는 보편적인 작업입니다.</p> + +<dl> +<dt>{@link android.content.Intent#ACTION_VIEW}</dt> + <dd>이 작업은 액티비티가 사용자에게 표시할 수 있는 어떤 정보를 가지고 있을 때 {@link + android.content.Context#startActivity startActivity()}가 있는 인텐트에서 +사용합니다. 예를 들어 갤러리 앱에서 볼 사진이나 지도 앱에서 볼 주소 등이 +이에 해당됩니다.</dd> + +<dt>{@link android.content.Intent#ACTION_SEND}</dt> + <dd>이것은 다른 이름으로 "공유" 인텐트라고도 합니다. 이것은 사용자가 다른 앱을 통해 공유할 수 있는 데이터를 가지고 있을 때 {@link + android.content.Context#startActivity startActivity()}가 있는 인텐트에서 사용합니다. 예를 들어 +이메일 앱 또는 소셜 공유 앱 등이 이에 해당됩니다.</dd> +</dl> + +<p>일반적인 작업을 정의하는 상수에 대한 자세한 내용은 {@link android.content.Intent} 클래스 +참조를 확인하십시오. 다른 작업은 Android 프레임워크의 다른 곳에서 정의됩니다. 예를 들어 +시스템의 설정 앱에서 특정 화면을 여는 작업의 경우 {@link android.provider.Settings}에서 +정의됩니다.</p> + +<p>인텐트에 대한 작업을 지정하려면 {@link android.content.Intent#setAction +setAction()} 또는 {@link android.content.Intent} 생성자를 사용하면 됩니다.</p> + +<p>나름의 작업을 직접 정의하는 경우, 앱의 패키지 이름을 접두사로 포함시켜야 +합니다. 예:</p> +<pre>static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";</pre> +</dd> + +<dt><b>데이터</b></dt> +<dd>작업을 수행할 데이터 및/또는 해당 데이터의 MIME 유형을 참조하는 URI({@link android.net.Uri} +객체)입니다. 제공된 데이터의 유형을 나타내는 것은 일반적으로 인텐트의 작업입니다. 예를 들어 +인텐트가 {@link android.content.Intent#ACTION_EDIT}인 경우, 데이터에 +편집할 문서의 URI가 들어있어야 합니다. + +<p>인텐트를 생성할 때에는 +URI외에도 데이터의 유형(MIME 유형)을 지정하는 것이 중요한 경우가 많습니다. +예를 들어, 이미지를 표시할 수 있는 액티비티는 아마도 오디오 파일을 재생할 수 없을 가능성이 +큽니다. 두 가지 기능의 URI 형식은 비슷할지라도 말입니다. +그러므로 데이터의 MIME 유형을 지정해두면 Android 시스템이 +인텐트를 수신할 최상의 구성 요소를 찾는 데 도움이 됩니다. +하지만, MIME 유형은 URI에서 추론되는 경우도 종종 있습니다. 특히 데이터가 +{@code content:} URI인 경우 더욱 그러한데, 이는 데이터가 기기에 위치하고 있으며 +{@link android.content.ContentProvider}가 제어한다는 것을 나타내어 해당 데이터 MIME 유형이 시스템에 표시되도록 합니다.</p> + +<p>데이터 URI만 설정하려면 {@link android.content.Intent#setData setData()}를 호출하십시오. +MIME 유형만 설정하려면, {@link android.content.Intent#setType setType()}을 호출합니다. 필요한 경우, +두 가지를 모두 명시적으로 설정해도 됩니다. 이때는 {@link +android.content.Intent#setDataAndType setDataAndType()}을 사용하십시오.</p> + +<p class="caution"><strong>주의:</strong> URI와 MIME 유형을 둘 다 설정하고자 하는 경우, {@link android.content.Intent#setData setData()} 및 +{@link android.content.Intent#setType setType()}을 호출하면 +<strong>안 됩니다</strong>. 이 둘은 서로의 값을 무효화하기 때문입니다. URI와 MIME 유형을 둘 모두 설정하려면 +항상 {@link android.content.Intent#setDataAndType setDataAndType()}을 +사용하십시오.</p> +</dd> + +<p><dt><b>카테고리</b></dt> +<dd>인텐트를 처리해야 하는 구성 요소의 종류에 관한 추가 정보를 담은 +문자열입니다. 인텐트 안에는 카테고리 설명이 +얼마든지 들어있을 수 있지만, 대부분의 인텐트에는 카테고리가 없어도 됩니다. +다음은 몇 가지 보편적인 카테고리입니다. + +<dl> +<dt>{@link android.content.Intent#CATEGORY_BROWSABLE}</dt> + <dd>대상 액티비티가 스스로 웹 브라우저가 자신을 시작해도 되도록 허용하여 +링크로 참조된 데이터를 표시하게 합니다. 예컨대 이미지나 이메일 메시지 등이 이에 해당합니다. + </dd> +<dt>{@link android.content.Intent#CATEGORY_LAUNCHER}</dt> + <dd>이 액티비티가 작업의 최초 액티비티이며, 이것이 시스템의 애플리케이션 시작 관리자에 +목록으로 게재되어 있습니다. + </dd> +</dl> + +<p>카테고리의 전체 목록을 보려면 {@link android.content.Intent} +클래스 설명을 참조하십시오.</p> + +<p>카테고리를 지정하려면 {@link android.content.Intent#addCategory addCategory()}를 사용하면 됩니다.</p> +</dd> +</dl> + + +<p>위에 목록으로 나열된 이러한 특성(구성 요소 이름, 작업, 데이터 및 카테고리)은 인텐트를 정의하는 특성을 +나타냅니다. Android 시스템은 이와 같은 속성을 읽어 +어느 앱 구성 요소를 시작해야 할지 확인할 수 있습니다.</p> + +<p>그러나 인텐트는 앱 구성 요소로 확인되는 방법에 영향을 미치지 않는 +추가 정보도 담고 있을 수 있습니다. 인텐트가 제공할 수 있는 기타 정보는 다음과 같습니다.</p> + +<dl> +<dt><b>추가 사항</b></dt> +<dd>요청한 작업을 수행하기 위해 필요한 추가 정보를 담고 있는 키-값 쌍입니다. +몇몇 작업이 특정한 종류의 데이터 URI를 사용하는 것과 마찬가지로, 몇몇 작업은 특정한 추가 사항도 사용합니다. + +<p>추가 데이터를 추가하려면 여러 가지 {@link android.content.Intent#putExtra putExtra()} 메서드를 +사용할 수 있습니다. 이들은 각기 두 개의 매개변수를 허용합니다. 즉 키 이름과 값입니다. +모든 추가 데이터를 가진 {@link android.os.Bundle} 객체를 생성할 수도 있고, 그런 다음 +{@link android.os.Bundle}을 {@link android.content.Intent}에 {@link +android.content.Intent#putExtras putExtras()}로 삽입해도 됩니다.</p> + +<p>예를 들어 +{@link android.content.Intent#ACTION_SEND}로 이메일을 전송할 인텐트를 생성하는 경우 "받는 사람" 수신자를 지정할 때 +{@link android.content.Intent#EXTRA_EMAIL} 키를 사용한 다음 "제목"은 +{@link android.content.Intent#EXTRA_SUBJECT} 키로 지정하면 됩니다.</p> + +<p>{@link android.content.Intent} 클래스는 표준화된 데이터 유형에 대해 수많은 {@code EXTRA_*} 상수를 +지정합니다. 나름의 추가 키를 선언해야 하는 경우(본인의 앱이 수신할 +인텐트에 대하여), 앱의 패키지 이름을 접두사로 포함시커야 +합니다. 예:</p> +<pre>static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";</pre> +</dd> + +<dt><b>플래그</b></dt> +<dd>{@link android.content.Intent} 클래스에서 정의된 플래그로, 인텐트에 대한 메타데이터와 같은 기능을 +합니다. 이런 플래그는 Android 시스템에 액티비티를 시작할 방법에 대한 지침을 줄 수도 있고(예를 들어 액티비티가 어느 +<a href="{@docRoot}guide/components/tasks-and-back-stack.html">작업</a>에 소속되어야 하는지 등) + 액티비티를 시작한 다음에 어떻게 처리해야 하는지도 알려줄 수 있습니다(예를 들어 해당 액티비티가 최근 +액티비티 목록에 소속되는지 여부). + +<p>자세한 정보는 {@link android.content.Intent#setFlags setFlags()} 메서드를 참조하십시오.</p> +</dd> + +</dl> + + + + +<h3 id="ExampleExplicit">명시적 인텐트 예시</h3> + +<p>명시적 인텐트는 특정한 앱 구성 요소를 시작하기 위해 사용하는 것입니다. 예를 들어 앱 내의 +특정 액티비티나 서비스를 말합니다. 명시적 인텐트를 생성하려면 +{@link android.content.Intent} 객체에 대한 구성 요소 이름을 정의합니다. 다른 +인텐트 속성은 모두 선택 사항입니다.</p> + +<p>예를 들어 앱 안에 {@code DownloadService}라는 서비스를 구축했다고 합시다. +이 서비스는 웹 상에서 파일을 다운로드하도록 설계된 것입니다. 이것을 시작하려면 다음과 같은 코드를 사용하면 됩니다.</p> + +<pre> +// Executed in an Activity, so 'this' is the {@link android.content.Context} +// The fileUrl is a string URL, such as "http://www.example.com/image.png" +Intent downloadIntent = new Intent(this, DownloadService.class); +downloadIntent.setData({@link android.net.Uri#parse Uri.parse}(fileUrl)); +startService(downloadIntent); +</pre> + +<p>{@link android.content.Intent#Intent(Context,Class)} + 생성자가 앱에 {@link android.content.Context}를 제공하고 +구성 요소에 {@link java.lang.Class} 객체를 제공합니다. 이처럼, +이 인텐트는 앱 내의 {@code DownloadService} 클래스를 명시적으로 시작합니다.</p> + +<p>서비스를 구축하고 시작하는 데 대한 자세한 정보는 +<a href="{@docRoot}guide/components/services.html">서비스</a> 가이드를 참조하십시오.</p> + + + + +<h3 id="ExampleSend">암시적 인텐트 예시</h3> + +<p>암시적 인텐트는 작업을 지정하여 기기에서 해당 작업을 수행할 수 있는 모든 앱을 호출할 수 +있도록 합니다. 암시적 인텐트를 사용하면 본인의 앱은 작업을 수행할 수 없지만 다른 앱은 아마도 할 수 있을 때, +그리고 사용자로 하여금 어느 앱을 사용할지 선택하도록 하고자 할 때 유용합니다.</p> + +<p>예를 들어 사용자가 다른 사람들과 공유했으면 하는 콘텐츠를 가지고 있는 경우, +{@link android.content.Intent#ACTION_SEND} 작업이 있는 인텐트를 생성한 다음 +공유할 콘텐츠를 지정하는 추가 정보를 추가하면 됩니다. 해당 인텐트로 +{@link android.content.Context#startActivity startActivity()}를 호출하면 +사용자가 어느 앱을 통해 콘텐츠를 공유할지 선택할 수 있습니다.</p> + +<p class="caution"><strong>주의:</strong> 개발자가 {@link android.content.Context#startActivity +startActivity()}로 전송한 암시적 인텐트를 처리할 앱이 사용자에게 <em>전혀</em> +표시되지 않을 수도 있습니다. 이런 일이 발생하면, 호출이 실패하고 앱 작동이 중단됩니다. 어느 액티비티든 +이 인텐트를 수신하도록 확실히 하려면, {@link android.content.Intent} 객체의 {@link android.content.Intent#resolveActivity +resolveActivity()}를 호출합니다. 결과가 null이 아닌 경우, +인텐트를 처리할 수 있는 앱이 최소한 하나는 있다는 뜻이며 +{@link android.content.Context#startActivity startActivity()}를 호출해도 안전합니다. 결과가 null이면, +해당 인텐트를 사용해서는 안 되며 가능한 경우 해당 인텐트를 발생시키는 기능을 비활성화해야 +합니다.</p> + + +<pre> +// Create the text message with a string +Intent sendIntent = new Intent(); +sendIntent.setAction(Intent.ACTION_SEND); +sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage); +sendIntent.setType({@link + org.apache.http.protocol.HTTP#PLAIN_TEXT_TYPE + HTTP.PLAIN_TEXT_TYPE}); // "text/plain" MIME type + +// Verify that the intent will resolve to an activity +if (sendIntent.resolveActivity(getPackageManager()) != null) { + startActivity(sendIntent); +} +</pre> + +<p class="note"><strong>참고:</strong> 이 경우에서는 URI를 사용하지 않았지만 인텐트의 데이터 유형이 정의되어 +추가 정보가 담고 있는 콘텐츠를 지정하였습니다.</p> + + +<p>{@link android.content.Context#startActivity startActivity()}를 호출하면 시스템이 +설치된 앱을 모두 살펴보고 이런 종류의 인텐트를 처리할 수 있는 앱이 어느 것인지 알아봅니다( +{@link android.content.Intent#ACTION_SEND} 작업이 있는 인텐트이며 "텍스트/일반" +데이터가 담긴 것). 이것을 처리할 수 있는 앱이 하나뿐이면, 해당 앱이 즉시 열리고 이 앱에 인텐트가 +주어집니다. 인텐트를 허용하는 액티비티가 여러 개인 경우, 시스템은 +대화를 표시하여 사용자가 어느 앱을 사용할지 직접 선택할 수 있게 합니다.</p> + + +<div class="figure" style="width:200px"> + <img src="{@docRoot}images/training/basics/intent-chooser.png" alt=""> + <p class="img-caption"><strong>그림 2.</strong> 선택기 대화입니다.</p> +</div> + +<h3 id="ForceChooser">앱 선택기 강제 적용하기</h3> + +<p>암시적 인텐트에 응답하는 앱이 하나 이상인 경우, +사용자가 어느 앱을 사용할지 선택할 수 있으며 해당 앱을 이 작업에 대한 기본 선택으로 만들 수 +있습니다. 이는 사용자가 이제부터 계속 같은 앱을 사용하고자 할 것이라고 추정되는 작업을 +수행할 때 좋습니다. 예를 들어 웹 페이지를 열 때가 이에 해당됩니다(대다수 사용자가 +웹 브라우저는 하나만 사용하는 것을 선호합니다).</p> + +<p>그러나, 인텐트에 응답할 수 있는 앱이 여러 개이고 사용자가 매번 다른 앱을 사용하기를 원할 수도 있는 +경우라면, 선택기 대화를 명시적으로 표시해야 합니다. 선택기 대화 상자는 +사용자가 작업에 사용할 앱을 매번 선택하도록 물어봅니다(사용자는 작업에 사용할 +기본 앱을 선택할 수 없습니다). 예를 들어 앱이 {@link +android.content.Intent#ACTION_SEND} 작업에 "공유"를 수행하는 경우, 사용자는 각자의 현재 상황에 따라 여러 가지 다른 앱을 사용하여 공유하기를 +원할 수도 있습니다. 따라서 항상 선택기 대화를 사용해야 합니다(그림 2 참조).</p> + + + + +<p>선택기를 표시하려면 {@link +android.content.Intent#createChooser createChooser()}를 사용하여 {@link android.content.Intent}를 생성한 후 {@link +android.app.Activity#startActivity startActivity()}에 전달합니다. 예:</p> + +<pre> +Intent sendIntent = new Intent(Intent.ACTION_SEND); +... + +// Always use string resources for UI text. +// This says something like "Share this photo with" +String title = getResources().getString(R.string.chooser_title); +// Create intent to show the chooser dialog +Intent chooser = Intent.createChooser(sendIntent, title); + +// Verify the original intent will resolve to at least one activity +if (sendIntent.resolveActivity(getPackageManager()) != null) { + startActivity(chooser); +} +</pre> + +<p>이 예시는 {@link +android.content.Intent#createChooser createChooser()} 메서드에 전달된 인텐트에 응답하는 앱 목록이 포함된 대화 상자를 표시하고 제공된 텍스트를 +대화 상자 제목으로 사용합니다.</p> + + + + + + + + + +<h2 id="Receiving">암시적 인텐트 수신하기</h2> + +<p>본인의 앱이 수신할 수 있는 암시적 인텐트가 어느 것인지 알리려면, +각 앱 구성 요소에 대한 하나 이상의 인텐트 필터를 <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a> + 요소로 <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">매니페스트 파일</a>에 선언합니다. +각 인텐트 필터가 인텐트의 작업, 데이터 및 카테고리를 근거로 어느 유형의 인텐트를 +허용하는지 나타냅니다. 시스템이 앱 구성 요소에 암시적 인텐트를 전달하는 것은 인텐트가 개발자의 인텐트 필터 중 하나를 +통과해 지나갈 수 있는 경우뿐입니다.</p> + +<p class="note"><strong>참고:</strong> 명시적 인텐트는 항상 자신의 대상에 전달되며, +이는 구성 요소가 어떤 인텐트 필터를 선언하든 무관합니다.</p> + +<p>앱 구성 요소는 자신이 수행할 수 있는 각 고유한 작업에 대해 별도의 필터를 선언해야 합니다. +예를 들어 이미지 갤러리 앱에 있는 한 액티비티에 두 개의 필터가 있을 수 있습니다. 필터 하나는 +이미지를 보고, 또 다른 필터는 이미지를 편집하는 것입니다. 액티비티가 시작되면, 이는 +{@link android.content.Intent}를 검사한 다음 +{@link android.content.Intent}에 있는 정보를 근거로 어떻게 동작할 것인지 결정합니다(편집기 제어 항목을 표시할 것인지 말 것인지 등).</p> + +<p>각 인텐트 필터는 앱의 매니페스트 파일에 있는 <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a> +요소가 정의하며, 이는 상응하는 앱 구성 요소에 중첩되어 +있습니다(예: <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> +요소). <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a> 내부에서는 +다음과 같은 세 가지 요소 중 하나 이상을 사용하여 허용할 인텐트 유형을 지정할 수 +있습니다.</p> + +<dl> +<dt><a href="{@docRoot}guide/topics/manifest/action-element.html">{@code <action>}</a></dt> + <dd>허용된 인텐트 작업을 {@code name} 속성에서 선언합니다. 이 값은 +어떤 작업의 리터럴 문자열 값이어야 하며, 클래스 상수가 아닙니다.</dd> +<dt><a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a></dt> + <dd>허용된 데이터 유형을 선언합니다. 이때 +데이터 URI(<code>scheme</code>, <code>host</code>, <code>port</code>, +<code>path</code> 등)와 MIME 유형의 여러 가지 측면을 나타내는 하나 이상의 속성을 사용하는 방법을 씁니다.</dd> +<dt><a href="{@docRoot}guide/topics/manifest/category-element.html">{@code <category>}</a></dt> + <dd>허용된 인텐트 카테고리를 {@code name} 속성에서 선언합니다. 이 값은 +어떤 작업의 리터럴 문자열 값이어야 하며, 클래스 상수가 아닙니다. + + <p class="note"><strong>참고:</strong> 암시적 인텐트를 수신하려면 인텐트 필터 안에 + +{@link android.content.Intent#CATEGORY_DEFAULT} 카테고리를 <strong>반드시 포함</strong>해야 합니다. +{@link android.app.Activity#startActivity startActivity()} 및 +{@link android.app.Activity#startActivityForResult startActivityForResult()} 메서드는 +모든 인텐트를 마치 {@link android.content.Intent#CATEGORY_DEFAULT} 카테고리를 선언한 것처럼 취급합니다. + 이 카테고리를 인텐트 필터에서 선언하지 않으면 액티비티에 어떤 암시적 인텐트도 +확인되지 않습니다.</p> + </dd> +</dl> + +<p>예를 들어, 다음은 데이터 유형이 텍스트인 경우 +{@link android.content.Intent#ACTION_SEND} 인텐트를 수신할 인텐트 필터가 있는 액티비티 선언입니다.</p> + +<pre> +<activity android:name="ShareActivity"> + <intent-filter> + <action android:name="android.intent.action.SEND"/> + <category android:name="android.intent.category.DEFAULT"/> + <data android:mimeType="text/plain"/> + </intent-filter> +</activity> +</pre> + +<p> +<a href="{@docRoot}guide/topics/manifest/action-element.html">{@code <action>}</a>, +<a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a> 또는 +<a href="{@docRoot}guide/topics/manifest/category-element.html">{@code <category>}</a>의 인스턴스가 하나 이상 포함된 필터를 생성해도 됩니다. +그렇게 하는 경우, 그저 해당 구성 요소가 그와 같은 필터 요소의 모든 조합을 처리할 수 있도록 +확실히 하기만 하면 됩니다.</p> + +<p>여러 가지 종류의 인텐트를 처리하고자 하되 특정 조합의 작업, 데이터 및 카테고리 유형으로만 한정하고자 한다면 +여러 가지 인텐트 필터를 생성해야 합니다.</p> + + +<div class="sidebox-wrapper"> +<div class="sidebox"> +<h2>구성 요소로의 액세스 제한</h2> +<p>인텐트 필터를 사용하는 것은 다른 앱이 여러분의 구성 요소를 시작하지 못하도록 방지하기에 +안전한 방식은 아닙니다. 인텐트 필터는 구성 요소로 하여금 특정한 종류의 암시적 인텐트에만 +응답하도록 제한하기는 하지만, 다른 앱이 여러분의 앱 구성 요소를 시작할 가능성도 있습니다. +해당 개발자가 여러분의 구성 요소 이름을 알아내는 경우 명시적 인텐트를 사용하면 됩니다. +구성 요소 중 하나를 시작하는 것은 <em>오직 본인 소유의 앱</em>으로 한정하는 것이 중요한 경우, 해당 구성 요소에 대하여 +<a href="{@docRoot}guide/topics/manifest/activity-element.html#exported">{@code +exported}</a> 속성을 {@code "false"}로 설정하십시오. +</p> +</div> +</div> + +<p>암시적 인텐트를 필터에 대해 테스트하려면 인텐트를 세 가지 요소에 대해 각기 +비교합니다. 인텐트가 구성 요소에 전달되려면 해당 인텐트는 세 개의 테스트를 모두 통과해야 합니다. +하나라도 일치하지 못하고 실패하면 Android 시스템이 해당 인텐트를 구성 요소에 전달하지 +않습니다. 그러나 구성 요소에는 여러 개의 인텐트 필터가 있을 수도 있으므로, 구성 요소의 +필터 중 하나를 통과하지 않는 인텐트도 다른 필터를 통하면 성공할 수 있습니다. +시스템이 인텐트를 확인하는 방법에 대한 자세한 정보는 +<a href="#Resolution">인텐트 확인</a>에 대해 다룬 아래 섹션에 제공되어 있습니다.</p> + +<p class="caution"><strong>주의:</strong> 부주의로 다른 앱의 +{@link android.app.Service}를 실행하는 일을 피하려면, 항상 명시적 인텐트를 사용하여 본인의 서비스를 시작하고 서비스에 대한 +인텐트 필터는 선언하지 마십시오.</p> + +<p class="note"><strong>참고:</strong> +액티비티 전체에 대해 인텐트 필터를 매니페스트 파일에서 선언해야 합니다. +다만 브로드캐스트 수신기에 대한 필터의 경우 동적으로 등록할 수도 있습니다. +{@link android.content.Context#registerReceiver(BroadcastReceiver, IntentFilter, String, +Handler) registerReceiver()}를 호출하면 됩니다. 그런 다음 해당 수신기를 등록 해제하려면 {@link +android.content.Context#unregisterReceiver unregisterReceiver()}를 사용하십시오. 이렇게 하면 앱이 +실행되는 동안에 정해진 기간 동안 특정한 브로드캐스트에 대해 수신 대기할 수 +있습니다.</p> + + + + + + + +<h3 id="ExampleFilters">필터 예시</h3> + +<p>몇 가지 인텐트 필터 동작에 대해 이해를 돕기 위해 소셜 공유 앱의 매니페스트 파일에서 +가져온 다음 조각을 참조하십시오.</p> + +<pre> +<activity android:name="MainActivity"> + <!-- This activity is the main entry, should appear in app launcher --> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> +</activity> + +<activity android:name="ShareActivity"> + <!-- This activity handles "SEND" actions with text data --> + <intent-filter> + <action android:name="android.intent.action.SEND"/> + <category android:name="android.intent.category.DEFAULT"/> + <data android:mimeType="text/plain"/> + </intent-filter> + <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data --> + <intent-filter> + <action android:name="android.intent.action.SEND"/> + <action android:name="android.intent.action.SEND_MULTIPLE"/> + <category android:name="android.intent.category.DEFAULT"/> + <data android:mimeType="application/vnd.google.panorama360+jpg"/> + <data android:mimeType="image/*"/> + <data android:mimeType="video/*"/> + </intent-filter> +</activity> +</pre> + +<p>첫 번째 액티비티인 {@code MainActivity}는 앱의 주요 진입 지점입니다. 즉 이것이 +사용자가 시작 관리자 아이콘을 사용하여 앱을 처음 시작할 때 열리는 액티비티입니다.</p> +<ul> + <li>{@link android.content.Intent#ACTION_MAIN} 작업은 +이것이 주요 진입 지점이며 어느 인텐트 데이터도 기대하지 않는다는 것을 나타냅니다.</li> + <li>{@link android.content.Intent#CATEGORY_LAUNCHER} 카테고리는 +이 액티비티의 아이콘이 시스템의 앱 시작 관리자에 배치되어야 한다는 것을 나타냅니다. <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> 요소가 +아이콘을 {@code icon}으로 지정하지 않은 경우, 시스템은 <a href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a> + 요소로부터 가져온 아이콘을 사용합니다.</li> +</ul> +<p>이들 두 가지가 함께 페어링되어야 액티비티가 앱 시작 관리자에 나타날 수 있습니다.</p> + +<p>두 번째 액티비티인 {@code ShareActivity}는 텍스트와 미디어 콘텐츠 공유를 +용이하게 할 목적으로 만들어진 것입니다. 사용자가 {@code MainActivity}에서 이 액티비티를 향해 이동하다가 이 안에 진입할 수도 있지만, +두 가지 인텐트 필터 중 하나와 일치하는 암시적 인텐트를 발생시키는 또 다른 앱에서 {@code ShareActivity}에 직접 진입할 수도 +있습니다.</p> + +<p class="note"><strong>참고:</strong> MIME 유형, 즉 +<a href="https://developers.google.com/panorama/android/">{@code +application/vnd.google.panorama360+jpg}</a>는 파노라마 사진을 나타내는 +특수 데이터 유형으로, 이것은 <a href="{@docRoot}reference/com/google/android/gms/panorama/package-summary.html">Google +파노라마</a> API로 처리할 수 있습니다.</p> + + + + + + + + + + + + + +<h2 id="PendingIntent">보류 인텐트 사용하기</h2> + +<p>{@link android.app.PendingIntent} 객체는 {@link +android.content.Intent} 객체 주변을 감싸는 래퍼입니다. {@link android.app.PendingIntent}의 +기본 목적은 외래 애플리케이션에 권한을 허가하여 안에 들어 있는 +{@link android.content.Intent}를 +마치 본인 앱의 자체 프로세스에서 실행하는 것처럼 사용하게 하는 것입니다.</p> + +<p>보류 인텐트의 주요 사용 사례는 다음과 같습니다.</p> +<ul> + <li>사용자가 여러분의 <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">알림</a>으로 +작업을 수행할 때 인텐트가 실행되도록 선언합니다(Android 시스템의 {@link android.app.NotificationManager}가 +{@link android.content.Intent}를 실행합니다). + <li>사용자가 여러분의 +<a href="{@docRoot}guide/topics/appwidgets/index.html">앱 위젯</a>으로 +작업을 수행할 때 인텐트가 실행되도록 선언합니다(메인 스크린 앱이 {@link android.content.Intent}를 실행합니다). + <li>향후 지정된 시간에 인텐트가 실행되도록 선언합니다(Android +시스템의 {@link android.app.AlarmManager}가 {@link android.content.Intent}를 실행합니다). +</ul> + +<p>각 {@link android.content.Intent} 객체는 특정한 유형의 +앱 구성 요소가({@link android.app.Activity}, {@link android.app.Service} 또는 +{@link android.content.BroadcastReceiver}) 처리하도록 설계되어 있기 때문에, {@link android.app.PendingIntent}도 +같은 고려 사항을 염두에 두고 생성해야 합니다. 보류 인텐트를 사용하는 경우, 여러분의 앱은 +{@link android.content.Context#startActivity +startActivity()}와 같은 호출이 있는 앱을 실행하지 않게 됩니다. 대신 +{@link android.app.PendingIntent}를 생성할 때 원래 의도한 구성 요소 유형을 선언해야 합니다. 이때 각각의 생성자 메서드를 호출하는 방법을 씁니다.</p> + +<ul> + <li>{@link android.app.Activity}를 시작하는 {@link android.content.Intent}의 경우, +{@link android.app.PendingIntent#getActivity PendingIntent.getActivity()}</li> + <li>{@link android.app.Service}를 시작하는 {@link android.content.Intent}의 경우, +{@link android.app.PendingIntent#getService PendingIntent.getService()}</li> + <li>{@link android.content.BroadcastReceiver}를 시작하는 {@link android.content.Intent}의 경우, +{@link android.app.PendingIntent#getBroadcast PendingIntent.getBroadcast()}</li> +</ul> + +<p>여러분의 앱이 다른 여러 앱에서 보류 인텐트를 <em>수신</em>하는 것이 아닌 한, +위의 메서드를 사용하여 {@link android.app.PendingIntent}를 생성하는 것이 아마도 여러분에게 필요한 유일한 +{@link android.app.PendingIntent} 메서드일 것입니다.</p> + +<p>각 메서드는 현재 앱의 {@link android.content.Context}, 래핑하고자 하는 +{@link android.content.Intent}와 인텐트의 적절한 사용 방식을 나타내는 + 하나 이상의 플래그(인텐트를 한 번 이상 사용할 수 있는지 등) 등을 취합니다.</p> + +<p>보류 인텐트 사용에 관한 자세한 정보는 각 해당되는 사용 사례에 대한 문서에 +제공되어 있습니다. 예를 들어 <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">알림</a> + 및 <a href="{@docRoot}guide/topics/appwidgets/index.html">앱 위젯</a> API 가이드가 이에 해당됩니다.</p> + + + + + + + +<h2 id="Resolution">인텐트 확인</h2> + + +<p>시스템이 액티비티를 시작하라는 암시적 인텐트를 수신하면, 시스템은 해당 인텐트에 대한 최선의 액티비티를 검색합니다. +이때 다음과 같은 세 가지 측면을 근거로 인텐트를 인텐트 필터에 비교해 보는 방법을 씁니다.</p> + +<ul> + <li>인텐트 작업 + <li>인텐트 데이터(URI와 데이터 유형 둘 다) + <li>인텐트 카테고리 +</ul> + +<p>다음 섹션에서는 인텐트 필터가 앱의 매니페스트 파일에서 어떻게 선언되었는지와 관련하여 +인텐트가 적절한 구성 요소에 일치되는 방식을 설명합니다.</p> + + +<h3 id="ActionTest">작업 테스트</h3> + +<p>허용된 인텐트 작업을 나타내려면 인텐트 필터는 0개 이상의 +<a href="{@docRoot}guide/topics/manifest/action-element.html">{@code +<action>}</a> 요소를 선언할 수 있습니다. 예:</p> + +<pre> +<intent-filter> + <action android:name="android.intent.action.EDIT" /> + <action android:name="android.intent.action.VIEW" /> + ... +</intent-filter> +</pre> + +<p>이 필터를 통과하려면 {@link android.content.Intent}에 +지정된 작업이 필터에 목록으로 나열된 작업 중 하나와 일치해야만 합니다.</p> + +<p>필터에 목록으로 나열된 작업이 없는 경우, 인텐트가 일치될 대상이 아무것도 없으므로 +모든 인텐트가 테스트에 실패합니다. 그러나, {@link android.content.Intent}가 +작업을 지정하지 않는 경우, 테스트를 통과하게 됩니다(필터에 최소한 한 개 이상의 작업이 +들어있지 않은 한).</p> + + + +<h3 id="CategoryTest">카테고리 테스트</h3> + +<p>허용된 인텐트 카테고리를 나타내려면 인텐트 필터는 0개 이상의 +<a href="{@docRoot}guide/topics/manifest/category-element.html">{@code +<category>}</a> 요소를 선언할 수 있습니다. 예:</p> + +<pre> +<intent-filter> + <category android:name="android.intent.category.DEFAULT" /> + <category android:name="android.intent.category.BROWSABLE" /> + ... +</intent-filter> +</pre> + +<p>인텐트가 카테고리 테스트를 통과하려면 {@link android.content.Intent} 내의 +모든 카테고리가 필터 내의 카테고리에 일치해야 합니다. 이 반대로는 반드시 성립하지 않아도 됩니다. 인텐트 필터가 +{@link android.content.Intent}에서 지정된 것보다 더 많은 카테고리를 선언할 수도 있지만 그래도 +{@link android.content.Intent}는 통과합니다. 그러므로, 카테고리가 없는 인텐트라면 이 테스트를 항상 통과하는 것이 맞습니다. +이는 필터에 어떤 카테고리가 선언되어 있는지와는 무관합니다.</p> + +<p class="note"><strong>참고:</strong> +Android는 {@link android.content.Intent#CATEGORY_DEFAULT} 카테고리를 +{@link +android.content.Context#startActivity startActivity()} 및 {@link +android.app.Activity#startActivityForResult startActivityForResult()}에 전달된 모든 암시적 인텐트에 적용합니다. +따라서 액티비티가 암시적 인텐트를 수신하기를 원하는 경우, +그 인텐트 필터 내에 {@code "android.intent.category.DEFAULT"}에 대한 카테고리가 반드시 포함되어 있어야 합니다(이전의 +{@code <intent-filter>} 예시에서 표시된 내용 참조).</p> + + + +<h3 id="DataTest">데이터 테스트</h3> + +<p>허용된 인텐트 데이터를 나타내려면 인텐트 필터는 0개 이상의 +<a href="{@docRoot}guide/topics/manifest/data-element.html">{@code +<data>}</a> 요소를 선언할 수 있습니다. 예:</p> + +<pre> +<intent-filter> + <data android:mimeType="video/mpeg" android:scheme="http" ... /> + <data android:mimeType="audio/mpeg" android:scheme="http" ... /> + ... +</intent-filter> +</pre> + +<p>각 <code><a href="{@docRoot}guide/topics/manifest/data-element.html"><data></a></code> +요소는 URI 구조와 데이터 유형(MIME 미디어 유형)을 나타낼 수 있습니다. URI의 각 부분에 대해 별도의 +속성—{@code scheme}, {@code host}, {@code port} +및 {@code path}—이 있습니다. +</p> + +<p style="margin-left: 2em">{@code <scheme>://<host>:<port>/<path>}</p> + +<p> +예: +</p> + +<p style="margin-left: 2em">{@code content://com.example.project:200/folder/subfolder/etc}</p> + +<p>이 URI에서 구성표는 {@code content}이고 호스트는 {@code com.example.project}, +포트는 {@code 200}이며 경로는 {@code folder/subfolder/etc}입니다. +</p> + +<p>이와 같은 속성은 각각 <a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a> 요소에서 선택 항목이지만, +이들 사이에 선형적 종속 관계가 있습니다.</p> +<ul> + <li>구성표가 지정되지 않으면 호스트가 무시됩니다.</li> + <li>호스트가 지정되지 않으면 포트가 무시됩니다.</li> + <li>구성표와 호스트가 둘 다 지정되지 않으면 경로가 무시됩니다.</li> +</ul> + +<p>인텐트 안의 URI가 필터 안의 URI 사양에 비교되는 경우, 이것은 필터 내에 포함된 URI의 +몇몇 부분에만 비교되는 것입니다. 예:</p> +<ul> + <li>필터가 구성표만 지정하는 경우, 해당 구성표가 있는 모든 URI가 필터와 +일치합니다.</li> + <li>필터가 구성표와 권한은 지정하지만 경로는 지정하지 않는 경우, 같은 구성표와 권한이 있는 +모든 URI가 경로와 관계 없이 필터를 통과합니다.</li> + <li>필터가 구성표, 권한과 경로를 지정하는 경우 같은 구성표, +권한과 경로가 있는 URI만 해당 필터를 통과합니다.</li> +</ul> + +<p class="note"><strong>참고:</strong> 경로 사양에는 경로 이름이 +부분적으로만 일치하도록 요구하기 위해 와일드카드 별표(*)가 들어 있을 수 있습니다.</p> + +<p>데이터 테스트는 인텐트 안의 URI와 MIME 유형 둘 모두를 필터 안에서 +지정된 MIME 유형과 비교합니다. 규칙은 다음과 같습니다. +</p> + +<ol type="a"> +<li>URI도 MIME 유형도 들어 있지 않은 인텐트가 테스트를 통과하는 것은 +필터가 URI나 MIME 유형을 전혀 지정하지 않은 경우뿐입니다.</li> + +<li>URI는 들어 있지만 MIME 유형은 없는 인텐트(URI로부터는 +명시적이지도 않고 추론할 수도 없음)가 테스트를 통과하는 것은 그 URI가 필터의 URI 형식과 일치하며 +필터가 인텐트와 마찬가지로 MIME 유형을 지정하지 않는 경우뿐입니다.</li> + +<li>MIME 유형은 들어 있지만 URI는 없는 인텐트가 테스트를 통과하는 것은 +해당 필터가 같은 MIME 유형을 목록으로 나열하지만 URI 형식은 지정하지 않은 경우뿐입니다.</li> + +<li>URI와 MIME 유형이 둘 다 들어 있는 인텐트(URI로부터는 +명시적이지도 않고 추론할 수도 없음)가 테스트의 MIME 유형 부분을 통과하는 것은 해당 유형이 +필터 내에 목록으로 나열된 한 유형에 일치하는 경우뿐입니다. 이것이 테스트의 URI 부분을 통과하는 것은 +URI가 필터 내의 URI와 일치하거나, {@code content:} + 또는 {@code file:} URI가 있고 필터가 URI를 지정하지 않은 경우뿐입니다. 달리 말하면, +구성 요소는 필터가 MIME 유형 <em>하나만</em> 목록으로 나열하는 경우 {@code content:} 및 {@code file:} 데이터를 +지원하는 것으로 간주한다는 뜻입니다.</p></li> +</ol> + +<p> +마지막 규칙인 규칙 (d)는 +구성 요소가 파일이나 콘텐츠 제공자로부터 로컬 파일을 가져올 수 있는지에 대한 기대 수준을 반영합니다. +따라서 이러한 필터는 데이터 유형만 목록으로 나열해도 되고, +{@code content:} 및 {@code file:} 구성표의 이름을 명시적으로 제시하지 않아도 됩니다. +이것이 일반적인 경우입니다. 예를 들어, 다음 예시와 같은 <a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a> 요소는 +Android에 구성 요소가 콘텐츠 제공자에서 이미지 데이터를 가져와 표시할 수 있다고 +알립니다. +</p> + +<pre> +<intent-filter> + <data android:mimeType="image/*" /> + ... +</intent-filter></pre> + +<p> +대부분의 사용 가능한 데이터는 콘텐츠 제공자가 제공하는 것이므로, 데이터 유형은 +지정하지만 URI는 지정하지 않는 필터가 아마 가장 보편적인 유형일 것입니다. +</p> + +<p> +또 다른 보편적인 구성을 예로 들자면 구성표와 데이터 유형을 가진 필터가 있겠습니다. 예를 들어 +다음 예시와 같은 <a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a> + 요소는 Android에 구성 요소가 +작업을 수행하기 위해 네트워크에서 비디오 데이터를 검색할 수 있다고 알립니다. +</p> + +<pre> +<intent-filter> + <data android:scheme="http" android:type="video/*" /> + ... +</intent-filter></pre> + + + +<h3 id="imatch">인텐트 일치</h3> + +<p>인텐트를 인텐트 필터에 비교해 일치시키면 활성화할 대상 구성 요소를 +찾아낼 수 있을 뿐만 아니라, 기기에 있는 일련의 구성 요소에 대해 +무언가 알아낼 수도 있습니다. 예를 들어 홈 앱이 앱 시작 관리자를 채우려면 + +{@link android.content.Intent#ACTION_MAIN} 작업과 +{@link android.content.Intent#CATEGORY_LAUNCHER} 카테고리를 지정하는 인텐트 필터를 가진 액티비티를 모두 찾아야 합니다.</p> + +<p>여러분의 애플리케이션도 인텐트 일치를 이와 비슷한 방식으로 사용할 수 있습니다. +{@link android.content.pm.PackageManager}에는 {@code query...()} + 메서드 집합이 있어 특정 인텐트를 허용하는 구성 요소를 모두 반환할 수 있고, +이와 유사한 일련의 {@code resolve...()} 메서드도 있어 한 인텐트에 응답하는 데 +가장 좋은 구성 요소를 판별할 수 있습니다. 예를 들어, +{@link android.content.pm.PackageManager#queryIntentActivities +queryIntentActivities()}는 인수로 통과한 +인텐트를 수행할 수 있는 모든 액티비티의 목록을 반환하며 {@link +android.content.pm.PackageManager#queryIntentServices +queryIntentServices()}는 이와 비슷한 서비스 목록을 반환합니다. +양쪽 메서드 모두 구성 요소를 활성화하지는 않습니다. 다만 응답할 수 있는 것들을 목록으로 +나열할 뿐입니다. 이와 비슷한 메서드인 +{@link android.content.pm.PackageManager#queryBroadcastReceivers +queryBroadcastReceivers()}는 브로드캐스트 수신기에 쓰입니다. +</p> + + + + diff --git a/docs/html-intl/intl/ko/guide/components/loaders.jd b/docs/html-intl/intl/ko/guide/components/loaders.jd new file mode 100644 index 000000000000..cfbbb9133ed4 --- /dev/null +++ b/docs/html-intl/intl/ko/guide/components/loaders.jd @@ -0,0 +1,494 @@ +page.title=로더 +parent.title=액티비티 +parent.link=activities.html +@jd:body +<div id="qv-wrapper"> +<div id="qv"> + <h2>이 문서의 내용</h2> + <ol> + <li><a href="#summary">로더 API 요약</a></li> + <li><a href="#app">애플리케이션 안에서 로더 사용하기</a> + <ol> + <li><a href="#requirements"></a></li> + <li><a href="#starting">로더 시작</a></li> + <li><a href="#restarting">로더 다시 시작</a></li> + <li><a href="#callback">LoaderManager 콜백 사용하기</a></li> + </ol> + </li> + <li><a href="#example">예</a> + <ol> + <li><a href="#more_examples">추가 예</a></li> + </ol> + </li> + </ol> + + <h2>Key 클래스</h2> + <ol> + <li>{@link android.app.LoaderManager}</li> + <li>{@link android.content.Loader}</li> + + </ol> + + <h2>관련 샘플</h2> + <ol> + <li> <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.html"> +LoaderCursor</a></li> + <li> <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> +LoaderThrottle</a></li> + </ol> + </div> +</div> + +<p>로더는 Android 3.0부터 도입된 것으로, 액티비티 또는 프래그먼트에서 비동기식으로 데이터를 쉽게 +로딩할 수 있게 합니다. 로더의 특성은 다음과 같습니다.</p> + <ul> + <li>모든 {@link android.app.Activity}와 {@link +android.app.Fragment}에 사용할 수 있습니다.</li> + <li>데이터의 비동기식 로딩을 제공합니다.</li> + <li>데이터의 출처를 모니터링하여 그 콘텐츠가 변경되면 새 결과를 +전달합니다.</li> + <li>구성 변경 후에 재생성된 경우, 마지막 로더의 커서로 자동으로 +다시 연결됩니다. 따라서 데이터를 다시 쿼리하지 않아도 +됩니다.</li> + </ul> + +<h2 id="summary">로더 API 요약</h2> + +<p>애플리케이션 안에서 로더를 사용하는 데 관련된 클래스와 인터페이스는 +여러 가지가 있습니다. 다음 표에서 요약되어 있습니다.</p> + +<table> + <tr> + <th>클래스/인터페이스</th> + <th>설명</th> + </tr> + <tr> + <td>{@link android.app.LoaderManager}</td> + <td>{@link android.app.Activity} 또는 +{@link android.app.Fragment}와 연관된 추상 클래스로, 하나 이상의 {@link +android.content.Loader} 인스턴스를 관리하는 데 쓰입니다. 이것을 사용하면 애플리케이션이 +{@link android.app.Activity} + 또는 {@link android.app.Fragment} 수명 주기와 함께 실행 시간이 긴 작업을 관리하는 데 도움이 됩니다. 이것의 가장 보편적인 용법은 +{@link android.content.CursorLoader}와 함께 사용하는 것이지만, +다른 유형의 데이터를 로드하기 위해 애플리케이션이 자체 로더를 작성하는 것도 얼마든지 가능합니다. + <br /> + <br /> + 액티비티 또는 프래그먼트당 {@link android.app.LoaderManager}는 하나씩밖에 없습니다. 하지만 {@link android.app.LoaderManager}에는 로더가 여러 개 있어도 +됩니다.</td> + </tr> + <tr> + <td>{@link android.app.LoaderManager.LoaderCallbacks}</td> + <td>클라이언트에 대해 {@link +android.app.LoaderManager}와 상호 작용하도록 하는 콜백 인터페이스입니다. 예를 들어 {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} +콜백 메서드를 사용하여 새 로더를 생성할 수 있습니다.</td> + </tr> + <tr> + <td>{@link android.content.Loader}</td> + <td>데이터의 비동기식 로딩을 수행하는 추상 클래스입니다. 이것이 로더의 기본 +클래스입니다. 보통은 {@link +android.content.CursorLoader}를 사용하기 마련이지만, 자신만의 하위 클래스를 구현해도 됩니다. 로더가 활성 상태인 동안에는 +소속 데이터의 출처를 모니터링하고 콘텐츠가 변경되면 새 결과를 +전달하는 것이 정상입니다. </td> + </tr> + <tr> + <td>{@link android.content.AsyncTaskLoader}</td> + <td>작업을 수행할 {@link android.os.AsyncTask}를 제공하는 추상 로더입니다.</td> + </tr> + <tr> + <td>{@link android.content.CursorLoader}</td> + <td>{@link android.content.AsyncTaskLoader}의 하위 클래스로, 이것이 +{@link android.content.ContentResolver}를 쿼리하고 {@link +android.database.Cursor}를 반환합니다. 이 클래스는 커서 쿼리에 대한 표준 방식으로 {@link +android.content.Loader} 프로토콜을 구현하며, +{@link android.content.AsyncTaskLoader}에 구축되어 +배경 스레드에서 커서 쿼리를 수행하므로 애플리케이션의 UI를 차단하지 않습니다. +이 로더를 사용하는 것이 프래그먼트나 액티비티의 API를 통해 관리된 쿼리를 수행하는 대신 {@link +android.content.ContentProvider}에서 +비동기식으로 데이터를 로딩하는 최선의 방법입니다.</td> + </tr> +</table> + +<p>위의 표에 나열된 클래스와 인터페이스가 애플리케이션 내에서 로더를 구현하는 데 +사용할 기본적인 구성 요소입니다. 생성하는 로더마다 +이 모든 것이 다 필요한 것은 아니지만, 로더를 초기화하려면 항상 {@link +android.app.LoaderManager}를 참조해야 하고 {@link +android.content.CursorLoader}와 같은 {@link android.content.Loader} +클래스도 구현해야 합니다. 다음 몇 섹션에서는 애플리케이션 안에서 이와 같은 +클래스와 인터페이스를 사용하는 법을 보여줍니다.</p> + +<h2 id ="app">애플리케이션 안에서 로더 사용하기</h2> +<p>이 섹션에서는 Android 애플리케이션 내에서 로더를 사용하는 방법을 설명합니다. 로더를 +사용하는 애플리케이션에는 보통 다음이 포함되어 있습니다.</p> +<ul> + <li>{@link android.app.Activity} 또는 {@link android.app.Fragment}.</li> + <li>{@link android.app.LoaderManager}의 인스턴스.</li> + <li>{@link +android.content.ContentProvider}가 지원하는 데이터를 로딩할 {@link android.content.CursorLoader}. 아니면, 개발자 나름의 +{@link android.content.Loader} 또는 {@link android.content.AsyncTaskLoader} 하위 클래스를 구현하여 +다른 출처에서 데이터를 로딩해도 됩니다.</li> + <li>{@link android.app.LoaderManager.LoaderCallbacks}의 구현. +여기에서 새 로더를 생성하고 기존 로더에 대한 참조를 +관리합니다.</li> +<li>로더의 데이터를 표시하는 방법(예: {@link +android.widget.SimpleCursorAdapter})</li> + <li>{@link android.content.ContentProvider}와 같은 데이터 출처로, +{@link android.content.CursorLoader}를 사용하는 경우에 해당.</li> +</ul> +<h3 id="starting">로더 시작</h3> + +<p>{@link android.app.LoaderManager}는 {@link android.app.Activity} 또는 +{@link android.app.Fragment} 내에서 하나 이상의 {@link +android.content.Loader} 인스턴스를 관리합니다. 액티비티 또는 프래그먼트당 {@link +android.app.LoaderManager}는 하나씩밖에 없습니다.</p> + +<p>보통은 +액티비티의 {@link +android.app.Activity#onCreate onCreate()} 메서드 내에서, 또는 프래그먼트의 +{@link android.app.Fragment#onActivityCreated onActivityCreated()} 메서드 내에서 {@link android.content.Loader}를 초기화합니다. 이렇게 하려면 +다음과 같은 방법을 따릅니다.</p> + +<pre>// Prepare the loader. Either re-connect with an existing one, +// or start a new one. +getLoaderManager().initLoader(0, null, this);</pre> + +<p>{@link android.app.LoaderManager#initLoader initLoader()} 메서드는 +다음과 같은 인수를 취합니다.</p> +<ul> + <li>로더를 식별하는 고유한 ID. 이 예시에서 ID는 0입니다.</li> +<li>생성 시 로더에 제공할 선택적 인수 +(이 예시에서는 <code>null</code>).</li> + +<li>{@link android.app.LoaderManager.LoaderCallbacks} 구현. 로더 이벤트를 보고하기 위해 +{@link android.app.LoaderManager}가 이것을 호출합니다. 이 예시에서는 + 로컬 클래스가 {@link +android.app.LoaderManager.LoaderCallbacks} 인터페이스를 구현하여 자신에 대한 참조인 +{@code this}를 통과합니다.</li> +</ul> +<p>{@link android.app.LoaderManager#initLoader initLoader()} 호출로 로더가 초기화되었고 활성 상태이도록 +확실히 합니다. 이로써 발생할 수 있는 결과가 두 가지 있습니다.</p> +<ul> + <li>ID가 지정한 로더가 이미 존재하는 경우, 마지막으로 생성된 로더를 +재사용합니다.</li> + <li>ID가 지정한 로더가 존재하지 <em>않는</em> 경우, +{@link android.app.LoaderManager#initLoader initLoader()}가 +{@link android.app.LoaderManager.LoaderCallbacks} 메서드 {@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}를 발생시킵니다. +여기에서 인스턴트화할 코드를 구현하고 새 로더를 반환합니다. +자세한 논의는 <a href="#onCreateLoader">onCreateLoader</a> 섹션을 참조하십시오.</li> +</ul> +<p>어떤 경우에든 주어진 {@link android.app.LoaderManager.LoaderCallbacks} +구현은 해당 로더와 연관되어 있으며, 로더 상태가 변경되면 +이것이 호출됩니다. 이 호출의 그러한 시점에서 발신자가 +시작된 상태에 있으며 요청한 로더가 이미 존재하고 자신의 데이터를 +생성해 놓은 경우, 시스템은 즉시 {@link +android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}를 +호출합니다({@link android.app.LoaderManager#initLoader initLoader()} 도중). +따라서 이런 일이 발생할 것에 대비해두어야만 합니다. 이 콜백에 대한 자세한 논의는 <a href="#onLoadFinished"> +onLoadFinished</a>를 참조하십시오.</p> + +<p>{@link android.app.LoaderManager#initLoader initLoader()} +메서드는 생성된 {@link android.content.Loader}를 반환하지만, 이에 대한 참조를 캡처하지 않아도 된다는 점을 +유의하십시오. {@link android.app.LoaderManager}는 로더의 수명을 +자동으로 관리합니다. {@link android.app.LoaderManager}는 +필요에 따라 로딩을 시작하고 중단하며, 로더와 그에 연관된 콘텐츠의 상태를 +유지관리합니다. 이것이 시사하는 바와 같이, 로더와 직접적으로 +상호 작용하는 경우는 극히 드뭅니다(다만, 로더의 행동을 미세하게 조정하기 위해 로더 메서드를 사용하는 사례를 알아보려면 +<a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> LoaderThrottle</a> 샘플을 참조하면 좋습니다). +가장 보편적으로 사용되는 메서드는 {@link +android.app.LoaderManager.LoaderCallbacks}로, 이를 사용해 특정한 이벤트가 일어났을 때 +로딩 프로세스에 개입하게 됩니다. 이 주제에 대한 자세한 논의는 <a href="#callback">LoaderManager 콜백 사용하기</a>를 참조하십시오.</p> + +<h3 id="restarting">로더 다시 시작</h3> + +<p>위에서 나타난 것과 같이 {@link android.app.LoaderManager#initLoader initLoader()}를 사용하는 경우, +이것은 지정된 ID에 해당되는 기존 로더가 있으면 그것을 사용합니다. +없으면, 하나 생성합니다. 하지만 때로는 오래된 데이터를 폐기하고 새로 시작하고 싶을 때가 +있습니다.</p> + +<p>오래된 데이터를 폐기하려면 {@link +android.app.LoaderManager#restartLoader restartLoader()}를 사용합니다. 예를 들어, 다음의 +{@link android.widget.SearchView.OnQueryTextListener} 구현은 +사용자의 쿼리가 변경되면 로더를 다시 시작합니다. 로더를 다시 시작해야 수정된 검색 필터를 사용하여 +새 쿼리를 수행할 수 있습니다.</p> + +<pre> +public boolean onQueryTextChanged(String newText) { + // Called when the action bar search text has changed. Update + // the search filter, and restart the loader to do a new query + // with this filter. + mCurFilter = !TextUtils.isEmpty(newText) ? newText : null; + getLoaderManager().restartLoader(0, null, this); + return true; +}</pre> + +<h3 id="callback">LoaderManager 콜백 사용하기</h3> + +<p>{@link android.app.LoaderManager.LoaderCallbacks}는 클라이언트가 +{@link android.app.LoaderManager}와 상호 작용할 수 있게 해주는 콜백 인터페이스입니다. </p> +<p>로더, 특히 {@link android.content.CursorLoader}는 중단된 후에도 +자신의 데이터를 유지할 것으로 기대됩니다. 이 때문에 애플리케이션이 +액티비티 또는 프래그먼트의 @link android.app.Activity#onStop +onStop()} 및 {@link android.app.Activity#onStart onStart()}를 가로질러 데이터를 유지할 수 있고, +따라서 사용자가 애플리케이션에 되돌아오면 데이터가 다시 로딩되기를 기다리지 +않아도 됩니다. 새 로더를 언제 생성해야 할지 알아보려면 {@link android.app.LoaderManager.LoaderCallbacks} +메서드를 사용합니다. 또한 로더의 데이터 사용을 중단할 때가 되면 +이를 애플리케이션에 알리는 데에도 이것을 사용할 수 있습니다.</p> + +<p>{@link android.app.LoaderManager.LoaderCallbacks}에는 다음과 같은 메서드가 +포함됩니다.</p> +<ul> + <li>{@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} - +주어진 ID에 대하여 인스턴트화하고 새 {@link android.content.Loader}를 반환합니다. +</li></ul> +<ul> + <li> {@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} +- 이전에 생성된 로더가 로딩을 완료하면 호출됩니다. +</li></ul> +<ul> + <li>{@link android.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()} +- 이전에 생성된 로더가 휴식 중이라서 해당되는 데이터를 사용할 수 없을 경우 +호출됩니다. +</li> +</ul> +<p>이러한 메서드는 다음 섹션에 보다 자세하게 설명되어 있습니다.</p> + +<h4 id ="onCreateLoader">onCreateLoader</h4> + +<p>로더에 액세스하려 시도하는 경우(예를 들어 {@link +android.app.LoaderManager#initLoader initLoader()}를 통해), 로더는 해당 ID가 지정하는 로더가 존재하는지 +여부를 확인합니다. 그런 로더가 없으면, {@link +android.app.LoaderManager.LoaderCallbacks} 메서드 {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}를 발생시킵니다. 여기에서 +새 로더를 생성합니다. 이것은 보통 {@link +android.content.CursorLoader}이지만, 개발자 나름대로 {@link +android.content.Loader} 하위 클래스를 구현해도 됩니다. </p> + +<p>이 예시에서는, {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} + 콜백 메서드가 {@link android.content.CursorLoader}를 생성합니다. +{@link android.content.CursorLoader}를 자체 생성자 메서드를 사용하여 구축해야 합니다. 이것은 +{@link +android.content.ContentProvider}로 쿼리를 수행하기 위해 필요한 모든 정보 집합을 필요로 합니다. 구체적으로 필요한 것은 다음과 같습니다.</p> +<ul> + <li><em>uri</em> — 검색할 콘텐츠의 URI입니다. </li> + <li><em>예측</em> — 반환할 열 목록입니다. +<code>null</code>을 전달하면 모든 열을 반환하며, 이는 비효율적입니다. </li> + <li><em>선택</em> — 반환할 행을 선언하는 필터로, +SQL WHERE 절로 형식이 설정됩니다(WHERE 자체는 제외). +<code>null</code>을 반환하면 주어진 URI에 대한 모든 행을 반환합니다. </li> + <li><em>selectionArgs</em> — 선택에 ?를 포함해도 됩니다. 이렇게 하면 +이것이 <em>selectionArgs</em>에서 가져온 값으로 교체되며, 이때 선택에 표시되는 +순서를 따릅니다. 값은 문자열로 바인딩됩니다. </li> + <li><em>sortOrder</em> — SQL ORDER BY 절 형식으로 설정된 +행의 순서 지정 방법입니다(ORDER BY 자체는 제외). <code>null</code>을 +전달하면 기본 정렬 순서를 사용하는데, 이는 순서가 없습니다.</li> +</ul> +<p>예:</p> +<pre> + // If non-null, this is the current filter the user has provided. +String mCurFilter; +... +public Loader<Cursor> onCreateLoader(int id, Bundle args) { + // This is called when a new Loader needs to be created. This + // sample only has one Loader, so we don't care about the ID. + // First, pick the base URI to use depending on whether we are + // currently filtering. + Uri baseUri; + if (mCurFilter != null) { + baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, + Uri.encode(mCurFilter)); + } else { + baseUri = Contacts.CONTENT_URI; + } + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + + Contacts.DISPLAY_NAME + " != '' ))"; + return new CursorLoader(getActivity(), baseUri, + CONTACTS_SUMMARY_PROJECTION, select, null, + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); +}</pre> +<h4 id="onLoadFinished">onLoadFinished</h4> + +<p>이 메서드는 이전에 생성된 로더가 로딩을 완료하면 호출됩니다. +이 로더에 대해 제공된 마지막 데이터가 릴리스되기 전에 틀림없이 +이 메서드가 호출됩니다. 이 시점에서 오래된 데이터의 +사용 내용을 모두 제거해야 하지만(곧 릴리스될 것이므로), 데이터 릴리스를 직접 수행해서는 안 됩니다. 해당 데이터는 +로더의 소유이며, 로더가 알아서 처리할 것이기 때문입니다.</p> + + +<p>로더는 애플리케이션이 데이터를 더 이상 사용하지 않는다는 사실을 알게 되면 곧바로 해당 데이터를 +릴리스할 것입니다. 예를 들어 데이터가 {@link +android.content.CursorLoader}의 커서인 경우, 거기에서 직접 {@link +android.database.Cursor#close close()}를 호출하면 안 됩니다. 커서가 +{@link android.widget.CursorAdapter}에 배치되는 경우, {@link +android.widget.SimpleCursorAdapter#swapCursor swapCursor()} 메서드를 사용해야 합니다. 그래야 +오래된 {@link android.database.Cursor}가 종료되지 않습니다. 예:</p> + +<pre> +// This is the Adapter being used to display the list's data.<br +/>SimpleCursorAdapter mAdapter; +... + +public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mAdapter.swapCursor(data); +}</pre> + +<h4 id="onLoaderReset">onLoaderReset</h4> + +<p>이 메서드는 이전에 생성된 로더가 휴식 중이라서 해당되는 데이터를 사용할 수 없는 경우 +호출됩니다. 이 콜백을 사용하면 데이터가 언제 릴리스될지 알아낼 수 있어 +이에 대한 참조를 직접 제거할 수 있습니다. </p> +<p>이 구현은 +{@link android.widget.SimpleCursorAdapter#swapCursor swapCursor()}를 호출하며, +이때 값은 <code>null</code>을 씁니다.</p> + +<pre> +// This is the Adapter being used to display the list's data. +SimpleCursorAdapter mAdapter; +... + +public void onLoaderReset(Loader<Cursor> loader) { + // This is called when the last Cursor provided to onLoadFinished() + // above is about to be closed. We need to make sure we are no + // longer using it. + mAdapter.swapCursor(null); +}</pre> + + +<h2 id="example">예</h2> + +<p>일례를 제시하기 위해 아래에 {@link android.widget.ListView}를 표시하는 {@link +android.app.Fragment}의 전체 구현을 나타내었습니다. 여기에는 +연락처 콘텐츠 제공자에 대한 쿼리 결과가 들어 있습니다. 이것은 {@link +android.content.CursorLoader}를 사용하여 제공자에 대한 쿼리를 관리합니다.</p> + +<p>이 예시에서 나타낸 바와 같이 애플리케이션이 사용자의 연락처에 액세스하려면 +애플리케이션의 매니페스트에 +{@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS} 권한이 포함되어 있어야 합니다.</p> + +<pre> +public static class CursorLoaderListFragment extends ListFragment + implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> { + + // This is the Adapter being used to display the list's data. + SimpleCursorAdapter mAdapter; + + // If non-null, this is the current filter the user has provided. + String mCurFilter; + + @Override public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + // Give some text to display if there is no data. In a real + // application this would come from a resource. + setEmptyText("No phone numbers"); + + // We have a menu item to show in action bar. + setHasOptionsMenu(true); + + // Create an empty adapter we will use to display the loaded data. + mAdapter = new SimpleCursorAdapter(getActivity(), + android.R.layout.simple_list_item_2, null, + new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS }, + new int[] { android.R.id.text1, android.R.id.text2 }, 0); + setListAdapter(mAdapter); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + getLoaderManager().initLoader(0, null, this); + } + + @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + // Place an action bar item for searching. + MenuItem item = menu.add("Search"); + item.setIcon(android.R.drawable.ic_menu_search); + item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + SearchView sv = new SearchView(getActivity()); + sv.setOnQueryTextListener(this); + item.setActionView(sv); + } + + public boolean onQueryTextChange(String newText) { + // Called when the action bar search text has changed. Update + // the search filter, and restart the loader to do a new query + // with this filter. + mCurFilter = !TextUtils.isEmpty(newText) ? newText : null; + getLoaderManager().restartLoader(0, null, this); + return true; + } + + @Override public boolean onQueryTextSubmit(String query) { + // Don't care about this. + return true; + } + + @Override public void onListItemClick(ListView l, View v, int position, long id) { + // Insert desired behavior here. + Log.i("FragmentComplexList", "Item clicked: " + id); + } + + // These are the Contacts rows that we will retrieve. + static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { + Contacts._ID, + Contacts.DISPLAY_NAME, + Contacts.CONTACT_STATUS, + Contacts.CONTACT_PRESENCE, + Contacts.PHOTO_ID, + Contacts.LOOKUP_KEY, + }; + public Loader<Cursor> onCreateLoader(int id, Bundle args) { + // This is called when a new Loader needs to be created. This + // sample only has one Loader, so we don't care about the ID. + // First, pick the base URI to use depending on whether we are + // currently filtering. + Uri baseUri; + if (mCurFilter != null) { + baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, + Uri.encode(mCurFilter)); + } else { + baseUri = Contacts.CONTENT_URI; + } + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + + Contacts.DISPLAY_NAME + " != '' ))"; + return new CursorLoader(getActivity(), baseUri, + CONTACTS_SUMMARY_PROJECTION, select, null, + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); + } + + public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mAdapter.swapCursor(data); + } + + public void onLoaderReset(Loader<Cursor> loader) { + // This is called when the last Cursor provided to onLoadFinished() + // above is about to be closed. We need to make sure we are no + // longer using it. + mAdapter.swapCursor(null); + } +}</pre> +<h3 id="more_examples">추가 예</h3> + +<p>로더를 사용하는 법을 보여주는 몇 가지 다른 예가 <strong>ApiDemos</strong>에 +소개되어 있습니다.</p> +<ul> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.html"> +LoaderCursor</a> — 위에 표시된 코드 조각의 완전한 +버전입니다.</li> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> LoaderThrottle</a> — 데이터가 변경될 때 콘텐츠 제공자가 +수행하는 쿼리의 수를 줄이기 위해 제한을 사용하는 방법을 예시로 나타낸 것입니다.</li> +</ul> + +<p>SDK 샘플을 다운로드하고 설치하는 데 대한 정보는 <a href="http://developer.android.com/resources/samples/get.html">샘플 +가져오기</a>를 참조하십시오. </p> + diff --git a/docs/html-intl/intl/ko/guide/components/processes-and-threads.jd b/docs/html-intl/intl/ko/guide/components/processes-and-threads.jd new file mode 100644 index 000000000000..850d55cf9c71 --- /dev/null +++ b/docs/html-intl/intl/ko/guide/components/processes-and-threads.jd @@ -0,0 +1,411 @@ +page.title=프로세스 및 스레드 +page.tags=수명 주기, 배경 + +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + +<h2>이 문서의 내용</h2> +<ol> +<li><a href="#Processes">프로세스</a> + <ol> + <li><a href="#Lifecycle">프로세스 수명 주기</a></li> + </ol> +</li> +<li><a href="#Threads">스레드</a> + <ol> + <li><a href="#WorkerThreads">작업자 스레드</a></li> + <li><a href="#ThreadSafe">스레드로부터 안전한 메서드</a></li> + </ol> +</li> +<li><a href="#IPC">프로세스 간 통신</a></li> +</ol> + +</div> +</div> + +<p>애플리케이션 구성 요소가 시작되고 애플리케이션에 실행 중인 다른 구성 요소가 없으면 +Android 시스템은 하나의 실행 스레드로 애플리케이션의 Linux 프로세스를 +시작합니다. 기본적으로 같은 애플리케이션의 모든 구성 요소는 같은 프로세스와 스레드에서 실행됩니다 +("기본" 스레드라고 합니다). 애플리케이션 구성 요소가 시작되고 (애플리케이션의 다른 구성 요소가 존재해서) +해당 애플리케이션의 프로세스가 이미 존재하면 해당 구성 요소는 +프로세스 내에서 시작되고 같은 실행 스레드를 사용합니다. 하지만 애플리케이션 내의 +여러 가지 구성 요소가 각자 별도의 프로세스에서 실행되도록 할 수도 있고, 어느 프로세스에든 추가 스레드를 +만들 수 있습니다.</p> + +<p>이 문서는 프로세스와 스레드가 Android 애플리케이션에서 작동하는 방식을 설명합니다.</p> + + +<h2 id="Processes">프로세스</h2> + +<p>기본적으로 같은 애플리케이션의 모든 구성 요소는 같은 프로세스와 스레드에서 실행되고, +대부분의 애플리케이션은 이를 바꿔서는 안 됩니다. 그러나 어느 프로세스가 특정 구성 요소에 속하는지 +확인해야 할 경우 매니페스트 파일에서 확인할 수 있습니다.</p> + +<p>구성 요소 —<a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code +<activity>}</a>와 <a href="{@docRoot}guide/topics/manifest/service-element.html">{@code +<service>}</a>, <a href="{@docRoot}guide/topics/manifest/receiver-element.html">{@code +<receiver>}</a> 및 <a href="{@docRoot}guide/topics/manifest/provider-element.html">{@code +<provider>}</a>—의 각 유형에 대한 매니페스트 항목은 구성 요소를 실행할 프로세스를 지정하는 {@code android:process} 속성을 지원합니다. + 이러한 속성을 설정하여 각 구성 요소를 자체 프로세스에서 실행시키거나 +다른 구성 요소를 제외한 일부 구성 요소만 프로세스를 공유하게 할 수 있습니다 또한, +{@code android:process}를 설정하여 다른 애플리케이션의 구성 요소를 같은 프로세스에서 실행시킬 수 있습니다. +단, 이는 애플리케이션이 같은 Linux 사용자 ID를 공유하고 같은 인증서에 +서명되었을 경우에 한합니다.</p> + +<p><a href="{@docRoot}guide/topics/manifest/application-element.html">{@code +<application>}</a> 요소도 {@code android:process} 속성을 지원하여, +모든 구성 요소에 적용되는 기본값을 설정합니다.</p> + +<p>Android는 어느 시점엔가 프로세스를 종료하기로 결정할 수도 있습니다. 즉 메모리가 부족하거나, 사용자에게 더욱 즉각적인 서비스를 제공하는 +다른 프로세스가 이 프로세스의 중단을 필요로 하는 경우 등입니다. 그러면 중단된 +프로세스에서 실행되고 있던 애플리케이션 구성 요소도 따라서 소멸됩니다. 그와 같은 구성 요소가 할 작업이 다시 생기면 +그에 대한 프로세스도 다시 시작됩니다.</p> + +<p>어느 프로세스를 삭제할지 결정할 때, Android 시스템은 +사용자에 대한 이들의 상대적 중요성을 가늠합니다. 예를 들어, 눈에 보이는 액티비티를 호스팅하는 프로세스와 비교하여 +화면에 보이지 않는 액티비티를 호스팅하는 프로세스를 쉽게 종료할 수 있습니다. 프로세스 종료 결정은 +해당 프로세스에서 실행되는 구성 요소의 상태에 따라 달라집니다. 종료할 +프로세스를 결정하는 데 사용하는 규칙은 아래에 설명되어 있습니다. </p> + + +<h3 id="Lifecycle">프로세스 수명 주기</h3> + +<p>Android 시스템은 최대한 오래 애플리케이션 프로세스를 유지하려고 시도하지만, +결국 오래된 프로세스를 제거하고 새 프로세스나 더 중요한 프로세스에 사용할 메모리를 확보해야 합니다. 유지할 +프로세스와 종료할 프로세스를 결정하기 위해 +시스템은 프로세스에서 실행되는 구성 요소와 해당 구성 요소의 상태에 기초하여 각 프로세스에 +"중요 계층"을 부여합니다. 중요도가 낮은 +프로세스가 먼저 제거되고, 그 다음으로 중요도가 낮은 프로세스를 제거하는 식으로 +필요에 따라 시스템 리소스를 회복하는 것입니다.</p> + +<p>중요 계층에는 다섯 가지 단계가 있습니다. 다음 목록은 +중요도 순서에 따른 프로세스 유형을 나타낸 것입니다(첫 번째 프로세스가 <em>가장 중요하고</em> +<em>마지막으로 종료됩니다)</em>.</p> + +<ol> + <li><b>전경 프로세스</b> + <p>사용자가 현재 진행하는 작업에 필요한 프로세스입니다. 다음 조건 중 +하나가 참일 경우 프로세스가 전경에 있는 것으로 간주합니다.</p> + + <ul> + <li>사용자와 상호 작용하는 {@link android.app.Activity}를 호스팅할 경우({@link +android.app.Activity}의 {@link android.app.Activity#onResume onResume()} 메서드가 호출되었을 경우 +).</li> + + <li>사용자와 상호 작용하는 액티비티에 바인딩된 {@link android.app.Service}를 호스팅할 경우. +</li> + + <li>"전경에서" 실행되는 {@link android.app.Service}를 호스팅할 경우( +해당 서비스가 {@link android.app.Service#startForeground startForeground()}를 호출했을 경우). + + <li>수명 주기 콜백 중 하나를 실행하는 {@link android.app.Service}를 호스팅할 경우 +({@link android.app.Service#onCreate onCreate()}, {@link android.app.Service#onStart +onStart()} 또는 {@link android.app.Service#onDestroy onDestroy()}).</li> + + <li>{@link +android.content.BroadcastReceiver#onReceive onReceive()} 메서드를 실행하는 {@link android.content.BroadcastReceiver}를 호스팅할 경우.</li> + </ul> + + <p>일반적으로, 주어진 시간에 존재하는 전경 프로세스는 몇 개밖에 되지 않습니다. 이들은 최후의 +수단으로서만 종료됩니다. 즉, 메모리가 너무 부족해 계속 실행할 수 없는 경우를 말합니다. 일반적으로 그 시점이 되면 +기기가 메모리 페이징 상태에 도달한 것이므로 전경 프로세스 몇 개를 중단해야만 +사용자 인터페이스의 반응성을 유지할 수 있습니다.</p></li> + + <li><b>가시적 프로세스</b> + <p>전경 구성 요소는 없지만 +사용자가 화면에서 보는 것에 영향을 미칠 수 있는 프로세스입니다. 다음 조건 중 하나가 참이면 +가시적 프로세스로 간주합니다.</p> + + <ul> + <li>전경에 있지는 않지만 사용자에게 보이는 {@link android.app.Activity}를 호스팅할 경우 +({@link android.app.Activity#onPause onPause()} 메서드가 호출되었을 경우). +예를 들어 이것은 전경 액티비티가 대화를 시작하여 이전 액티비티가 그 뒤에 보일 경우 + 발생합니다.</li> + + <li>눈에 보이는(또는 전경) 액티비티에 바인딩된 {@link android.app.Service}를 호스팅할 경우. +</li> + </ul> + + <p>가시적인 프로세스는 매우 중요도가 높은 것으로 취급하고 모든 전경 프로세스를 실행하는 데 필요할 경우에만 +종료됩니다. </p> + </li> + + <li><b>서비스 프로세스</b> + <p>{@link +android.content.Context#startService startService()} 메서드로 시작되었지만 두 개의 상위 계층 분류에 +들어가지 않는 서비스를 실행하는 프로세스입니다. 서비스 프로세스는 사용자가 보는 것과 직접 연결되어 있지는 않지만, +일반적으로 사용자가 신경 쓰는 작업을 하므로(배경에서 음악 재생 또는 네트워크에서 데이터 다운로드) +시스템은 모든 전경 및 가시적 프로세스와 함께 실행할 만큼 메모리가 충분하지 않을 경우에만 +프로세스를 중단합니다. </p> + </li> + + <li><b>배경 프로세스</b> + <p>현재 사용자에게 보이지 않는 액티비티를 보유한 프로세스입니다(액티비티의 +{@link android.app.Activity#onStop onStop()} 메서드가 호출되었습니다). 이와 같은 프로세스는 +사용자 환경에 직접적 영향을 미치지 않고, 시스템은 언제든 이 프로세스를 중단시켜 +전경, +가시적 또는 서비스 프로세스를 위한 메모리를 확보할 수 있습니다. 보통 한번에 실행 중인 배경 프로세스가 많은 편이므로 이들은 +LRU(최저 사용 빈도) 목록에 보관하여 사용자가 가장 최근에 본 액티비티가 있는 +프로세스가 가장 마지막에 중단되도록 합니다. 액티비티가 수명 주기 메서드를 올바르게 구현하고 +자신의 현재 상태를 저장할 경우, 사용자가 액티비티로 다시 이동할 때 액티비티가 모든 가시적 상태를 +복원하므로 프로세스를 중단시키더라도 사용자 환경에는 눈에 띄게 영향을 미치지 +않습니다. 상태 저장과 복원에 관한 정보는 <a href="{@docRoot}guide/components/activities.html#SavingActivityState">액티비티</a> +문서를 참조하십시오.</p> + </li> + + <li><b>빈 프로세스</b> + <p>활성 애플리케이션 구성 요소를 보유하지 않은 프로세스입니다. 이런 프로세스를 +유지하는 유일한 이유는 다음에 내부 구성 요소를 실행할 때 시작 시간을 절약하기 위한 캐싱 +때문입니다. 시스템은 프로세스 캐시와 기본 커널 캐시 사이에서 전반적인 시스템 리소스의 균형을 맞추기 위해 +이 프로세스를 중단시키는 경우가 많습니다.</p> + </li> +</ol> + + + <p>Android는 프로세스에서 현재 활성 상태인 구성 요소의 중요도에 따라 +프로세스에 가장 높은 수준을 부여합니다. 예를 들어, 프로세스가 서비스와 가시적 액티비티를 호스팅할 경우, +해당 프로세스는 서비스 프로세스가 아니라 가시적 프로세스 등급이 부여됩니다.</p> + + <p>또한, 프로세스의 등급은 다른 프로세스가 이에 의존할 경우 상승할 수 있습니다. +즉, 다른 프로세스에 서비스를 제공하는 프로세스가 서비스 제공 대상 프로세스보다 +등급이 낮은 경우는 있을 수 없습니다. 예를 들어 프로세스 A의 콘텐츠 제공자가 프로세스 B의 클라이언트에 서비스를 제공하거나 +프로세스 A의 서비스가 프로세스 B의 구성 요소에 바인딩되어 있을 경우 프로세스 A는 항상 중요도가 +프로세스 B와 같거나 그보다 높습니다.</p> + + <p>서비스를 실행하는 프로세스가 배경 액티비티가 포함된 프로세스보다 높으므로, +장기 작업을 시작하는 액티비티는 작업자 스레드만 생성하기보다는 해당 작업에 대한 <a href="{@docRoot}guide/components/services.html">서비스</a>를 시작하는 것이 좋습니다. +이는 특히 작업이 해당 액티비티보다 오래 지속될 경우 더욱 중요합니다. +예를 들어, 웹사이트에 사진을 업로드하는 액티비티가 업로드를 수행하는 서비스를 시작해야 +사용자가 액티비티를 떠나더라도 배경에서 업로드를 지속할 수 있습니다. +서비스를 사용하면 액티비티에 어떤 일이 발생하든 해당 작업에 반드시 +"서비스 프로세스" 우선 순위 이상이 부여됩니다. 이것이 브로드캐스트 수신기가 시간이 오래 걸리는 작업을 +스레드에 넣기보다는 서비스를 사용해야 하는 것과 같은 이유입니다.</p> + + + + +<h2 id="Threads">스레드</h2> + +<p> 애플리케이션이 시작되면 시스템이 애플리케이션에 대한 실행의 스레드를 생성하며, 이를 +"기본"이라고 합니다. 이 스레드는 드로어블 이벤트를 포함하여 적절한 사용자 인터페이스 위젯에 +이벤트를 발송하는 역할을 맡기 때문에 중요합니다. 이것은 Android UI 도구 키트의 구성 요소({@link +android.widget}과 {@link android.view} 패키지의 구성 요소)와 개발자의 애플리케이션이 +상호 작용하는 스레드이기도 합니다. 따라서 기본 스레드는 +UI 스레드라고 불릴 때도 있습니다.</p> + +<p>시스템은 구성 요소의 각 인스턴스에 대해 별도의 스레드를 생성하지 <em>않습니다</em>. 같은 +프로세스에서 실행되는 모든 구성 요소는 UI 스레드에서 시작되고, 각 구성 요소를 호출하는 시스템이 +해당 스레드에서 발송됩니다. 따라서 +시스템 콜백에 응답하는 메서드(사용자 작업을 보고하는 {@link android.view.View#onKeyDown onKeyDown()} 또는 +수명 주기 콜백 메서드)는 항상 프로세스의 UI 스레드에서 실행됩니다.</p> + +<p>예를 들어, 사용자가 화면의 버튼을 터치하면, 앱 UI 스레드가 위젯에 터치 이벤트를 발송하고, +위젯은 눌린 상태를 설정하고 이벤트 대기열에 +무효화 요청을 게시합니다. UI 스레드가 이 요청을 대기열에서 해제하고 위젯에 스스로를 다시 +그려야 한다고 알립니다.</p> + +<p>앱이 사용자 상호작용에 응답하여 집약적인 작업을 수행할 때는 이 단일 스레드 모델은 +애플리케이션을 제대로 구현하지 않으면 낮은 성능을 보일 수 있습니다. 특히, +모든 것이 UI 스레드에서 발생하고 네트워크 액세스나 데이터 베이스 쿼리 등의 긴 작업을 수행하면 +UI가 통째로 차단됩니다. 스레드가 차단되면 드로잉 이벤트를 포함하여 +모든 이벤트가 발송되지 않습니다. 사용자가 보기에는 애플리케이션이 +중단된 것처럼 보입니다. 더 나쁜 경우, UI 스레드가 몇 초 이상 차단되어 있으면 +(현재 약 5초) 사용자에게 악명 높은 "<a href="http://developer.android.com/guide/practices/responsiveness.html">애플리케이션이 응답하지 +않습니다</a>"(ANR) 대화가 표시됩니다. 그러면 사용자가 여러분의 애플리케이션을 종료 할 수도 있고, 불만족한 경우 앱을 +제거할 수도 있습니다.</p> + +<p>또한, Andoid UI 도구 키트는 스레드로부터 안전하지 <em>않습니다</em>. 따라서 UI를 +작업자 스레드에서 조작해서는 안 됩니다. 사용자 인터페이스 조작 작업은 모두 UI +스레드에서 해야만 합니다. 결론적으로, Android의 단일 스레드 모델에는 두 가지 단순한 규칙이 있습니다.</p> + +<ol> +<li>UI 스레드를 차단하지 마십시오. +<li>Ui 스레드 외부에서 Android UI 도구 키트에 액세스하지 마십시오. +</ol> + +<h3 id="WorkerThreads">작업자 스레드</h3> + +<p>위에 설명한 단일 스레드 모델로 인해, 애플리케이션 UI의 반응성을 위해서는 +UI 스레드를 차단하지 않는 것이 매우 중요합니다. 수행해야 할 작업이 있는데 +이들이 즉각적인 조치를 요하지 않는 경우, 이런 작업은 반드시 별도의 스레드에서 수행해야 합니다("배경" 또는 +"작업자" 스레드).</p> + +<p>예를 들어, 아래는 별도의 스레드에서 이미지를 다운로드하고 이를 +{@link android.widget.ImageView}에 표시하는 클릭 수신기에 대한 몇 가지 코드입니다.</p> + +<pre> +public void onClick(View v) { + new Thread(new Runnable() { + public void run() { + Bitmap b = loadImageFromNetwork("http://example.com/image.png"); + mImageView.setImageBitmap(b); + } + }).start(); +} +</pre> + +<p>처음에는 네트워크 작업을 처리하기 위한 새 스레드를 생성하므로 +아무 문제 없이 작동하는 것처럼 보입니다. 하지만, 이것은 단일 스레드된 모델의 두 번째 규칙 즉, '<em>UI 스레드 외부에서 +Android UI 도구 키트에 액세스하지 마세요.</em>'를 위반합니다. 이 샘플은 UI 스레드 대신 작업자 스레드에서 {@link +android.widget.ImageView}를 수정합니다. 이렇게 되면 +정의되지 않고 예기치 못한 동작이 발생하는 결과를 초래할 수 있고, 이는 추적하기 어려워 시간도 오래 걸립니다.</p> + +<p>이 문제를 해결하기 위해 Android는 다른 스레드에서 UI 스레드에 액세스하는 여러 가지 방식을 +제안합니다. 다음은 몇 가지 유용한 메서드 목록입니다.</p> + +<ul> +<li>{@link android.app.Activity#runOnUiThread(java.lang.Runnable) +Activity.runOnUiThread(Runnable)}</li> +<li>{@link android.view.View#post(java.lang.Runnable) View.post(Runnable)}</li> +<li>{@link android.view.View#postDelayed(java.lang.Runnable, long) View.postDelayed(Runnable, +long)}</li> +</ul> + +<p>예를 들어 위의 코드를 수정하려면 {@link +android.view.View#post(java.lang.Runnable) View.post(Runnable)} 메서드를 사용하면 됩니다.</p> + +<pre> +public void onClick(View v) { + new Thread(new Runnable() { + public void run() { + final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png"); + mImageView.post(new Runnable() { + public void run() { + mImageView.setImageBitmap(bitmap); + } + }); + } + }).start(); +} +</pre> + +<p>이 구현은 이제 스레드로부터 안전합니다. 네트워크 작업은 별도의 스레드에서 수행된 반면 +{@link android.widget.ImageView}는 UI 스레드에서 조작되었기 때문입니다.</p> + +<p>그러나, 작업이 복잡해질수록 이런 종류의 코드가 더 복잡해질 수 있고 유지 관리하기 +까다로워질 수 있습니다. 더 복잡한 상호 작용을 작업자 스레드로 처리하려면, 작업자 스레드에서 +{@link android.os.Handler}를 사용하여 UI 스레드에서 전달 받은 메시지를 처리하는 방안을 +고려해보십시오. 하지만 최선의 해결책은 {@link android.os.AsyncTask} 클래스를 확장하는 방법일 것입니다. +이것은 UI와 상호 작용해야 하는 작업자 스레드 작업의 실행을 단순화합니다.</p> + + +<h4 id="AsyncTask">AsyncTask 사용</h4> + +<p>{@link android.os.AsyncTask}를 사용하면 사용자 인터페이스에서 비동기식 작업을 수행할 수 +있게 해줍니다. 이것은 작업자 스레드에서 차단 작업을 수행하고 그런 다음 그 결과를 UI 스레드에 +게시하며, 개발자가 직접 스레드 및/또는 처리기를 처리할 필요가 없습니다.</p> + +<p>이를 사용하려면 우선 {@link android.os.AsyncTask}를 하위 클래스로 한 다음 {@link +android.os.AsyncTask#doInBackground doInBackground()} 콜백 메서드를 구현해야 합니다. 이것은 여러 가지 +배경 스레드에서 실행됩니다. UI를 업데이트하려면 {@link +android.os.AsyncTask#onPostExecute onPostExecute()}를 구현해야 합니다. 이는 {@link +android.os.AsyncTask#doInBackground doInBackground()}로부터의 결과를 전달하며 UI 스레드에서 실행되므로, +안전하게 UI를 업데이트할 수 있습니다. 그런 다음 UI 스레드에서 {@link android.os.AsyncTask#execute execute()}를 +호출하여 해당 작업을 실행하면 됩니다.</p> + +<p>예를 들어, 이런 방식으로 {@link android.os.AsyncTask}를 사용하여 +이전의 예시를 구현할 수 있습니다.</p> + +<pre> +public void onClick(View v) { + new DownloadImageTask().execute("http://example.com/image.png"); +} + +private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> { + /** The system calls this to perform work in a worker thread and + * delivers it the parameters given to AsyncTask.execute() */ + protected Bitmap doInBackground(String... urls) { + return loadImageFromNetwork(urls[0]); + } + + /** The system calls this to perform work in the UI thread and delivers + * the result from doInBackground() */ + protected void onPostExecute(Bitmap result) { + mImageView.setImageBitmap(result); + } +} +</pre> + +<p>이제 UI는 안전하고 코드는 더욱 단순해졌습니다. 작업을 작업자 스레드에서 수행되어야 하는 +부분과 UI 스레드에서 수행되어야 하는 부분으로 구분하기 때문입니다.</p> + +<p>이 클래스를 사용하는 법을 완전히 숙지하려면 {@link android.os.AsyncTask} 참조를 +읽어보시는 것이 좋습니다. 개괄적인 작동 방식은 아래에 간략히 소개해 놓았습니다.</p> + +<ul> +<li>매개 변수의 유형, 진행률 값과 작업의 최종 값을 제네릭을 사용하여 +지정할 수 있습니다.</li> +<li>메서드 {@link android.os.AsyncTask#doInBackground doInBackground()}는 작업자 스레드에서 자동으로 +실행됩니다.</li> +<li>{@link android.os.AsyncTask#onPreExecute onPreExecute()}, {@link +android.os.AsyncTask#onPostExecute onPostExecute()} 및 {@link +android.os.AsyncTask#onProgressUpdate onProgressUpdate()}는 모두 UI 스레드에서 호출됩니다.</li> +<li>{@link android.os.AsyncTask#doInBackground doInBackground()}가 반환한 값이 +{@link android.os.AsyncTask#onPostExecute onPostExecute()}로 전송됩니다.</li> +<li>언제든 {@link android.os.AsyncTask#publishProgress publishProgress()}를 {@link +android.os.AsyncTask#doInBackground doInBackground()}에서 호출하여 UI 스레드에서 {@link +android.os.AsyncTask#onProgressUpdate onProgressUpdate()}를 실행하도록 할 수 있습니다.</li> +<li>모든 스레드에서 언제든 작업을 취소할 수 있습니다.</li> +</ul> + +<p class="caution"><strong>주의:</strong> 작업자 스레드를 사용할 때 마주칠 수 있는 또 한 가지 문제는 +<a href="{@docRoot}guide/topics/resources/runtime-changes.html">런타임 구성 변경</a>으로 인해 액티비티가 예기치 못하게 다시 시작되는 것입니다 +(예를 들어 사용자가 화면 방향을 바꾸는 경우). 이 경우 작업자 스레드를 소멸시킬 수 있습니다. +스레드가 재시작될 때 작업을 지속하는 방법과 액티비티가 제거되었을 때 작업을 적절히 취소하는 방법은 +<a href="http://code.google.com/p/shelves/">Shelves</a> 샘플 애플리케이션의 소스 코드를 참조하십시오.</p> + + +<h3 id="ThreadSafe">스레드로부터 안전한 메서드</h3> + +<p> 어떤 경우에는 구현하는 메서드가 하나 이상의 스레드에서 호출되는 일도 있습니다. 따라서 +이를 스레드로부터 안전하게 작성해야만 합니다. </p> + +<p>이것은 주로 원격으로 호출할 수 있는 메서드에 대해 참입니다. 예를 들어 <a href="{@docRoot}guide/components/bound-services.html">바인딩된 서비스</a> 내의 메서드 등이 해당됩니다. +{@link android.os.IBinder}에서 구현된 메서드가 +{@link android.os.IBinder IBinder}가 실행되는 프로세스에서 호출될 경우, 해당 메서드는 발신자의 스레드에서 실행됩니다. +그러나 호출이 다른 프로세스에서 발생하면, 해당 메서드는 시스템이 +{@link android.os.IBinder +IBinder}와 같은 프로세스에 유지하는 스레드 풀에서 선택된 스레드에서 실행됩니다(프로세스의 UI 스레드에서 실행되지 않습니다). 예를 들어, 어느 서비스의 +{@link android.app.Service#onBind onBind()} 메서드는 해당 서비스 +프로세스의 UI 스레드에서 호출되고, {@link android.app.Service#onBind +onBind()}가 반환하는 객체에서 구현된 메서드는(예: RPC 메서드를 구현하는 하위 클래스) 해당 풀 안의 여러 스레드에서 +호출되게 됩니다. 서비스에 클라이언트가 하나 이상 있을 수 있으므로, 하나 이상의 풀이 +동시에 같은 {@link android.os.IBinder IBinder} 메서드에 참여할 수 있습니다. 그러므로 {@link android.os.IBinder +IBinder} 메서드는 스레드로부터 안전하게 구현되어야 합니다.</p> + +<p> 마찬가지로 콘텐츠 제공자는 다른 프로세스에서 발생한 데이터 요청을 수신할 수 있습니다. +{@link android.content.ContentResolver}와 {@link android.content.ContentProvider} +클래스가 세부적인 프로세스 간 통신의 관리 방식을 숨길 수는 있지만, 이러한 요청에 응답하는 {@link +android.content.ContentProvider} 메서드(—{@link +android.content.ContentProvider#query query()}, {@link android.content.ContentProvider#insert +insert()}, {@link android.content.ContentProvider#delete delete()}, {@link +android.content.ContentProvider#update update()} 및 {@link android.content.ContentProvider#getType +getType()} 메서드—)가 프로세스의 UI 스레드가 아니라 +콘텐츠 제공자 프로세스의 스레드 풀에서 호출됩니다. 이러한 메서드가 동시에 몇 개의 스레드에서 호출될 수 있으므로, +스레드로부터 안전하게 구현되어야 합니다. </p> + + +<h2 id="IPC">프로세스 간 통신</h2> + +<p>Android는 원격 프로시저 호출(RPC)을 사용한 프로세스 간 통신(IPC) 메커니즘을 제공합니다. +여기서 메서드는 액티비티나 다른 애플리케이션 구성 요소에 호출되지만 +원격으로 (또 다른 프로세스에서) 실행되고, 결과는 모두 발신자에게 되돌려 +보냅니다. 메서드 호출과 메서드의 데이터는 운영 체제가 이해할 수 있는 수준으로 분해되고, +로컬 프로세스와 주소 공간에서 원격 프로세스와 주소 공간으로 전송된 다음 +다시 결합되어 여기서 호출에 다시 응답합니다. 그런 다음 반환 값이 +반대 방향으로 전송됩니다. Android가 이와 같은 IPC 트랜잭션을 수행하는 데 필요한 +모든 코드를 제공하므로, 개발자는 그저 RPC 프로그래밍 인터페이스를 정의하고 구현하는 데에만 집중하면 됩니다. </p> + +<p>IPC를 수행하려면 애플리케이션이 반드시 서비스에 바인딩되어야만 하며, 이때 {@link +android.content.Context#bindService bindService()}를 사용해야 합니다. 자세한 정보는 <a href="{@docRoot}guide/components/services.html">서비스</a> 개발자 가이드를 참조하십시오.</p> + + +<!-- +<h2>Beginner's Path</h2> + +<p>For information about how to perform work in the background for an indefinite period of time +(without a user interface), continue with the <b><a +href="{@docRoot}guide/components/services.html">Services</a></b> document.</p> +--> diff --git a/docs/html-intl/intl/ko/guide/components/recents.jd b/docs/html-intl/intl/ko/guide/components/recents.jd new file mode 100644 index 000000000000..cba3c45da3da --- /dev/null +++ b/docs/html-intl/intl/ko/guide/components/recents.jd @@ -0,0 +1,256 @@ +page.title=개요 화면 +page.tags="recents","overview" + +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + + <h2>이 문서의 내용</h2> + <ol> + <li><a href="#adding">개요 화면에 작업 추가</a> + <ol> + <li><a href="#flag-new-doc">작업 추가에 인텐트 플래그 사용</a></li> + <li><a href="#attr-doclaunch">작업 추가에 액티비티 특성 사용</a></li> + </ol> + </li> + <li><a href="#removing">작업 제거</a> + <ol> + <li><a href="#apptask-remove">작업 제거에 AppTask 클래스 사용</a></li> + <li><a href="#retain-finished">완료된 작업 보존</a></li> + </ol> + </li> + </ol> + + <h2>Key 클래스</h2> + <ol> + <li>{@link android.app.ActivityManager.AppTask}</li> + <li>{@link android.content.Intent}</li> + </ol> + + <h2>샘플 코드</h2> + <ol> + <li><a href="{@docRoot}samples/DocumentCentricApps/index.html">문서 중심 앱</a></li> + </ol> + +</div> +</div> + +<p>개요 화면(다른 말로 최근 사용 화면, 최근 작업 목록 또는 최근 앱이라고도 함)은 +시스템 수준 UI로, 최근에 액세스한 <a href="{@docRoot}guide/components/activities.html"> +액티비티</a> 및 <a href="{@docRoot}guide/components/tasks-and-back-stack.html">작업</a>을 목록으로 나열한 것입니다. 사용자는 +목록을 가로질러 이동하며 작업을 선택해서 재개할 수도 있고, 아니면 +목록에서 한 작업을 스와이프하여 밀어내어 목록에서 제거할 수도 있습니다. Android 5.0 릴리스(API 레벨 21)의 경우, 같은 액티비티의 여러 인스턴스에 +각기 다른 문서가 담겨 있는 경우 이들이 개요 화면에 작업으로 나타날 수 있습니다. 예를 들어 +Google Drive에 여러 개의 Google 문서 각각에 대한 작업이 있을 수 있습니다. 각 문서는 개요 화면에 하나의 +작업으로 나타납니다.</p> + +<img src="{@docRoot}images/components/recents.png" alt="" width="284" /> +<p class="img-caption"><strong>그림 1.</strong> 세 개의 Google Drive 문서가 각기 별도의 +작업을 나타내는 모습을 표시한 개요 화면입니다.</p> + +<p>보통은 개요 화면에 작업과 액티비티가 어떻게 표현될지는 시스템이 +정의하도록 두어야 합니다. 이 행동을 개발자가 수정할 필요도 없습니다. +하지만, 개요 화면에 액티비티가 언제, 어떻게 나타날지는 앱이 결정할 수 있습니다. +{@link android.app.ActivityManager.AppTask} 클래스를 사용하면 작업을 관리할 수 있게 해주고, +{@link android.content.Intent} 클래스의 액티비티 플래그를 사용하면 개요 화면에서 액티비티가 추가되거나 제거되는 시점을 +지정할 수 있습니다. 또한, <code><a href="{@docRoot}guide/topics/manifest/activity-element.html"> +<activity></a></code> 특성을 사용하면 매니페스트에서의 동작을 설정할 수 있습니다.</p> + +<h2 id="adding">개요 화면에 작업 추가</h2> + +<p>{@link android.content.Intent} 클래스의 플래그를 사용하여 작업을 추가하면 +개요 화면에서 문서가 열리거나 다시 열리는 시점과 방법을 보다 철저히 통제할 수 있습니다. +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> + 특성을 사용하는 경우, 문서를 항상 새 작업에서 여는 방법과 기존 작업을 해당 문서에 다시 사용하는 방법 중에서 +선택할 수 있습니다.</p> + +<h3 id="flag-new-doc">작업 추가에 인텐트 플래그 사용</h3> + +<p>액티비티를 위해 새 문서를 생성하는 경우, +{@link android.app.ActivityManager.AppTask} 클래스의 +{@link android.app.ActivityManager.AppTask#startActivity(android.content.Context, android.content.Intent, android.os.Bundle) startActivity()} + 메서드를 호출하고, 이것을 액티비티를 시작하는 인텐트에 전달하면 됩니다. 논리적인 중단을 삽입하여 시스템이 개요 화면에서 액티비티를 +새 작업으로 처리하도록 하려면, {@link android.content.Intent}의 +{@link android.content.Intent#addFlags(int) addFlags()} 메서드에 있는 {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} +플래그를 전달하여 액티비티를 시작하도록 합니다.</p> + +<p class="note"><strong>참고:</strong> {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} +플래그가 {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET} 플래그를 +대체합니다. 이것은 Android 5.0(API 레벨 21)부터 사용이 중단되었습니다.</p> + +<p>새 문서를 생성하면서 {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} 플래그를 설정하는 경우, +시스템은 항상 대상 액티비티를 루트로 하여 새 작업을 만듭니다. +이렇게 설정하면 같은 문서를 하나 이상의 작업에서 열 수 있습니다. 다음 코드는 기본 액티비티가 이 작업을 수행하는 방법을 +보여줍니다.</p> + +<p class="code-caption"><a href="{@docRoot}samples/DocumentCentricApps/index.html"> +DocumentCentricActivity.java</a></p> +<pre> +public void createNewDocument(View view) { + final Intent newDocumentIntent = newDocumentIntent(); + if (useMultipleTasks) { + newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + } + startActivity(newDocumentIntent); + } + + private Intent newDocumentIntent() { + boolean useMultipleTasks = mCheckbox.isChecked(); + final Intent newDocumentIntent = new Intent(this, NewDocumentActivity.class); + newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); + newDocumentIntent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, incrementAndGet()); + return newDocumentIntent; + } + + private static int incrementAndGet() { + Log.d(TAG, "incrementAndGet(): " + mDocumentCounter); + return mDocumentCounter++; + } +} +</pre> + +<p class="note"><strong>참고:</strong> {@code FLAG_ACTIVITY_NEW_DOCUMENT} + 플래그로 시작된 액티비티의 경우, 반드시 매니페스트에 {@code android:launchMode="standard"} 특성 값(기본)이 설정되어 +있어야 합니다.</p> + +<p>기본 액티비티가 새 액티비티를 시작하면 시스템은 기존의 작업을 검색하여 그 중 +해당 액티비티에 대한 인텐트 구성 요소 이름과 인텐트 데이터와 일치하는 인텐트를 가진 작업을 찾습니다. 그러한 작업을 +찾을 수 없거나 {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} + 플래그에 들어 있는 인텐트를 찾을 수 없는 경우, 해당 액티비티를 루트로 하여 새 작업이 생성됩니다. 원하는 항목을 찾으면, 시스템은 해당 작업을 전경으로 가지고 와 +새 인텐트를 {@link android.app.Activity#onNewIntent onNewIntent()}에 전달합니다. +새 액티비티가 인텐트를 받아 개요 화면에서 새 문서를 생성하며, 이는 다음 예시에 나타낸 +것과 같습니다.</p> + +<p class="code-caption"><a href="{@docRoot}samples/DocumentCentricApps/index.html"> +NewDocumentActivity.java</a></p> +<pre> +@Override +protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_new_document); + mDocumentCount = getIntent() + .getIntExtra(DocumentCentricActivity.KEY_EXTRA_NEW_DOCUMENT_COUNTER, 0); + mDocumentCounterTextView = (TextView) findViewById( + R.id.hello_new_document_text_view); + setDocumentCounterText(R.string.hello_new_document_counter); +} + +@Override +protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + /* If FLAG_ACTIVITY_MULTIPLE_TASK has not been used, this activity + is reused to create a new document. + */ + setDocumentCounterText(R.string.reusing_document_counter); +} +</pre> + + +<h3 id="#attr-doclaunch">작업 추가에 액티비티 특성 사용</h3> + +<p>액티비티는 자신의 매니페스트에 새 작업을 시작할 때는 항상 +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> + 특성, <a href="{@docRoot}guide/topics/manifest/activity-element.html#dlmode"> +{@code android:documentLaunchMode}</a>를 사용한다고 나타낼 수도 있습니다. 이 특성에는 네 가지 값이 있어 사용자가 애플리케이션으로 문서를 열면 +다음과 같은 효과를 발생시킵니다.</p> + +<dl> + <dt>"{@code intoExisting}"</dt> + <dd>액티비티가 문서에 대해 기존의 작업을 재사용합니다. 이것은 +{@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} 플래그를 설정할 때 +{@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} 플래그 <em>없이</em> 설정하는 것과 같습니다. +이는 위의 <a href="#flag-new-doc">작업 추가에 인텐트 플래그 사용</a>에서 설명한 것과 같습니다.</dd> + + <dt>"{@code always}"</dt> + <dd>액티비티가 문서에 대해 새 작업을 생성하며, 이는 문서가 이미 열려 있는 경우라도 마찬가지입니다. 이 값을 +사용하는 것은 {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} + 및 {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} 플래그를 둘 다 설정하는 것과 같습니다.</dd> + + <dt>"{@code none”}"</dt> + <dd>액티비티가 문서에 대해 새 작업을 생성하지 않습니다. 개요 화면은 액티비티를 기본 상태에서와 +같이 다룹니다. 즉 앱에 대한 하나의 작업을 표시하며, 이때 사용자가 +마지막으로 호출한 작업이 무엇이든 관계 없이 그 작업에서부터 재개합니다.</dd> + + <dt>"{@code never}"</dt> + <dd>액티비티가 문서에 대해 새 작업을 생성하지 않습니다. 이 값을 설정하면 +{@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} + 및 {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} 플래그의 행동을 +재정의합니다(이들 중 하나가 인텐트에서 설정되어 있는 경우). 개요 화면은 앱에 대한 하나의 작업을 표시하고, +이것이 사용자가 마지막으로 호출한 액티비티가 무엇이든 그것부터 재개합니다.</dd> +</dl> + +<p class="note"><strong>참고:</strong> {@code none} 및 {@code never}를 제외한 다른 값의 경우, +액티비티를 {@code launchMode="standard"}로 정의해야 합니다. 이 특성을 지정하지 않으면 +{@code documentLaunchMode="none"}이 사용됩니다.</p> + +<h2 id="removing">작업 제거</h2> + +<p>기본적으로 문서 작업은 해당되는 액티비티가 완료되면 자동으로 개요 화면에서 +제거됩니다. 이 행동을 재정의하려면 {@link android.app.ActivityManager.AppTask} 클래스를 사용합니다. 이때 +{@link android.content.Intent} 플래그 또는 <code><a href="{@docRoot}guide/topics/manifest/activity-element.html"> +<activity></a></code> 특성을 함께 사용하십시오.</p> + +<p>개요 화면에서 작업을 완전히 제외하려면 언제든 +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> +특성, <a href="{@docRoot}guide/topics/manifest/activity-element.html#exclude"> +{@code android:excludeFromRecents}</a>를 {@code true}로 설정합니다.</p> + +<p>개요 화면에서 앱이 포함할 수 있는 작업의 최대 개수를 설정하려면 +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> + 특성 <a href="{@docRoot}guide/topics/manifest/activity-element.html#maxrecents">{@code android:maxRecents} +</a>를 정수 값으로 설정합니다. 기본은 16개입니다. 작업의 최대 대수에 도달하면 가장 예전에 사용된 작업이 개요 화면에서 +제거됩니다. {@code android:maxRecents} 최대값은 +50입니다(메모리 용량이 적은 기기에서는 25). 1 미만의 값은 유효하지 않습니다.</p> + +<h3 id="#apptask-remove">작업 제거에 AppTask 클래스 사용</h3> + +<p>개요 화면에서 새 작업을 생성하는 액티비티에서는 작업을 언제 제거할 것인지와 +그와 관련된 모든 액티비티를 언제 완료할 것인지 지정할 수 있습니다. +{@link android.app.ActivityManager.AppTask#finishAndRemoveTask() finishAndRemoveTask()} 메서드를 호출하면 됩니다.</p> + +<p class="code-caption"><a href="{@docRoot}samples/DocumentCentricApps/index.html"> +NewDocumentActivity.java</a></p> +<pre> +public void onRemoveFromRecents(View view) { + // The document is no longer needed; remove its task. + finishAndRemoveTask(); +} +</pre> + +<p class="note"><strong>참고:</strong> +{@link android.app.ActivityManager.AppTask#finishAndRemoveTask() finishAndRemoveTask()} 메서드를 +사용하면 {@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS} 태그 사용을 재정의합니다. +이 내용은 아래에서 설명합니다.</p> + +<h3 id="#retain-finished">완료된 작업 보존</h3> + +<p>작업을 개요 화면에 보존하려면(액티비티가 완료되었더라도), +액티비티를 시작하는 인텐트의 {@link android.content.Intent#addFlags(int) addFlags()} 메서드에 있는 +{@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS} 플래그를 전달합니다.</p> + +<p class="code-caption"><a href="{@docRoot}samples/DocumentCentricApps/index.html"> +DocumentCentricActivity.java</a></p> +<pre> +private Intent newDocumentIntent() { + final Intent newDocumentIntent = new Intent(this, NewDocumentActivity.class); + newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | + android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS); + newDocumentIntent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, incrementAndGet()); + return newDocumentIntent; +} +</pre> + +<p>같은 효과를 얻기 위해 +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> + 특성 <a href="{@docRoot}guide/topics/manifest/activity-element.html#autoremrecents"> +{@code android:autoRemoveFromRecents}</a>를 {@code false}로 설정해도 됩니다. 기본 값은 문서 액티비티의 경우 {@code true} +이고, 일반 액티비티의 경우 {@code false}입니다. 이 특성을 사용하면 이전에 논한 것과 같이 +{@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS} 플래그를 재정의하게 됩니다.</p> + + + + + + + diff --git a/docs/html-intl/intl/ko/guide/components/services.jd b/docs/html-intl/intl/ko/guide/components/services.jd new file mode 100644 index 000000000000..fb95ea5ed0fa --- /dev/null +++ b/docs/html-intl/intl/ko/guide/components/services.jd @@ -0,0 +1,813 @@ +page.title=서비스 +@jd:body + +<div id="qv-wrapper"> +<ol id="qv"> +<h2>이 문서의 내용</h2> +<ol> +<li><a href="#Basics">기본 정보</a></li> +<ol> + <li><a href="#Declaring">매니페스트에서 서비스 선언하기</a></li> +</ol> +<li><a href="#CreatingAService">시작된 서비스 생성하기</a> + <ol> + <li><a href="#ExtendingIntentService">IntentService 클래스 확장하기</a></li> + <li><a href="#ExtendingService">서비스 클래스 확장하기</a></li> + <li><a href="#StartingAService">서비스 시작</a></li> + <li><a href="#Stopping">서비스 중단</a></li> + </ol> +</li> +<li><a href="#CreatingBoundService">바인딩된 서비스 생성</a></li> +<li><a href="#Notifications">사용자에게 알림 전송</a></li> +<li><a href="#Foreground">전경에서 서비스 실행하기</a></li> +<li><a href="#Lifecycle">서비스 수명 주기 관리</a> +<ol> + <li><a href="#LifecycleCallbacks">수명 주기 콜백 구현하기</a></li> +</ol> +</li> +</ol> + +<h2>Key 클래스</h2> +<ol> + <li>{@link android.app.Service}</li> + <li>{@link android.app.IntentService}</li> +</ol> + +<h2>샘플</h2> +<ol> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/ServiceStartArguments.html">{@code + ServiceStartArguments}</a></li> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalService.html">{@code + LocalService}</a></li> +</ol> + +<h2>참고 항목</h2> +<ol> +<li><a href="{@docRoot}guide/components/bound-services.html">바인딩된 서비스</a></li> +</ol> + +</div> + + +<p>{@link android.app.Service}는 배경에서 오래 실행되는 작업을 +수행할 수 있는 애플리케이션 구성 요소이며 사용자 인터페이스를 제공하지 않습니다. 또 다른 +애플리케이션 구성 요소가 서비스를 시작할 수 있으며, 이는 사용자가 또 다른 +애플리케이션으로 전환하더라도 배경에서 계속해서 실행됩니다. 이외에도, 구성 요소를 서비스에 바인딩하여 +서비스와 상호 작용할 수 있으며, 심지어는 프로세스 간 통신(IPC)도 수행할 수 있습니다. 예를 들어 한 서비스는 +네트워크 트랜잭션을 처리하고, 음악을 재생하고 파일 I/O를 수행하거나 콘텐츠 제공자와 상호 작용할 수 있으며 +이 모든 것을 배경에서 수행할 수 있습니다.</p> + +<p>서비스는 본질적으로 두 가지 형식을 취합니다.</p> + +<dl> + <dt>시작됨</dt> + <dd>서비스가 "시작된" 상태가 되려면 애플리케이션 구성 요소(예: 액티비티)가 +{@link android.content.Context#startService startService()}를 호출하여 시작하면 됩니다. 서비스는 한 번 시작되고 나면 +배경에서 무기한으로 실행될 수 있으며, 이는 해당 서비스를 시작한 구성 요소가 소멸되었더라도 무관합니다. 보통, +시작된 서비스는 한 작업을 수행하고 결과를 발신자에게 반환하지 않습니다. +예를 들어 네트워크에서 파일을 다운로드하거나 업로드할 수 있습니다. 작업을 완료하면, 해당 서비스는 +알아서 중단되는 것이 정상입니다.</dd> + <dt>바인딩됨</dt> + <dd>서비스가 "바인딩된" 상태가 되려면 애플리케이션 구성 요소가 {@link +android.content.Context#bindService bindService()}를 사용하여 해당 서비스에 바인딩되면 됩니다. 바인딩된 서비스는 클라이언트-서버 +인터페이스를 제공하여 구성 요소가 서비스와 상호 작용할 수 있도록 해주며, 결과를 가져올 수도 있고 심지어 +이와 같은 작업을 여러 프로세스에 걸쳐 프로세스 간 통신(IPC)으로 수행할 수도 있습니다. 바인딩된 서비스는 또 다른 애플리케이션 구성 요소가 +이에 바인딩되어 있는 경우에만 실행됩니다. 여러 개의 구성 요소가 서비스에 한꺼번에 바인딩될 수 있지만, +이 모든 것이 바인딩을 해제하면 해당 서비스는 소멸됩니다.</dd> +</dl> + +<p>이 문서는 주로 이러한 두 가지 유형의 서비스를 따로따로 논하지만, 서비스는 +두 가지 방식 모두로 작동할 수 있습니다. 즉 서비스가 시작될 수도 있고(나아가 무기한으로 실행되고) 바인딩도 허용할 수 있다는 뜻입니다. +이는 그저 두어 가지 콜백 메서드의 구현 여부에 달린 문제입니다. {@link +android.app.Service#onStartCommand onStartCommand()}를 사용하면 구성 요소가 서비스를 시작할 수 있게 허용하고, {@link +android.app.Service#onBind onBind()}를 사용하면 바인딩을 허용합니다.</p> + +<p>애플리케이션이 시작되었든, 바인딩되었든 아니면 양쪽 모두이든 모든 애플리케이션 구성 요소가 +해당 서비스를 사용할 수 있으며(별도의 애플리케이션에서라도), 이는 어느 구성 요소든 액티비티를 +사용할 수 있는 것과 같습니다. 이를 {@link android.content.Intent}로 시작하면 됩니다. 그러나, +매니페스트에서 서비스를 비공개로 선언하고 다른 애플리케이션으로부터의 액세스를 차단할 수도 있습니다. 이것은 +<a href="#Declaring">매니페스트에서 서비스 +선언하기</a>에 관한 섹션에서 더 자세히 이야기합니다.</p> + +<p class="caution"><strong>주의:</strong> 서비스는 자신의 호스팅 프로세스의 +기본 스레드에서 실행됩니다. 서비스는 자신의 스레드를 직접 생성하지 <strong>않으며</strong>, +별도의 프로세스에서 실행되지도 <strong>않습니다</strong>(별도로 지정하는 경우는 예외). 이것은 즉, +서비스가 CPU 집약적인 작업을 수행할 예정이거나 차단적인 작업을 수행할 예정인 경우(예를 들어 MP3 +재생 또는 네트워킹 등), 서비스 내에 새 스레드를 생성하여 해당 작업을 수행하도록 해야 한다는 뜻입니다. 별도의 스레드를 사용하면 +'애플리케이션이 응답하지 않습니다(ANR)' 오류가 일어날 위험을 줄일 수 있으며 +애플리케이션의 기본 스레드는 액티비티와 사용자 상호 작용 전용으로 유지될 수 있습니다.</p> + + +<h2 id="Basics">기본 정보</h2> + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h3>서비스와 스레드 중 어느 것을 사용해야 할까요?</h3> + <p>서비스는 그저 배경에서 실행될 수 있는 구성 요소일 뿐입니다. 이는 사용자가 +애플리케이션과 상호 작용하지 않아도 관계 없이 해당됩니다. 따라서, 서비스를 생성하는 것은 꼭 그것이 필요할 때만으로 국한되어야 +합니다.</p> + <p>기본 스레드 외부에서 작업을 수행해야 하지만 사용자가 애플리케이션과 상호 작용 중인 +동안에만 수행하면 되는 경우라면, 서비스가 아니라 그 대신 새 스레드를 생성해야 합니다. 예를 들어 +액티비티가 실행되는 중에만 음악을 재생하고자 하는 경우, +{@link android.app.Activity#onCreate onCreate()} 안에 스레드를 생성하고 이를 {@link +android.app.Activity#onStart onStart()}에서 실행하기 시작한 다음 {@link android.app.Activity#onStop +onStop()}에서 중단하면 됩니다. 또한, 기존의 {@link java.lang.Thread} 클래스 대신 +{@link android.os.AsyncTask} 또는 {@link android.os.HandlerThread}를 사용하는 방안도 고려하십시오. 스레드에 관한 자세한 정보는 <a href="{@docRoot}guide/components/processes-and-threads.html#Threads">프로세스 및 +스레딩</a> 문서를 참조하십시오.</p> + <p>서비스를 사용하는 경우 기본적으로 애플리케이션의 기본 스레드에서 +계속 실행되므로 서비스가 집약적이거나 차단적인 작업을 수행하는 경우 여전히 서비스 내에 +새 스레드를 생성해야 한다는 점을 명심하십시오.</p> +</div> +</div> + +<p>서비스를 생성하려면 {@link android.app.Service}의 하위 클래스를 생성해야 합니다(아니면 이의 +기존 하위 클래스 중 하나). 구현에서는 서비스 수명 주기의 주요 측면을 처리하는 콜백 메서드를 +몇 가지 재정의해야 하며 서비스에 바인딩할 구성 요소에 대한 메커니즘을 +제공해야 합니다(해당되는 경우). 재정의해야 하는 가장 중요한 콜백 메서드는 다음과 같습니다.</p> + +<dl> + <dt>{@link android.app.Service#onStartCommand onStartCommand()}</dt> + <dd>시스템이 이 메서드를 호출하는 것은 또 다른 구성 요소(예: 액티비티)가 서비스를 +시작하도록 요청하는 경우입니다. 이때 {@link android.content.Context#startService +startService()}를 호출하는 방법을 씁니다. 이 메서드가 실행되면 서비스가 시작되고 배경에서 무기한으로 실행될 수 +있습니다. 이것을 구성하면 서비스의 작업이 완료되었을 때 해당 서비스를 중단하는 것은 +개발자 본인의 책임입니다. 이때 {@link android.app.Service#stopSelf stopSelf()} 또는 {@link +android.content.Context#stopService stopService()}를 호출하면 됩니다 (바인딩만 제공하고자 하는 경우, 이 메서드를 구현하지 +않아도 됩니다).</dd> + <dt>{@link android.app.Service#onBind onBind()}</dt> + <dd>시스템이 이 메서드를 호출하는 것은 또 다른 구성 요소가 해당 서비스에 바인딩되고자 하는 경우 +(예를 들어 RPC를 수행하기 위해)입니다. 이때 {@link android.content.Context#bindService +bindService()}를 호출하는 방법을 씁니다. 이 메서드를 구현할 때에는 클라이언트가 서비스와 통신을 주고받기 위해 사용할 +인터페이스를 제공해야 합니다. 이때 {@link android.os.IBinder}를 반환하면 됩니다. 이 메서드는 항상 +구현해야 하지만, 바인딩을 허용하지 않고자 하면 null을 반환해야 합니다.</dd> + <dt>{@link android.app.Service#onCreate()}</dt> + <dd>시스템이 이 메서드를 호출하는 것은 서비스가 처음 생성되어 일회성 설정 +절차를 수행하는 경우입니다({@link android.app.Service#onStartCommand onStartCommand()} 또는 +{@link android.app.Service#onBind onBind()}를 호출하기 전에). 서비스가 이미 실행 중인 경우, 이 메서드는 호출되지 +않습니다.</dd> + <dt>{@link android.app.Service#onDestroy()}</dt> + <dd>시스템이 이 메서드를 호출하는 것은 해당 서비스를 더 이상 사용하지 않고 소멸시키는 경우입니다. +서비스에 이것을 구현해야 스레드, 등록된 각종 수신기(listener, receiver) 등 +모든 리소스를 정리할 수 있습니다. 이것이 서비스가 수신하는 마지막 호출입니다.</dd> +</dl> + +<p>한 구성 요소가 {@link +android.content.Context#startService startService()}를 호출하여 서비스를 시작하면({@link +android.app.Service#onStartCommand onStartCommand()}로의 호출을 초래함), 해당 서비스는 +알아서 {@link android.app.Service#stopSelf()}로 스스로를 중단할 때까지 또는 +또 다른 구성 요소가 {@link android.content.Context#stopService stopService()}를 호출하여 서비스를 중단시킬 때까지 실행 중인 상태로 유지됩니다.</p> + +<p>한 구성 요소가 +{@link android.content.Context#bindService bindService()}를 호출하여 서비스를 생성하는 경우(그리고 {@link +android.app.Service#onStartCommand onStartCommand()}를 호출하지 <em>않은</em> 경우), 해당 서비스는 +해당 구성 요소가 바인딩되어 있는 경우에만 실행됩니다. 서비스가 모든 클라이언트로부터 바인딩 해제되면 시스템이 이를 +소멸시킵니다.</p> + +<p>Android 시스템이 서비스를 강제 중단시키는 것은 메모리가 부족하여 사용자가 초점을 집중하고 있는 +액티비티를 위해 시스템 리소스를 회복해야만 하는 경우로만 국한됩니다. 해당 서비스가 사용자의 주목을 +끌고 있는 액티비티에 바인딩되어 있다면 중단될 가능성이 낮고, 서비스가 <a href="#Foreground">전경에서 실행</a>된다고 선언된 경우(나중에 자세히 논함), 거의 절대 중단되지 않습니다. +그렇지 않으면, 서비스가 시작되었고 오랫동안 실행되는 경우 +시간이 지나면서 시스템이 배경 작업 목록에서의 이 서비스의 위치를 점점 낮추고 +서비스는 중단되기 매우 쉬워집니다. 서비스가 시작되었다면 이를 시스템에 의한 재시작을 정상적으로 +처리하도록 디자인해야 합니다. 시스템이 서비스를 중단시키는 경우, 리소스를 다시 사용할 수 있게 되면 +시스템이 가능한 한 빨리 이를 다시 시작합니다(다만 이것은 개발자가 {@link +android.app.Service#onStartCommand onStartCommand()}에서 반환하는 값에도 좌우됩니다. 이 내용은 나중에 논합니다). 시스템이 서비스를 +소멸시킬 수 있는 경우에 대한 자세한 정보는 <a href="{@docRoot}guide/components/processes-and-threads.html">프로세스 및 스레딩</a> +문서를 참조하십시오.</p> + +<p>다음 섹션에서는 각 유형의 서비스를 생성하는 방법과 다른 애플리케이션 구성 요소에서 +이를 사용하는 방법을 배우게 됩니다.</p> + + + +<h3 id="Declaring">매니페스트에서 서비스 선언하기</h3> + +<p>액티비티(및 다른 구성 요소)와 마찬가지로, 서비스는 모두 애플리케이션의 매니페스트 +파일에서 선언해야 합니다.</p> + +<p>서비스를 선언하려면 <a href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> 요소를 +<a href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a> + 요소의 하위로 추가하면 됩니다. 예:</p> + +<pre> +<manifest ... > + ... + <application ... > + <service android:name=".ExampleService" /> + ... + </application> +</manifest> +</pre> + +<p>매니페스트에서 서비스를 선언하는 데 대한 자세한 정보는 <a href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> +요소 참조를 확인하십시오.</p> + +<p><a href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> 요소에 포함시킬 수 있는 다른 속성도 있습니다. +이를 포함시켜 서비스를 시작하는 데 필요한 권한과 서비스가 실행되어야 하는 프로세스 등의 +속성을 정의할 수 있습니다. <a href="{@docRoot}guide/topics/manifest/service-element.html#nm">{@code android:name}</a> +속성이 유일한 필수 속성입니다. 이것은 서비스의 클래스 이름을 나타냅니다. 일단 애플리케이션을 +게시하고 나면 이 이름을 변경해서는 안 됩니다. 이름을 변경하면 +서비스를 시작하거나 바인딩할 명시적 인텐트에 대한 종속성으로 인해 코드를 단절시킬 위험이 있기 때문입니다(블로그 게시물의 <a href="http://android-developers.blogspot.com/2011/06/things-that-cannot-change.html"> +바꿀 수 없는 항목</a>을 참조하십시오). + +<p>앱의 보안을 보장하려면 <strong> +{@link android.app.Service}을 시작하거나 바인딩할 때 항상 명시적 인텐트를 사용하고</strong> 서비스에 대한 인텐트 필터는 선언하지 마십시오. 어느 +서비스를 시작할지 어느 정도 모호성을 허용하는 것이 중요한 경우, 서비스에 대해 +인텐트 필터를 제공하고 구성 요소 이름을 {@link +android.content.Intent}에서 배제할 수 있지만, 그러면 해당 인텐트에 대한 패키지를 {@link +android.content.Intent#setPackage setPackage()}로 설정하여 대상 서비스에 대해 충분한 명확화를 +제공하도록 해야 합니다.</p> + +<p>이외에도 서비스를 본인의 앱에만 사용 가능하도록 보장할 수도 있습니다. +<a href="{@docRoot}guide/topics/manifest/service-element.html#exported">{@code android:exported}</a> + 속성을 포함시킨 뒤 이를 {@code "false"}로 설정하면 됩니다. 이렇게 하면 다른 앱이 여러분의 서비스를 시작하지 못하도록 효과적으로 방지해주며, +이는 명시적 인텐트를 사용하는 경우에도 문제 없이 적용됩니다.</p> + + + + +<h2 id="CreatingStartedService">시작된 서비스 생성하기</h2> + +<p>시작된 서비스란 다른 구성 요소가 {@link +android.content.Context#startService startService()}를 호출하여 시작하고, 그 결과 서비스의 +{@link android.app.Service#onStartCommand onStartCommand()} 메서드를 호출하는 결과를 초래한 것을 말합니다.</p> + +<p>서비스가 시작되면 이를 시작한 구성 요소와 독립된 자신만의 +수명 주기를 가지며 해당 서비스는 배경에서 무기한으로 실행될 수 있습니다. 이는 해당 서비스를 +시작한 구성 요소가 소멸되었더라도 무관합니다. 따라서, 서비스는 작업이 완료되면 +{@link android.app.Service#stopSelf stopSelf()}를 호출하여 스스로 알아서 중단되는 것이 정상이며 아니면 다른 구성 요소가 +{@link android.content.Context#stopService stopService()}를 호출하여 중단시킬 수도 있습니다.</p> + +<p>애플리케이션 구성 요소(예: 액티비티)가 서비스를 시작하려면 {@link +android.content.Context#startService startService()}를 호출하고, {@link android.content.Intent}를 +전달하면 됩니다. 이것은 서비스를 나타내고 서비스가 사용할 모든 데이터를 포함합니다. 서비스는 이 +{@link android.content.Intent}를 {@link android.app.Service#onStartCommand +onStartCommand()} 메서드에서 수신합니다.</p> + +<p>예를 들어 어느 액티비티가 온라인 데이터베이스에 데이터를 약간 저장해야 한다고 가정합니다. 액티비티가 +동반자 서비스를 시작하여 저장할 데이터를 이에 전달할 수 있습니다. 이때 인텐트를 {@link +android.content.Context#startService startService()}에 전달하면 됩니다. 서비스는 이 인텐트를 {@link +android.app.Service#onStartCommand onStartCommand()}에서 수신하고 인터넷에 연결한 다음 데이터베이스 +트랜잭션을 수행합니다. 작업을 완료하면, 해당 서비스는 알아서 스스로 중단되고 +소멸됩니다.</p> + +<p class="caution"><strong>주의:</strong> 서비스는 기본적으로 자신이 선언된 애플리케이션의 같은 +프로세스에서 실행되기도 하고 해당 애플리케이션의 기본 스레드에서 실행되기도 합니다. 따라서, 사용자가 +같은 애플리케이션의 액티비티와 상호 작용하는 동안 서비스가 집약적이거나 차단적인 작업을 수행하는 경우, +해당 서비스 때문에 액티비티 성능이 느려지게 됩니다. 애플리케이션 성능에 영향을 미치는 것을 방지하려면, +서비스 내에서 새 스레드를 시작해야 합니다.</p> + +<p>기존에는 시작된 서비스를 생성하기 위해 확장할 수 있는 클래스가 두 개 있었습니다.</p> +<dl> + <dt>{@link android.app.Service}</dt> + <dd>이것이 모든 서비스의 기본 클래스입니다. 이 클래스를 확장하는 경우, 서비스의 모든 작업을 수행할 +새 스레드를 만드는 것이 중요합니다. 서비스가 기본적으로 애플리케이션의 기본 스레드를 사용하기 +때문인데, 이로 인해 애플리케이션이 실행 중인 모든 액티비티의 성능이 +느려질 수 있기 때문입니다.</dd> + <dt>{@link android.app.IntentService}</dt> + <dd>이것은 {@link android.app.Service}의 하위 클래스로, 작업자 스레드를 +사용하여 모든 시작 요청을 처리하되 한 번에 하나씩 처리합니다. 서비스가 여러 개의 요청을 +동시에 처리하지 않아도 되는 경우 이것이 최선의 옵션입니다. 해야 할 일은 {@link +android.app.IntentService#onHandleIntent onHandleIntent()}를 구현하는 것뿐으로, 이것이 각 시작 요청에 대한 인텐트를 수신하여 +개발자는 배경 작업을 수행할 수 있습니다.</dd> +</dl> + +<p>다음 섹션에서는 이와 같은 클래스 중 하나를 사용하여 서비스를 구현하는 방법을 +설명합니다.</p> + + +<h3 id="ExtendingIntentService">IntentService 클래스 확장하기</h3> + +<p>대부분의 시작된 서비스는 여러 개의 요청을 동시에 처리하지 않아도 되기 때문에 +(이는 사실 위험한 다중 스레딩 시나리오일 수 있습니다), 서비스를 구현할 때에는 +{@link android.app.IntentService} 클래스를 사용하는 것이 최선의 방안일 것입니다.</p> + +<p>{@link android.app.IntentService}는 다음과 같은 작업을 수행합니다.</p> + +<ul> + <li>애플리케이션의 기본 스레드와는 별도로 {@link +android.app.Service#onStartCommand onStartCommand()}에 전달된 모든 인텐트를 실행하는 기본 작업자 스레드를 +생성합니다.</li> + <li>한 번에 인텐트를 하나씩 {@link +android.app.IntentService#onHandleIntent onHandleIntent()} 구현에 전달하는 작업 대기열을 생성하므로 다중 스레딩에 대해 염려할 필요가 +전혀 없습니다.</li> + <li>시작 요청이 모두 처리된 후 서비스를 중단하므로 개발자가 +{@link android.app.Service#stopSelf}를 호출할 필요가 전혀 없습니다.</li> + <li>{@link android.app.IntentService#onBind onBind()}의 기본 구현을 제공하여 null을 +반환하도록 합니다.</li> + <li>{@link android.app.IntentService#onStartCommand +onStartCommand()}의 기본 구현을 제공하여 인텐트를 작업 대기열에 보내고, 다음으로 {@link +android.app.IntentService#onHandleIntent onHandleIntent()} 구현에 보내도록 합니다.</li> +</ul> + +<p>이 모든 것은 결론적으로 개발자가 직접 할 일은 클라이언트가 제공한 작업을 수행할 {@link +android.app.IntentService#onHandleIntent onHandleIntent()}를 구현하는 것뿐이라는 사실로 +이어집니다. (다만, 서비스에 대해 작은 생성자를 제공해야 하기도 합니다.)</p> + +<p>다음은 {@link android.app.IntentService}의 구현을 예시로 나타낸 것입니다.</p> + +<pre> +public class HelloIntentService extends IntentService { + + /** + * A constructor is required, and must call the super {@link android.app.IntentService#IntentService} + * constructor with a name for the worker thread. + */ + public HelloIntentService() { + super("HelloIntentService"); + } + + /** + * The IntentService calls this method from the default worker thread with + * the intent that started the service. When this method returns, IntentService + * stops the service, as appropriate. + */ + @Override + protected void onHandleIntent(Intent intent) { + // Normally we would do some work here, like download a file. + // For our sample, we just sleep for 5 seconds. + long endTime = System.currentTimeMillis() + 5*1000; + while (System.currentTimeMillis() < endTime) { + synchronized (this) { + try { + wait(endTime - System.currentTimeMillis()); + } catch (Exception e) { + } + } + } + } +} +</pre> + +<p>필요한 것은 이게 전부입니다. 생성자 하나와 {@link +android.app.IntentService#onHandleIntent onHandleIntent()} 구현뿐이죠.</p> + +<p>다른 콜백 메서드도 재정의하기로 결정하는 경우-예를 들어 {@link +android.app.IntentService#onCreate onCreate()}, {@link +android.app.IntentService#onStartCommand onStartCommand()} 또는 {@link +android.app.IntentService#onDestroy onDestroy()}-슈퍼 구현을 꼭 호출해야 합니다. +그래야 {@link android.app.IntentService}가 작업자 스레드의 수명을 적절하게 처리할 수 있습니다.</p> + +<p>예를 들어 {@link android.app.IntentService#onStartCommand onStartCommand()}는 반드시 +기본 구현을 반환해야 합니다(이로써 인텐트가 {@link +android.app.IntentService#onHandleIntent onHandleIntent()}로 전달되는 것입니다).</p> + +<pre> +@Override +public int onStartCommand(Intent intent, int flags, int startId) { + Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); + return super.onStartCommand(intent,flags,startId); +} +</pre> + +<p>{@link android.app.IntentService#onHandleIntent onHandleIntent()} 외에 슈퍼 클래스를 +호출하지 않아도 되는 유일한 메서드는 {@link android.app.IntentService#onBind +onBind()}입니다(다만 이를 구현하는 것은 서비스가 바인딩을 허용할 때에만 필요합니다).</p> + +<p>다음 섹션에서는 기본 {@link android.app.Service} +클래스를 확장할 때 같은 종류의 서비스를 구현하는 방법을 배우게 됩니다. 이때에는 코드가 훨씬 많이 필요하지만, +동시 시작 요청을 처리해야 하는 경우 이것이 적절할 수 있습니다.</p> + + +<h3 id="ExtendingService">서비스 클래스 확장하기</h3> + +<p>이전 섹션에서 본 것과 같이 {@link android.app.IntentService}를 사용하면 +시작된 서비스 구현이 매우 단순해집니다. 하지만 서비스가 다중 스레딩을 +수행해야 하는 경우(작업 대기열을 통해 시작 요청을 처리하는 대신), 그때는 +{@link android.app.Service} 클래스를 확장하여 각 인텐트를 처리하게 할 수 있습니다.</p> + +<p>비교를 위해 다음 예시의 코드를 보겠습니다. 이는 {@link +android.app.Service} 클래스의 구현으로, 위의 예시에서 {@link +android.app.IntentService}를 사용하여 수행한 것과 똑같은 작업을 수행합니다. 바꿔 말하면 각 시작 요청에 대해 +작업자 스레드를 사용하여 작업을 수행하고 한 번에 요청을 하나씩만 처리한다는 뜻입니다.</p> + +<pre> +public class HelloService extends Service { + private Looper mServiceLooper; + private ServiceHandler mServiceHandler; + + // Handler that receives messages from the thread + private final class ServiceHandler extends Handler { + public ServiceHandler(Looper looper) { + super(looper); + } + @Override + public void handleMessage(Message msg) { + // Normally we would do some work here, like download a file. + // For our sample, we just sleep for 5 seconds. + long endTime = System.currentTimeMillis() + 5*1000; + while (System.currentTimeMillis() < endTime) { + synchronized (this) { + try { + wait(endTime - System.currentTimeMillis()); + } catch (Exception e) { + } + } + } + // Stop the service using the startId, so that we don't stop + // the service in the middle of handling another job + stopSelf(msg.arg1); + } + } + + @Override + public void onCreate() { + // Start up the thread running the service. Note that we create a + // separate thread because the service normally runs in the process's + // main thread, which we don't want to block. We also make it + // background priority so CPU-intensive work will not disrupt our UI. + HandlerThread thread = new HandlerThread("ServiceStartArguments", + Process.THREAD_PRIORITY_BACKGROUND); + thread.start(); + + // Get the HandlerThread's Looper and use it for our Handler + mServiceLooper = thread.getLooper(); + mServiceHandler = new ServiceHandler(mServiceLooper); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); + + // For each start request, send a message to start a job and deliver the + // start ID so we know which request we're stopping when we finish the job + Message msg = mServiceHandler.obtainMessage(); + msg.arg1 = startId; + mServiceHandler.sendMessage(msg); + + // If we get killed, after returning from here, restart + return START_STICKY; + } + + @Override + public IBinder onBind(Intent intent) { + // We don't provide binding, so return null + return null; + } + + @Override + public void onDestroy() { + Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); + } +} +</pre> + +<p>보시다시피 {@link android.app.IntentService}를 사용할 때보다 훨씬 손이 많이 갑니다.</p> + +<p>그러나, 각 호출을 {@link android.app.Service#onStartCommand +onStartCommand()}로 직접 처리할 수 있기 때문에 여러 개의 요청을 동시에 수행할 수 있습니다. 이 예시는 그것을 +보여주는 것은 아니지만, 그런 작업을 원하는 경우 각 요청에 대해 새 스레드를 +하나씩 생성한 다음 곧바로 실행하면 됩니다(이전 요청이 끝날 때까지 기다리는 대신).</p> + +<p>{@link android.app.Service#onStartCommand onStartCommand()} 메서드가 반드시 +정수를 반환해야 한다는 사실을 유의하십시오. 정수는 시스템이 서비스를 중단시킨 경우 시스템이 해당 서비스를 +계속하는 방법에 대해 설명하는 값입니다(위에서 논한 바와 같이 {@link +android.app.IntentService}의 기본 구현이 이것을 개발자 대신 처리해줍니다. 개발자가 이를 수정할 수도 있습니다). +{@link android.app.Service#onStartCommand onStartCommand()}로부터의 반환 값은 반드시 +다음 상수 중 하나여야 합니다.</p> + +<dl> + <dt>{@link android.app.Service#START_NOT_STICKY}</dt> + <dd>시스템이 서비스를 {@link android.app.Service#onStartCommand +onStartCommand()} 반환 후에 중단시키면 서비스를 재생성하면 <em>안 됩니다.</em> 다만 전달할 +보류 인텐트가 있는 경우는 예외입니다. 이것은 서비스가 불필요하게 실행되는 일을 피할 수 있는 가장 안전한 옵션이며, +애플리케이션이 완료되지 않은 모든 작업을 단순히 재시작할 수 있을 때 좋습니다.</dd> + <dt>{@link android.app.Service#START_STICKY}</dt> + <dd>시스템이 서비스를 {@link android.app.Service#onStartCommand +onStartCommand()} 반환 후에 중단시키는 경우, 서비스를 재생성하고 {@link +android.app.Service#onStartCommand onStartCommand()}를 호출하되 마지막 인텐트를 다시 전달하지는 <em>마십시오.</em> +그 대신, 시스템이 null 인텐트로 {@link android.app.Service#onStartCommand onStartCommand()}를 +호출합니다. 다만 서비스를 시작할 보류 인텐트가 있는 경우만은 예외이며, 이럴 때에는 +그러한 인텐트를 전달합니다. 이것은 명령을 실행하지는 않지만 무기한으로 실행 중이며 작업을 기다리고 있는 +미디어 플레이어(또는 그와 비슷한 서비스)에 적합합니다.</dd> + <dt>{@link android.app.Service#START_REDELIVER_INTENT}</dt> + <dd>시스템이 서비스를 {@link android.app.Service#onStartCommand +onStartCommand()} 반환 후에 중단시키는 경우, 서비스를 재생성하고 서비스에 전달된 마지막 인텐트로 {@link +android.app.Service#onStartCommand onStartCommand()}를 +호출하십시오. 모든 보류 인텐트가 차례로 전달됩니다. 이것은 즉시 재개되어야 하는 작업을 +능동적으로 수행 중인 서비스(예를 들어 파일 다운로드 등)에 적합합니다.</dd> +</dl> +<p>이러한 반환 값에 대한 자세한 내용은 각 상수에 대해 링크로 연결된 참조 문서를 +확인하십시오.</p> + + + +<h3 id="StartingAService">서비스 시작</h3> + +<p>액티비티나 다른 구성 요소에서 서비스를 시작하려면 +{@link android.content.Intent}를(시작할 서비스를 나타냄) {@link +android.content.Context#startService startService()}에 전달하면 됩니다. Android 시스템이 서비스의 {@link +android.app.Service#onStartCommand onStartCommand()} 메서드를 호출하여 여기에 {@link +android.content.Intent}를 전달합니다. ({@link android.app.Service#onStartCommand +onStartCommand()}를 직접 호출하면 절대로 안 됩니다.)</p> + +<p>예를 들어 이전 섹션의 예시 서비스({@code +HelloService})를 액티비티가 시작하려면 {@link android.content.Context#startService +startService()}로 명시적 인텐트를 사용하면 됩니다.</p> + +<pre> +Intent intent = new Intent(this, HelloService.class); +startService(intent); +</pre> + +<p>{@link android.content.Context#startService startService()} 메서드가 즉시 반환되며 +Android 시스템이 서비스의 {@link android.app.Service#onStartCommand +onStartCommand()} 메서드를 호출합니다. 서비스가 이미 실행 중이지 않은 경우, 시스템은 우선 {@link +android.app.Service#onCreate onCreate()}를 호출하고, 다음으로 {@link android.app.Service#onStartCommand +onStartCommand()}를 호출합니다.</p> + +<p>서비스가 바인딩도 제공하지 않는 경우, {@link +android.content.Context#startService startService()}와 함께 전달된 인텐트가 애플리케이션 구성 요소와 서비스 사이의 +유일한 통신 방법입니다. 그러나 서비스가 결과를 돌려보내기를 원하는 경우, 서비스를 시작한 +클라이언트가 브로드캐스트를 위해 {@link android.app.PendingIntent}를 +만들 수 있고({@link android.app.PendingIntent#getBroadcast getBroadcast()} 사용) 이를 서비스를 시작한 +{@link android.content.Intent} 내의 서비스에 전달할 수 있습니다. 그러면 서비스가 +이 브로드캐스트를 사용하여 결과를 전달할 수 있게 됩니다.</p> + +<p>서비스를 시작하기 위한 여러 개의 요청은 서비스의 +{@link android.app.Service#onStartCommand onStartCommand()}로의 상응하는 여러 개의 호출이라는 결과를 낳습니다. 하지만, 서비스를 중단하려면 +이를 중단하라는 요청 하나({@link android.app.Service#stopSelf stopSelf()} 또는 {@link +android.content.Context#stopService stopService()} 사용)만 있으면 됩니다.</p> + + +<h3 id="Stopping">서비스 중단</h3> + +<p>시작된 서비스는 자신만의 수명 주기를 직접 관리해야 합니다. 다시 말해, 시스템이 +서비스를 중단하거나 소멸시키지 않는다는 뜻입니다. 다만 시스템 메모리를 회복해야 하고 서비스가 +{@link android.app.Service#onStartCommand onStartCommand()} 반환 후에도 계속 실행되는 경우는 예외입니다. 따라서, +서비스는 {@link android.app.Service#stopSelf stopSelf()}를 호출하여 스스로 중단시켜야 하고, 아니면 +다른 구성 요소가 {@link android.content.Context#stopService stopService()}를 호출하여 이를 중단시킬 수 있습니다.</p> + +<p>일단 {@link android.app.Service#stopSelf stopSelf()} 또는 {@link +android.content.Context#stopService stopService()}로 중단하기를 요청하고 나면 시스템이 서비스를 가능한 한 빨리 +소멸시킵니다.</p> + +<p>그러나, 서비스가 {@link +android.app.Service#onStartCommand onStartCommand()}로의 요청을 동시에 여러 개 처리하기를 바라는 경우라면 시작 요청 처리를 완료한 뒤에도 +서비스를 중단하면 안 됩니다. 그 이후 새 시작 요청을 받았을 수 있기 +때문입니다(첫 요청 종료 시에 중단하면 두 번째 요청을 종료시킵니다). 이 문제를 +피하려면, {@link android.app.Service#stopSelf(int)}를 사용하여 서비스를 +중단시키라는 개발자의 요청이 항상 최신 시작 요청에 기반하도록 해야 합니다. 다시 말해, {@link +android.app.Service#stopSelf(int)}를 호출할 때면 시작 요청의 ID({@link android.app.Service#onStartCommand onStartCommand()}에 전달된 +<code>startId</code>)를 전달하게 됩니다. 여기에 중단 요청이 +부합됩니다. 그런 다음 개발자가 {@link +android.app.Service#stopSelf(int)}를 호출할 수 있기 전에 서비스가 새 시작 요청을 받은 경우, ID가 일치하지 않게 되고 서비스는 중단되지 않습니다.</p> + +<p class="caution"><strong>주의:</strong> 서비스가 작업을 완료한 다음 애플리케이션이 +소속 서비스를 중단할 수 있어야 한다는 점이 중요합니다. 그래야 시스템 리소스 낭비를 피하고 배터리 전력 소모를 줄일 수 있습니다. 필요한 경우 +다른 구성 요소도 서비스를 중단시킬 수 있습니다. {@link +android.content.Context#stopService stopService()}를 호출하면 됩니다. 서비스에 대해 바인딩을 활성화하더라도, +서비스가 {@link +android.app.Service#onStartCommand onStartCommand()}로의 호출을 한 번이라도 받았으면 항상 서비스를 직접 중단시켜야 합니다.</p> + +<p>서비스의 수명 주기에 대한 자세한 정보는 아래에 있는 <a href="#Lifecycle">서비스 수명 주기 관리</a>에 관한 섹션을 참고하세요.</p> + + + +<h2 id="CreatingBoundService">바인딩된 서비스 생성</h2> + +<p>바인딩된 서비스는 애플리케이션 구성 요소가 자신에게 바인딩될 수 있도록 허용하는 서비스로, 이때 {@link +android.content.Context#bindService bindService()}를 호출하여 오래 지속되는 연결을 생성합니다 +(또한 보통은 구성 요소가 {@link +android.content.Context#startService startService()}를 호출하여 서비스를 <em>시작</em>하는 것을 허용하지 않습니다).</p> + +<p>액티비티와 애플리케이션의 다른 구성 요소에서 서비스와 상호 작용하기를 원하는 경우 +바인딩된 서비스를 생성해야 합니다. 아니면 애플리케이션의 기능 몇 가지를 프로세스 간 통신(IPC)을 통해 +다른 애플리케이션에 노출하고자 하는 경우에도 좋습니다.</p> + +<p>바인딩된 서비스를 생성하려면 {@link +android.app.Service#onBind onBind()} 콜백 메서드를 구현하여 서비스와의 통신을 위한 인터페이스를 정의하는 +{@link android.os.IBinder}를 반환하도록 해야 합니다. 그러면 다른 애플리케이션 구성 요소가 +{@link android.content.Context#bindService bindService()}를 호출하여 해당 인터페이스를 검색하고, 서비스에 있는 메서드를 +호출하기 시작할 수 있습니다. 서비스는 자신에게 바인딩된 애플리케이션 구성 요소에게 도움이 되기 위해서만 +존재하는 것이므로, 서비스에 바인딩된 구성 요소가 없으면 시스템이 이를 소멸시킵니다(바인딩된 서비스는 시작된 서비스처럼 +{@link android.app.Service#onStartCommand onStartCommand()}를 통해 +중단시키지 <em>않아도</em> 됩니다).</p> + +<p>바인딩된 서비스를 생성하려면 가장 먼저 해야 할 일은 클라이언트가 서비스와 +통신할 수 있는 방법을 나타내는 인터페이스를 정의하는 것입니다. 서비스와 클라이언트 사이에서 쓰이는 이 인터페이스는 +반드시 {@link android.os.IBinder}의 구현이어야 하며 이를 +서비스가 {@link android.app.Service#onBind +onBind()} 콜백 메서드에서 반환해야 합니다. 클라이언트가 {@link android.os.IBinder}를 수신하면 해당 인터페이스를 통해 서비스와 +상호 작용을 시작할 수 있습니다.</p> + +<p>여러 클라이언트가 서비스에 한꺼번에 바인딩될 수 있습니다. 클라이언트가 서비스와의 상호 작용을 완료하면 이는 +{@link android.content.Context#unbindService unbindService()}를 호출하여 바인딩을 해제합니다. 서비스에 +바인딩된 클라이언트가 하나도 없으면 시스템이 해당 서비스를 소멸시킵니다.</p> + +<p>바인딩된 서비스를 구현하는 데에는 여러 가지 방법이 있으며 그러한 구현은 시작된 서비스보다 +훨씬 복잡합니다. 따라서 바인딩된 서비스 논의는 +<a href="{@docRoot}guide/components/bound-services.html">바인딩된 서비스</a>에 관한 별도의 문서에서 다룹니다.</p> + + + +<h2 id="Notifications">사용자에게 알림 전송</h2> + +<p>서비스는 일단 실행되고 나면 사용자에게 <a href="{@docRoot}guide/topics/ui/notifiers/toasts.html">알림 메시지</a> 또는 <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">상태 표시줄 알림</a> 등을 사용해 이벤트를 알릴 수 있습니다.</p> + +<p>알림 메시지란 현재 창의 표면에 잠시 나타났다가 사라지는 메시지이고, +상태 표시줄 알림은 상태 표시줄에 메시지가 담긴 아이콘을 제공하여 사용자가 이를 선택하여 +조치를 취할 수 있게 하는 것입니다(예: 액티비티 시작).</p> + +<p>보통, 일종의 배경 작업이 완료되었고 +(예: 파일 다운로드 완료) 이제 사용자가 그에 대해 조치를 취할 수 있는 경우 상태 표시줄 알림이 +최선의 기법입니다. 사용자가 확장된 보기에서 알림을 선택하면, +해당 알림이 액티비티를 시작할 수 있습니다(예: 다운로드한 파일 보기).</p> + +<p>자세한 정보는 <a href="{@docRoot}guide/topics/ui/notifiers/toasts.html">알림 메시지</a> 또는 <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">상태 표시줄 알림</a> +개발자 가이드를 참조하십시오.</p> + + + +<h2 id="Foreground">전경에서 서비스 실행하기</h2> + +<p>전경 서비스는 사용자가 능동적으로 인식하고 있으므로 메모리 부족 시에도 +시스템이 중단할 후보로 고려되지 않는 서비스를 말합니다. 전경 +서비스는 상태 표시줄에 대한 알림을 제공해야 합니다. 이것은 +"진행 중" 제목 아래에 배치되며, 이는 곧 해당 알림은 서비스가 중단되었거나 +전경에서 제거되지 않은 이상 무시할 수 없다는 뜻입니다.</p> + +<p>예를 들어 서비스에서 음악을 재생하는 음악 플레이어는 전경에서 +실행되도록 설정해야 합니다. 사용자가 이것의 작동을 분명히 인식하고 있기 +때문입니다. 상태 표시줄에 있는 알림은 현재 노래를 나타내고 +사용자로 하여금 음악 플레이어와 상호 작용할 액티비티를 시작하게 해줄 수도 있습니다.</p> + +<p>서비스가 전경에서 실행되도록 요청하려면 {@link +android.app.Service#startForeground startForeground()}를 호출하면 됩니다. 이 메서드는 두 개의 매개변수를 취합니다. +그 중 하나는 해당 알림을 고유하게 식별하는 정수이고 다른 하나는 상태 표시줄에 해당되는 {@link +android.app.Notification}입니다. 예:</p> + +<pre> +Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text), + System.currentTimeMillis()); +Intent notificationIntent = new Intent(this, ExampleActivity.class); +PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); +notification.setLatestEventInfo(this, getText(R.string.notification_title), + getText(R.string.notification_message), pendingIntent); +startForeground(ONGOING_NOTIFICATION_ID, notification); +</pre> + +<p class="caution"><strong>주의:</strong> {@link +android.app.Service#startForeground startForeground()}에 부여하는 정수 ID가 0이면 안 됩니다.</p> + + +<p>서비스를 전경에서 제거하려면 {@link +android.app.Service#stopForeground stopForeground()}를 호출하면 됩니다. 이 메서드는 부울 값을 취하며, 이것이 +상태 표시줄 알림도 제거할지 여부를 나타냅니다. 이 메서드는 서비스를 중단시키지 <em>않습니다</em>. + 그러나, 서비스가 전경에서 실행 중인 동안 서비스를 중단시키면 +알림도 마찬가지로 제거됩니다.</p> + +<p>알림에 대한 자세한 정보는 <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">상태 표시줄 +알림 생성</a>을 참조하십시오.</p> + + + +<h2 id="Lifecycle">서비스 수명 주기 관리</h2> + +<p>서비스의 수명 주기는 액티비티의 수명 주기보다 훨씬 간단합니다. 하지만, 서비스를 생성하고 +소멸시키는 방법에 특히 주의를 기울여야 한다는 면에서 중요도는 이쪽이 더 높습니다. 서비스는 사용자가 모르는 채로 +배경에서 실행될 수 있기 때문입니다.</p> + +<p>서비스 수명 주기—생성되었을 때부터 소멸될 때까지—는 두 가지 서로 다른 경로를 +따를 수 있습니다.</p> + +<ul> +<li>시작된 서비스 + <p>서비스는 또 다른 구성 요소가 {@link +android.content.Context#startService startService()}를 호출하면 생성됩니다. 그러면 서비스가 무기한으로 실행될 수 있으며 +스스로 알아서 중단되어야 합니다. 이때 {@link +android.app.Service#stopSelf() stopSelf()}를 호출하는 방법을 씁니다. 또 다른 구성 요소도 서비스를 중단시킬 수 +있습니다. {@link android.content.Context#stopService +stopService()}를 호출하면 됩니다. 서비스가 중단되면 시스템이 이를 소멸시킵니다.</p></li> + +<li>바인딩된 서비스 + <p>서비스는 또 다른 구성 요소(클라이언트)가 {@link +android.content.Context#bindService bindService()}를 호출하면 생성됩니다. 그러면 클라이언트가 +{@link android.os.IBinder} 인터페이스를 통해 서비스와 통신을 주고받을 수 있습니다. 클라이언트가 연결을 종료하려면 +{@link android.content.Context#unbindService unbindService()}를 호출하면 됩니다. 여러 클라이언트가 같은 서비스에 +바인딩될 수 있으며, 이 모두가 바인딩을 해제하면 시스템이 해당 서비스를 소멸시킵니다 (서비스가 스스로를 중단시키지 +<em>않아도</em> 됩니다).</p></li> +</ul> + +<p>이와 같은 두 가지 경로는 완전히 별개의 것은 아닙니다. 다시 말해, 이미 +{@link android.content.Context#startService startService()}로 시작된 서비스에 바인딩할 수도 있다는 뜻입니다. 예를 +들어, 배경 음악 서비스를 시작하려면 {@link android.content.Context#startService +startService()}를 호출하되 재생할 음악을 식별하는 {@link android.content.Intent}를 사용하면 됩니다. 나중에, +아마도 사용자가 플레이어에 좀 더 많은 통제권을 발휘하고자 하거나 +현재 노래에 대한 정보를 얻고자 할 때, 액티비티가 서비스에 바인딩될 수 있습니다. {@link +android.content.Context#bindService bindService()}를 사용하면 됩니다. 이런 경우에는 {@link +android.content.Context#stopService stopService()} 또는 {@link android.app.Service#stopSelf +stopSelf()}도 클라이언트가 모두 바인딩 해제될 때까지 실제로 서비스를 중단시키지 않습니다. </p> + + +<h3 id="LifecycleCallbacks">수명 주기 콜백 구현하기</h3> + +<p>액티비티와 마찬가지로 서비스에도 수명 주기 콜백 메서드가 있어 이를 구현하면 서비스의 +상태 변경 내용을 모니터링할 수 있고 적절한 시기에 작업을 수행할 수 있습니다. 다음의 골격 +서비스는 각 수명 주기 메서드를 설명한 것입니다.</p> + +<pre> +public class ExampleService extends Service { + int mStartMode; // indicates how to behave if the service is killed + IBinder mBinder; // interface for clients that bind + boolean mAllowRebind; // indicates whether onRebind should be used + + @Override + public void {@link android.app.Service#onCreate onCreate}() { + // The service is being created + } + @Override + public int {@link android.app.Service#onStartCommand onStartCommand}(Intent intent, int flags, int startId) { + // The service is starting, due to a call to {@link android.content.Context#startService startService()} + return <em>mStartMode</em>; + } + @Override + public IBinder {@link android.app.Service#onBind onBind}(Intent intent) { + // A client is binding to the service with {@link android.content.Context#bindService bindService()} + return <em>mBinder</em>; + } + @Override + public boolean {@link android.app.Service#onUnbind onUnbind}(Intent intent) { + // All clients have unbound with {@link android.content.Context#unbindService unbindService()} + return <em>mAllowRebind</em>; + } + @Override + public void {@link android.app.Service#onRebind onRebind}(Intent intent) { + // A client is binding to the service with {@link android.content.Context#bindService bindService()}, + // after onUnbind() has already been called + } + @Override + public void {@link android.app.Service#onDestroy onDestroy}() { + // The service is no longer used and is being destroyed + } +} +</pre> + +<p class="note"><strong>참고:</strong> 액티비티 수명 주기 콜백 메서드와는 달리 이와 같은 콜백 메서드를 구현하는 데에는 +슈퍼클래스 구현을 호출하지 <em>않아도</em> 됩니다.</p> + +<img src="{@docRoot}images/service_lifecycle.png" alt="" /> +<p class="img-caption"><strong>그림 2.</strong> 서비스 수명 주기입니다. 왼쪽의 다이어그램은 +서비스가 {@link android.content.Context#startService +startService()}로 생성된 경우의 수명 주기를 나타내며 오른쪽의 다이어그램은 서비스가 +{@link android.content.Context#bindService bindService()}로 생성된 경우의 수명 주기를 나타낸 것입니다.</p> + +<p>이와 같은 메서드를 구현함으로써, 서비스 수명 주기의 두 가지 중첩된 루프를 모니터링할 수 있습니다. </p> + +<ul> +<li>서비스의 <strong>수명 주기 전체</strong>는 {@link +android.app.Service#onCreate onCreate()}가 호출된 시점과 {@link +android.app.Service#onDestroy}가 반환된 시점 사이에 일어납니다. 액티비티와 마찬가지로 서비스는 자신의 초기 설정을 +{@link android.app.Service#onCreate onCreate()}에서 수행하며 남은 리소스를 모두 {@link +android.app.Service#onDestroy onDestroy()}에 릴리스합니다. 예를 들어 +음악 재생 서비스의 경우 음악이 재생될 스레드를 {@link +android.app.Service#onCreate onCreate()}로 생성하고, 그럼 다음 해당 스레드를 중단할 때에는 {@link +android.app.Service#onDestroy onDestroy()}에서 할 수도 있습니다. + +<p>{@link android.app.Service#onCreate onCreate()}와 {@link android.app.Service#onDestroy +onDestroy()} 메서드는 모든 서비스에 대해 호출됩니다. 이는 서비스가 +{@link android.content.Context#startService startService()}로 생성되었든 {@link +android.content.Context#bindService bindService()}로 생성되었든 관계 없이 적용됩니다.</p></li> + +<li>서비스의 <strong>활성 수명 주기</strong>는 {@link +android.app.Service#onStartCommand onStartCommand()} 또는 {@link android.app.Service#onBind onBind()}로의 호출과 함께 시작됩니다. +각 메서드에 {@link +android.content.Intent}가 전달되는데 이것은 각각 {@link android.content.Context#startService +startService()} 또는 {@link android.content.Context#bindService bindService()} 중 하나에 전달된 것입니다. +<p>서비스가 시작되면 수명 주기 전체가 종료되는 것과 동시에 활성 수명 주기도 종료됩니다 +(서비스는 {@link android.app.Service#onStartCommand +onStartCommand()}가 반환된 뒤에도 여전히 활성 상태입니다). 서비스가 바인딩된 경우, 활성 수명 주기는 {@link +android.app.Service#onUnbind onUnbind()}가 반환되면 종료됩니다.</p> +</li> +</ul> + +<p class="note"><strong>참고:</strong> 시작된 서비스를 중단하려면 +{@link android.app.Service#stopSelf stopSelf()} 또는 {@link +android.content.Context#stopService stopService()}를 호출하면 되지만, 서비스에 대한 상응하는 콜백은 +없습니다(즉 {@code onStop()} 콜백이 없습니다). 그러므로, 서비스가 클라이언트에 바인딩되어 있지 않은 한 +시스템은 서비스가 중단되면 이를 소멸시킵니다. 수신되는 콜백은 {@link +android.app.Service#onDestroy onDestroy()}가 유일합니다.</p> + +<p>그림 2는 서비스에 대한 일반적인 콜백 메서드를 나타낸 것입니다. 이 그림에서는 +{@link android.content.Context#startService startService()}로 생성된 서비스와 +{@link android.content.Context#bindService bindService()}로 생성된 서비스를 +구분하고 있지만, 어떤 식으로 시작되었든 모든 서비스는 클라이언트가 자신에 바인딩되도록 허용할 수 있다는 점을 명심하십시오. +말하자면, {@link android.app.Service#onStartCommand +onStartCommand()}로 처음 시작된 서비스(클라이언트가 {@link android.content.Context#startService startService()}를 호출해서)라고 해도 +여전히 {@link android.app.Service#onBind onBind()}로의 호출을 받을 수 있습니다(클라이언트가 +{@link android.content.Context#bindService bindService()}를 호출하는 경우).</p> + +<p>바인딩을 제공하는 서비스 생성에 대한 자세한 내용은 <a href="{@docRoot}guide/components/bound-services.html">바인딩된 서비스</a> 문서를 참조하십시오. 이 안에는 {@link android.app.Service#onRebind onRebind()} +콜백 메서드에 대한 자세한 정보가 <a href="{@docRoot}guide/components/bound-services.html#Lifecycle">바인딩된 서비스의 +수명 주기 관리</a>에 관한 섹션에 +담겨 있습니다.</p> + + +<!-- +<h2>Beginner's Path</h2> + +<p>To learn how to query data from the system or other applications (such as contacts or media +stored on the device), continue with the <b><a +href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a></b> +document.</p> +--> diff --git a/docs/html-intl/intl/ko/guide/components/tasks-and-back-stack.jd b/docs/html-intl/intl/ko/guide/components/tasks-and-back-stack.jd new file mode 100644 index 000000000000..6b896f9d50ad --- /dev/null +++ b/docs/html-intl/intl/ko/guide/components/tasks-and-back-stack.jd @@ -0,0 +1,578 @@ +page.title=작업 및 백 스택 +parent.title=액티비티 +parent.link=activities.html +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + +<h2>이 문서의 내용</h2> +<ol> +<li><a href="#ActivityState">액티비티 상태 저장하기</a></li></li> +<li><a href="#ManagingTasks">작업 관리하기</a> + <ol> + <li><a href="#TaskLaunchModes">시작 모드 정의하기</a></li> + <li><a href="#Affinities">유사성 처리하기</a></li> + <li><a href="#Clearing">백 스택 지우기</a></li> + <li><a href="#Starting">작업 시작하기</a></li> + </ol> +</li> +</ol> + +<h2>글</h2> +<ol> + <li><a href="http://android-developers.blogspot.com/2010/04/multitasking-android-way.html"> +Android식 멀티태스킹</a></li> +</ol> + +<h2>참고 항목</h2> +<ol> + <li><a href="{@docRoot}design/patterns/navigation.html">Android 디자인: +탐색</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>} 매니페스트 +요소</a></li> + <li><a href="{@docRoot}guide/components/recents.html">개요 화면</a></li> +</ol> +</div> +</div> + + +<p>하나의 애플리케이션에는 보통 여러 개의 <a href="{@docRoot}guide/components/activities.html">액티비티</a>가 들어있습니다. 각 액티비티는 +사용자가 수행할 수 있는 특정한 종류의 작업을 중심으로 디자인되어야 하며 다른 액티비티를 +시작할 수 있는 기능이 있습니다. 예를 들어 이메일 애플리케이션에는 새 메시지 목록을 표시하는 하나의 액티비티가 있을 수 있습니다. +사용자가 메시지를 하나 선택하면, 새 액티비티가 열려 해당 메시지를 볼 수 있게 합니다.</p> + +<p>액티비티는 기기에서 다른 애플리케이션에 존재하는 액티비티를 시작할 수도 있습니다. 예를 들어 +애플리케이션이 이메일 메시지를 보내고자 하는 경우, "전송" 작업을 수행할 인텐트를 +정의하여 이메일 주소와 메시지 등의 몇 가지 데이터를 포함시키면 됩니다. 그러면 다른 애플리케이션에서 가져온 액티비티 중 +이러한 종류의 인텐트를 처리한다고 스스로 선언한 것이 열립니다. 이 경우, 이 인텐트는 +이메일을 전송하기 위한 것이므로 이메일 애플리케이션의 "작성" 액티비티가 시작됩니다(같은 인텐트를 +지원하는 액티비티가 여러 개 있는 경우, 시스템은 사용자에게 어느 것을 사용할지 선택하도록 합니다). 이메일이 전송되면 +액티비티가 재개되고 해당 이메일 액티비티가 애플리케이션의 일부였던 것처럼 보입니다. 액티비티는 +서로 다른 애플리케이션에서 온 것일 수 있지만, Android는 두 액티비티를 +모두 같은 <em>작업</em> 안에 유지하여 이처럼 막힘 없는 사용자 환경을 유지합니다.</p> + +<p>작업이란 액티비티 컬렉션을 일컫는 말로, 사용자가 특정 작업을 수행할 때 이것과 +상호 작용합니다. 액티비티는 스택 안에 정렬되며(<em>백 스택</em>), 이때 +순서는 각 액티비티가 열린 순서와 같습니다.</p> + +<!-- SAVE FOR WHEN THE FRAGMENT DOC IS ADDED +<div class="sidebox-wrapper"> +<div class="sidebox"> +<h3>Adding fragments to a task's back stack</h3> + +<p>Your activity can also include {@link android.app.Fragment}s to the back stack. For example, +suppose you have a two-pane layout using fragments, one of which is a list view (fragment A) and the +other being a layout to display an item from the list (fragment B). When the user selects an item +from the list, fragment B is replaced by a new fragment (fragment C). In this case, it might be +desireable for the user to navigate back to reveal fragment B, using the <em>Back</em> button.</p> +<p>In order to add fragment B to the back stack so that this is possible, you must call {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()} before you {@link +android.app.FragmentTransaction#commit()} the transaction that replaces fragment B with fragment +C.</p> +<p>For more information about using fragments and adding them to the back stack, see the {@link +android.app.Fragment} class documentation.</p> + +</div> +</div> +--> + +<p>기기 메인 스크린이 대다수 작업의 시작 지점입니다. 사용자가 +애플리케이션 +시작 관리자에 있는 아이콘(또는 메인 스크린의 바로 가기)을 터치하면 해당 애플리케이션의 작업이 전경으로 나옵니다. 해당 애플리케이션에 대한 +작업이 존재하지 않으면(이 애플리케이션을 최근에 사용한 적이 없는 경우), 새 작업이 생성되고 +해당 애플리케이션의 "기본" 액티비티가 스택에 있는 루트 액티비티로 열립니다.</p> + +<p>현재 액티비티가 또 다른 액티비티를 시작하는 경우, 새 액티비티가 스택의 맨 위로 밀어올려지고 +사용자의 초점이 이에 맞춰집니다. 이전 액티비티는 스택에 유지되지만, 중단됩니다. 액티비티가 중단되면 +시스템은 이 액티비티의 사용자 인터페이스의 현재 상태를 보존합니다. 사용자가 +<em>뒤로</em> + 버튼을 누르면, 현재 액티비티가 스택의 맨 위에서 튀어나오고(해당 액티비티는 소멸됩니다) +이전 액티비티가 재개됩니다(이것의 UI 이전 상태가 복원됩니다). 스택에 있는 액티비티는 +결코 다시 정렬되지 않습니다. 다만 스택에서 밀어올려지거나 튀어나올 뿐입니다. 즉, 현재 액티비티에 의해 +시작되면 스택 위로 밀어올려지고, 사용자가 <em>뒤로</em> 버튼을 사용하여 액티비티를 떠나면 튀어나와 사라지는 것입니다. 따라서, +백 스택은 +일종의 "후입선출" 객체 구조로서 작동한다고 할 수 있습니다. 그림 1은 +이 행동을 시간 표시 막대와 함께 표시하여 여러 액티비티 사이의 진행률을 보여주며, +각 시점에서 현재 백 스택의 모습을 나타낸 것입니다.</p> + +<img src="{@docRoot}images/fundamentals/diagram_backstack.png" alt="" /> +<p class="img-caption"><strong>그림 1.</strong> 작업에 있는 각각의 새 액티비티가 백 스택에 항목을 추가하는 +방법을 나타낸 것입니다. 사용자가 <em>뒤로</em> 버튼을 누르면 현재 +액티비티가 +소멸되고 이전 액티비티가 재개됩니다.</p> + + +<p>사용자가 계속해서 <em>뒤로</em> 버튼을 누르면, 스택에 있는 각 액티비티가 하나씩 튀어나가 +이전 것을 +드러내고, 마침내는 사용자가 메인 스크린으로 되돌아가게 됩니다(아니면 작업이 시작되었을 때 +실행 중이던 액티비티가 무엇이든 그것으로 되돌아갑니다). 스택에서 모든 액티비티가 제거되면 이 작업은 더 이상 존재하지 않게 됩니다.</p> + +<div class="figure" style="width:287px"> +<img src="{@docRoot}images/fundamentals/diagram_multitasking.png" alt="" /> <p +class="img-caption"><strong>그림 2.</strong> 두 개의 작업: 작업 B가 전경에서 사용자 상호 작용을 수신하는 한편, +작업 A는 배경에서 재개되기를 기다립니다.</p> +</div> +<div class="figure" style="width:215px"> + <img src="{@docRoot}images/fundamentals/diagram_multiple_instances.png" alt="" /> <p +class="img-caption"><strong>그림 3.</strong> 하나의 액티비티가 여러 번 인스턴트화됩니다.</p> +</div> + +<p>작업이란 하나의 잘 짜여진 단위로 사용자가 새 작업을 시작할 때 "배경"으로 이동할 수도 있고 +<em>홈</em> 버튼을 통해 메인 스크린으로 이동할 수도 있습니다. 작업의 모든 액티비티는 배경에 있는 동안은 +중단되지만 +, 해당 작업에 대한 백 스택은 그대로 변함 없이 유지됩니다. 이 작업은 또 다른 작업이 발생하는 동안 +초점을 잃을 뿐입니다(그림 2 참조). 그런 다음 작업이 "전경"으로 되돌아와 사용자가 +이전에 하던 일을 계속할 수 있습니다. 예를 들어 현재 작업(작업 A)의 스택에 세 개의 액티비티가 있다고 +가정하면 그 중 둘은 현재 액티비티 아래에 있습니다. 사용자가 <em>홈</em> + 버튼을 누른 다음 +애플리케이션 시작 관리자로부터 새 애플리케이션을 시작합니다. 메인 스크린이 나타나면 작업 A는 +배경으로 이동합니다. 새 애플리케이션이 시작되면 시스템은 해당 애플리케이션에 대한 작업을 시작하며 +(작업 B) 여기에는 나름의 액티비티 스택이 딸려 있습니다. 해당 애플리케이션과 +상호 작용한 후, 사용자는 다시 홈으로 돌아와 원래 작업 A를 시작한 +애플리케이션을 선택합니다. 이제 작업 A가 전경으로 옵니다. +이 스택에 있는 액티비티 세 개는 모두 멀쩡하고, 스택 맨 위에 있는 액티비티가 +재개됩니다. 이 시점에서 +사용자는 작업 B로 도로 전환할 수도 있습니다. 홈으로 이동하여 해당 작업을 +시작한 애플리케이션 아이콘을 선택하면 됩니다(아니면 +<a href="{@docRoot}guide/components/recents.html">개요 화면</a>에서 해당 앱의 작업을 선택해도 됩니다). +이것이 Android에서 멀티태스킹을 하는 작업의 예시입니다.</p> + +<p class="note"><strong>참고:</strong> 여러 개의 작업을 배경에 한꺼번에 대기시킬 수 있습니다. +하지만, 사용자가 수많은 배경 작업을 동시에 실행하면 시스템이 메모리를 복원하기 위해 +배경 액티비티를 소멸시키기 시작할 수 있고, 그러면 액티비티 상태가 손실됩니다. +다음의 <a href="#ActivityState">액티비티 상태</a>에 관한 섹션을 참조하십시오.</p> + +<p>백 스택에 있는 액티비티는 결코 다시 정렬되지 않으므로, 애플리케이션에서 +사용자에게 하나 이상의 액티비티로부터 특정 액티비티를 시작하도록 허용하는 경우, 해당 액티비티의 새 인스턴스가 +생성되어 스택 위로 밀려옵니다(해당 액티비티의 기존 인스턴스를 +맨 위로 가져오는 대신). 따라서, 애플리케이션 안의 한 액티비티가 여러 번 +인스턴트화될 수 있으며(서로 다른 작업으로부터도 가능), 이를 나타낸 것이 그림 3입니다. 이 때문에 사용자가 +<em>뒤로</em> 버튼을 사용하여 뒤로 이동하는 경우, 액티비티의 각 인스턴스가 열린 순서대로 드러납니다 +(각자 나름의 +UI 상태를 가지고). 다만, 액티비티가 한 번 이상 인스턴트화되는 것을 원치 않으면 이 행동은 수정할 수 +있습니다. 그 방법에 대해서는 <a href="#ManagingTasks">작업 관리하기</a>에 관한 이후 섹션에서 이야기합니다.</p> + + +<p>액티비티 및 작업에 대한 기본 행동을 요약하려면 다음과 같이 합니다.</p> + +<ul> + <li>액티비티 A가 액티비티 B를 시작하면 액티비티 A는 중단되지만, 시스템이 그 상태를 +(예: 스크롤 위치 및 양식에 입력된 텍스트 등) 보존합니다. +사용자가 액티비티 B에 있는 동안 <em>뒤로</em> 버튼을 누르면 액티비티 A가 재개되며 상태도 +복원됩니다.</li> + <li>사용자가 <em>홈</em> 버튼을 눌러 작업을 떠나면 현재 액티비티가 +중단되고 +그 소속 작업이 배경으로 들어갑니다. 시스템은 작업에 속한 모든 액티비티의 상태를 보존합니다. 사용자가 +나중에 작업을 시작한 시작 관리자 아이콘을 선택하여 해당 작업을 재개하면, 그 작업이 +전경으로 나오고 스택 맨 위에서 액티비티를 재개합니다.</li> + <li>사용자가 <em>뒤로</em> 버튼을 누르면, 현재 액티비티가 스택에서 튀어나오고 +소멸됩니다. + 스택에 있던 이전 액티비티가 재개됩니다. 액티비티가 소멸되면, 시스템은 그 액티비티의 상태를 +보존하지 <em>않습니다.</em></li> + <li>액티비티는 여러 번 인스턴트화할 수 있으며, 다른 작업에서도 이를 수행할 수 있습니다.</li> +</ul> + + +<div class="note design"> +<p><strong>탐색 디자인</strong></p> + <p>Android에서 앱 탐색의 작동 원리를 자세히 알아보려면, Android 디자인의 <a href="{@docRoot}design/patterns/navigation.html">탐색</a> 가이드를 읽어보십시오.</p> +</div> + + +<h2 id="ActivityState">액티비티 상태 저장하기</h2> + +<p>위에서 논한 바와 같이, 시스템의 기본 행동은 액티비티가 중단되면 그 상태를 보존해두는 +것입니다. 이렇게 하면, 사용자가 이전 액티비티로 도로 이동했을 때 그에 속한 사용자 인터페이스가 이전 상태 +그대로 표시됩니다. 그러나 액티비티의 상태를 미리 보존할 수도 있으며 사전에 이렇게 <strong>해야 합니다.</strong> +이때에는, 액티비티가 소멸되고 다시 만들어야 하는 경우를 대비해 +콜백 메서드를 사용합니다.</p> + +<p>시스템이 액티비티 중 하나를 중단시키는 경우(예를 들어 새 액티비티가 시작되었을 때 또는 작업이 +배경으로 이동하는 경우), 시스템은 시스템 메모리를 회복해야 하는 경우 액티비티를 +완전히 소멸시켜버릴 수도 있습니다. 이런 상황이 벌어지면, 액티비티 상태에 대한 정보는 손실됩니다. 이런 일이 벌어지더라도, +시스템은 여전히 +백 스택에 해당 액티비티의 자리가 있다는 것을 알고 있습니다. 다만 액티비티가 스택 맨 위로 올라오면 +시스템이 이를 (재개하는 것이 아니라) 재생성해야만 합니다. 사용자의 작업 내용을 +잃어버리는 불상사를 피하려면 그 내용을 미리 보존해두어야 합니다. 이때 액티비티의 +{@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} 콜백 +메서드를 구현하는 방법을 씁니다.</p> + +<p>액티비티 상태를 저장하는 방법에 대한 자세한 정보는 <a href="{@docRoot}guide/components/activities.html#SavingActivityState">액티비티</a> +문서를 참조하십시오.</p> + + + +<h2 id="ManagingTasks">작업 관리하기</h2> + +<p>Android가 작업과 백 스택을 관리하는 방식은 위에 설명된 바와 같고—같은 작업 안에서 +연이어 시작된 모든 작업을 한곳에 배치하되 "후입선출" 스택에 두는 것—이 방식은 +대부분의 애플리케이션에 아주 효과적입니다. 여러분은 액티비티가 작업과 연관된 방식이나 +백 스택에서의 존재 방식에 대해 염려하지 않아도 됩니다. 그러나, 정상적인 동작을 인터럽트하기로 결정할 수도 +있습니다. 애플리케이션의 액티비티 하나가 시작되면 새 작업을 시작하려 +할 수도 있습니다(현재 작업 내에 배치되는 것 대신에). 아니면, 액티비티를 시작하면 그것의 +기존 인스턴스 하나를 앞으로 가져오고자 할 수도 있습니다(백 스택 맨 위에서 새 인스턴스를 +생성하는 것 대신에). 또는 백 스택에서 사용자가 작업을 떠날 때의 루트 액티비티를 제외하고 +모든 액티비티를 지우고자 할 수도 있습니다.</p> + +<p>이 모든 것과 그 외에도 많은 것을 할 수 있는 것이 바로 +<a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> +매니페스트 요소 안에 있는 속성과, +{@link android.app.Activity#startActivity startActivity()}에 전달한 인텐트에 있는 플래그입니다.</p> + +<p>이런 면에서, 여러분이 사용할 수 있는 주요 <a href="{@docRoot}guide/topics/manifest/activity-element.html"> +{@code <activity>}</a> 속성은 다음과 같습니다.</p> + +<ul class="nolist"> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#aff"> + {@code taskAffinity}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode"> + {@code launchMode}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#reparent"> + {@code allowTaskReparenting}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#clear"> + {@code clearTaskOnLaunch}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#always"> + {@code alwaysRetainTaskState}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#finish"> + {@code finishOnTaskLaunch}</a></li> +</ul> + +<p>그리고 다음은 여러분이 사용할 수 있는 주요 인텐트 플래그입니다.</p> + +<ul class="nolist"> + <li>{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}</li> + <li>{@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}</li> + <li>{@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}</li> +</ul> + +<p>다음 섹션에서는 이와 같은 매니페스트 속성과 인텐트 플래그를 사용하여 +액티비티가 작업과 연관되는 방식을 정의하고 백 스택에서 액티비티가 동작하는 방식을 정의하는 방법을 배우게 됩니다.</p> + +<p>이외에도 별도로 작업과 액티비티를 표시하는 방법에 대한 고려 사항과 +개요 화면에서의 관리 방법을 논합니다. 자세한 정보는 <a href="{@docRoot}guide/components/recents.html">개요 화면</a>을 +참조하십시오. 보통은 개요 화면에 작업과 액티비티가 어떻게 표현될지는 +시스템이 정의하도록 두어야 합니다. 이 동작을 개발자가 수정할 필요도 없습니다.</p> + +<p class="caution"><strong>주의:</strong> 대부분의 애플리케이션은 액티비티와 작업에 대한 +기본 동작을 인터럽트하지 않는 것이 정상입니다. 액티비티가 기본 동작을 수정하는 것이 필요하다는 +판단이 서면, 시작 과정 중에 액티비티의 유용성을 테스트하십시오. +또한 다른 액티비티와 작업에서 <em>뒤로</em> 버튼을 써서 해당 액티비티로 돌아올 때에도 유용성을 테스트해야 합니다. +사용자의 예상되는 동작과 충돌할 가능성이 있는 탐색 동작을 꼭 테스트하십시오.</p> + + +<h3 id="TaskLaunchModes">시작 모드 정의하기</h3> + +<p>시작 모드를 사용하면 액티비티의 새 인스턴스가 현재 작업과 연관된 방식을 정의할 수 있게 +해줍니다. 여러 가지 시작 모드를 두 가지 방식으로 정의할 수 있습니다.</p> +<ul class="nolist"> + <li><a href="#ManifestForTasks">매니페스트 파일 사용하기</a> + <p>매니페스트 파일에서 액티비티를 선언하는 경우, 액티비티가 시작될 때 여러 작업과 어떤 식으로 +연관을 맺어야 하는지 지정할 수 있습니다.</li> + <li><a href="#IntentFlagsForTasks">인텐트 플래그 사용하기</a> + <p>{@link android.app.Activity#startActivity startActivity()}를 호출하는 경우 +{@link android.content.Intent}에 플래그를 포함시켜 새 액티비티가 현재 작업과 어떻게 연관되어야 할지(또는 +애초에 연관을 맺을지 아닐지) 선언하도록 할 수 있습니다.</p></li> +</ul> + +<p>따라서, 액티비티 A가 액티비티 B를 시작하면 액티비티 B는 자신의 매니페스트에서 +현재 작업과 연관을 맺는 데 적당한 방식(연관을 맺어야 한다면)을 정의할 수 있고 액티비티 A 또한 +액티비티 B가 현재 작업과 연관을 맺는 방식을 요청할 수 있습니다. 두 액티비티가 모두 액티비티 B가 작업과 +연관되는 방식을 정의하는 경우, 액티비티 A의 요청(인텐트에 정의된 바를 따름)을 액티비티 B의 +요청(자신의 매니페스트에서 정의)보다 우위로 인식합니다.</p> + +<p class="note"><strong>참고:</strong> 매니페스트 파일에 사용할 수 있는 시작 모드 중에는 +인텐트의 플래그로 사용할 수는 없는 것도 있으며, 이와 마찬 가지로 인텐트의 플래그로 사용할 수 있는 시작 모드 중에는 +매니페스트에서 정의할 수 없는 것도 있습니다.</p> + + +<h4 id="ManifestForTasks">매니페스트 파일 사용하기</h4> + +<p>매니페스트 파일에서 액티비티를 선언하는 경우, 액티비티가 작업과 +어떤 식으로 연관되어야 할지 지정하려면 <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> +요소의 <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code +launchMode}</a> 속성을 사용하면 됩니다.</p> + +<p><a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code +launchMode}</a> 속성은 액티비티가 작업 안으로 들어가며 시작되는 방법에 대한 지침을 +나타냅니다. +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">launchMode</a></code> +속성에 할당할 수 있는 시작 모드는 네 가지가 있습니다.</p> + +<dl> +<dt>{@code "standard"} (기본 모드)</dt> + <dd>기본입니다. 시스템이 액티비티가 시작된 작업에서 액티비티의 새 인스턴스를 만들고 +인텐트의 경로를 이것으로 지정합니다. 액티비티는 여러 번 인스턴트화될 수 있고, +각 인스턴스는 서로 다른 작업에 속할 수 있으며 한 작업에 여러 개의 인스턴스가 있을 수 있습니다.</dd> +<dt>{@code "singleTop"}</dt> + <dd>액티비티의 인스턴스가 이미 현재 작업의 맨 위에 존재하는 경우, 시스템은 인텐트의 경로를 +해당 인스턴스로 지정합니다. 이때 액티비티의 새 인스턴스를 만들기보다는 해당 인스턴스의 {@link +android.app.Activity#onNewIntent onNewIntent()} 메서드를 호출하는 방법을 +통합니다. 액티비티는 여러 번 인스턴트화될 수 있고, 각 인스턴스는 서로 다른 작업에 +속할 수 있으며 한 작업에 여러 개의 인스턴스가 있을 수 있습니다(다만 백 스택의 맨 위에 있는 +액티비티가 액티비티의 기존 인스턴스가 <em>아닌</em> 경우에만 이것이 적용됩니다). + <p>예를 들어 어느 작업의 백 스택이 루트 액티비티 A와 액티비티 B, C, 그리고 맨 위의 액티비티 D로 +구성되어 있다고 가정합니다(이 스택은 A-B-C-D 형태를 띠며 D가 맨 위에 있습니다). 유형 D의 액티비티에 대한 인텐트가 도착합니다. +D에 기본 {@code "standard"} 시작 모드가 있는 경우, 클래스의 새 인스턴스가 시작되고 이 스택은 +A-B-C-D-D가 됩니다. 하지만, D의 시작 모드가 {@code "singleTop"}인 경우, D의 +기존 인스턴스가 해당 인텐트를 {@link +android.app.Activity#onNewIntent onNewIntent()}를 통해 받게 됩니다. 이것이 스택의 맨 위에 있기 때문입니다. 스택은 +계속 A-B-C-D로 유지됩니다. 그러나 유형 B의 액티비티에 대한 인텐트가 도착하는 경우, +B의 새 인스턴스가 스택에 추가되며 이는 액티비티의 시작 모드가 {@code "singleTop"}이더라도 무관하게 적용됩니다.</p> + <p class="note"><strong>참고:</strong> 어느 액티비티의 새 인스턴스가 생성되면, +사용자가 <em>뒤로</em> 버튼을 눌러 이전 액티비티로 되돌아갈 수 있게 됩니다. 그러나 액티비티의 기존 +인스턴스가 +새 인텐트를 처리하는 경우, 사용자가 <em>뒤로</em> 버튼을 눌러도 새 인텐트가 {@link android.app.Activity#onNewIntent +onNewIntent()}에 도착하기 전의 액티비티 +상태로 +되돌아갈 수 없습니다.</p> +</dd> + +<dt>{@code "singleTask"}</dt> + <dd>시스템이 새 작업을 만들고 새 작업의 루트에 있는 액티비티를 인스턴트화합니다. +하지만, 액티비티의 인스턴스가 이미 별개의 작업에 존재하는 경우, 시스템은 인텐트의 경로를 +기존 인스턴스로 지정합니다. 이때 새 인스턴스를 만들기보다 해당 인스턴스의 {@link +android.app.Activity#onNewIntent onNewIntent()} 메서드를 호출하는 방법을 통합니다. 한 번에 +액티비티 인스턴스 한 개씩만 존재할 수 있습니다. + <p class="note"><strong>참고:</strong> 액티비티가 새 작업에서 시작되더라도, +<em>뒤로</em> 버튼을 누르면 여전히 사용자를 이전 액티비티로 돌려보냅니다.</p></dd> +<dt>{@code "singleInstance"}.</dt> + <dd>{@code "singleTask"}와 같습니다. 다만 시스템이 인스턴스를 보유하고 있는 작업 안으로 +다른 어떤 액티비티도 시작하지 않는다는 것은 예외입니다. 액티비티는 언제나 자신의 작업의 유일무이한 구성원입니다. +이것으로 시작한 액티비티는 모두 별개의 작업에서 열립니다.</dd> +</dl> + + +<p>또 다른 예로 Android 브라우저 애플리케이션이 있습니다. 이것은 웹 브라우저 액티비티가 항상 +자신만의 작업에서 열려야 한다고 선언합니다. 이때 <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> 요소에 {@code singleTask} 시작 모드를 지정하는 방법을 씁니다. +다시 말해 애플리케이션이 Android 브라우저를 열라는 인텐트를 발행하면 +브라우저의 액티비티가 애플리케이션과 같은 작업에 배치되지 <em>않는다</em>는 +뜻입니다. 그 대신, 브라우저에 대한 새 작업이 시작되거나, 브라우저에 이미 +배경에서 실행 중인 작업이 있는 경우 해당 작업이 전경으로 불려나와 새 인텐트를 처리하게 +됩니다.</p> + +<p>액티비티가 새 작업에서 시작되었든 액티비티를 시작한 것과 같은 작업에서 시작되었든 관계 없이 +<em>뒤로</em> 버튼을 사용하면 언제나 사용자를 이전 액티비티로 돌려보냅니다. 다만, +{@code singleTask} 시작 모드를 나타내는 액티비티를 시작한 다음 해당 +액티비티의 인스턴스가 이미 배경 작업에 존재하는 경우, 그 작업 전체가 전경에 불려나옵니다. 이 시점에서 +백 스택에는 이제 앞으로 가져온 작업에서 가져온 모든 액티비티가 포함되어 있으며, 이는 스택의 +맨 위에 위치합니다. 그림 4는 이와 같은 유형의 시나리오를 나타낸 것입니다.</p> + +<img src="{@docRoot}images/fundamentals/diagram_backstack_singletask_multiactivity.png" alt="" /> +<p class="img-caption"><strong>그림 4.</strong> 시작 모드가 "singleTask"인 액티비티가 +백 스택에 추가되는 방법을 표현한 것입니다. 이 액티비티가 이미 자신의 백 스택을 가지고 있는 +배경 작업의 일부인 경우, 해당 백 스택도 모두 전경으로 +불려나오며, 이는 현재 작업 위에 배치됩니다.</p> + +<p>매니페스트 파일에서 시작 모드를 사용하는 것에 대한 자세한 정보는 +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> +요소 문서를 참조하십시오. 여기에서 {@code launchMode} 속성과 허용된 값을 더 자세히 +논합니다.</p> + +<p class="note"><strong>참고:</strong> 액티비티에 대하여 <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a> 속성으로 지정한 동작을 +재정의하려면 액티비티를 시작한 인텐트에 포함된 플래그를 사용하면 됩니다. 이 내용은 +다음 섹션에서 논합니다.</p> + + + +<h4 id="#IntentFlagsForTasks">인텐트 플래그 사용하기</h4> + +<p>액티비티를 시작할 때면, 액티비티가 자신의 작업과 연관되는 기본 방식을 수정할 수 있습니다. +{@link +android.app.Activity#startActivity startActivity()}에 전달한 인텐트 안에 있는 플래그를 포함시키면 됩니다. 기본 동작을 수정하는 데 사용할 수 있는 +플래그는 다음과 같습니다.</p> + +<p> + <dt>{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}</dt> + <dd>액티비티를 새 작업에서 시작합니다. 지금 시작하고 있는 액티비티에 대해 이미 실행 중인 작업이 있으면, +해당 작업의 마지막 상태를 복원하여 전경으로 불려나오고 액티비티는 새 인텐트를 +{@link android.app.Activity#onNewIntent onNewIntent()}에서 수신합니다. + <p>이렇게 하면 {@code "singleTask"} <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a> 값에서와 같은 동작을 발생시키며, +이는 이전 섹션에서 논한 것과 같습니다.</p></dd> + <dt>{@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}</dt> + <dd>시작되고 있는 액티비티가 현재 액티비티인 경우(백 스택 맨 위에 있는), 해당 액티비티의 새 인스턴스를 생성하는 대신 기존 +인스턴스가 {@link android.app.Activity#onNewIntent onNewIntent()}에 +대한 호출을 받습니다. + <p>이렇게 하면 {@code "singleTop"} <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a> 값에서와 같은 동작을 발생시키며, +이는 이전 섹션에서 논한 것과 같습니다.</p></dd> + <dt>{@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}</dt> + <dd>시작되고 있는 액티비티가 이미 현재 작업에서 실행 중인 경우, 해당 액티비티의 +새 인스턴스를 시작하는 대신 그 위에 있는 모든 다른 액티비티가 +소멸되고 이 인텐트는 해당 액티비티(이제 맨 위로 올라옴)의 재개된 인스턴스로, +{@link android.app.Activity#onNewIntent onNewIntent()}를 통해 전달됩니다. + <p>이 동작을 발생시키는 <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a> +속성에 대한 값은 없습니다.</p> + <p>{@code FLAG_ACTIVITY_CLEAR_TOP}는 +{@code FLAG_ACTIVITY_NEW_TASK}와 함께 쓰이는 경우가 가장 보편적입니다. +이들 플래그를 함께 사용하면 다른 작업에 있는 기존 액티비티의 위치를 +찾아 이를 인텐트에 응답할 수 있는 위치에 놓을 한 가지 방편이 됩니다. </p> + <p class="note"><strong>참고:</strong> 지정된 액티비티의 시작 모드가 +{@code "standard"}인 경우, +이것 또한 스택에서 제거되고 그 자리에 새 인스턴스가 대신 생성되어 수신되는 인텐트를 +처리하게 됩니다. 이는 시작 모드가 +{@code "standard"}인 경우, 새 인텐트에 대해서는 항상 새 인스턴스가 생성되기 때문입니다. </p> +</dd> +</dl> + + + + + +<h3 id="Affinities">유사성 처리하기</h3> + +<p><em>유사성</em>이란 액티비티가 어느 작업에 소속되기를 선호하는지를 나타내는 것입니다. 기본적으로, +같은 애플리케이션에서 나온 액티비티는 서로 유사성을 지니고 있습니다. 따라서, 기본적으로 +같은 애플리케이션 안에 있는 모든 액티비티는 같은 작업 안에 있는 것을 선호합니다. 하지만 액티비티에 대한 기본 유사성은 개발자가 +수정할 수 있습니다. 각기 다른 애플리케이션에서 정의된 +액티비티가 하나의 유사성을 공유할 수도 있고, 같은 애플리케이션에서 정의된 여러 액티비티에 +서로 다른 작업 유사성을 할당할 수도 있습니다.</p> + +<p>어느 액티비티라도 <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> +요소의 <a href="{@docRoot}guide/topics/manifest/activity-element.html#aff">{@code taskAffinity}</a> 속성을 +사용하여 유사성을 수정할 수 있습니다.</p> + +<p><a href="{@docRoot}guide/topics/manifest/activity-element.html#aff">{@code taskAffinity}</a> +속성은 문자열 값을 취합니다. 이는 +<a href="{@docRoot}guide/topics/manifest/manifest-element.html"> +{@code <manifest>} +</a> 요소에서 선언한 기본 패키지 이름과 달리 고유해야 합니다. 왜냐하면 시스템이 이 이름을 사용하여 애플리케이션의 기본 작업 유사성을 +식별하기 때문입니다.</p> + +<p>유사성이 역할을 갖는 것은 다음과 같은 두 가지 상황에서입니다.</p> +<ul> + <li>액티비티를 시작한 인텐트에 +{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} + 플래그가 들어 있는 경우. + +<p>새로운 액티비티는 기본적으로 +{@link android.app.Activity#startActivity startActivity()}를 호출한 액티비티의 작업 안으로 들어가며 시작됩니다. 이것은 발신자와 같은 +백 스택 위로 밀어내집니다. 하지만 +{@link android.app.Activity#startActivity startActivity()}에 +전달된 인텐트에 {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} + 플래그가 들어있는 경우, 시스템은 새 액티비티를 담을 다른 작업을 찾습니다. 이는 새 작업인 경우가 많습니다. +그렇지만 꼭 그래야 하는 것은 아닙니다. 새 액티비티와 같은 유사성을 가진 기존 작업이 이미 존재하는 경우, +해당 액티비티는 그 작업 안으로 들어가며 시작됩니다. 그렇지 않으면, 새 작업을 시작합니다.</p> + +<p>이 플래그 때문에 액티비티가 새 작업을 시작하게 되고 사용자가 <em>홈</em> 버튼을 눌러 이 액티비티를 +떠나고자 +하는 경우, 사용자가 작업으로 도로 이동할 방법이 있어야 합니다. 엔티티 중에는(예를 들어 +알림 관리자) 액티비티를 항상 외부 작업으로만 시작하고 자신의 일부로서는 절대 시작하지 않는 것이 있습니다. +따라서 이들은 {@code FLAG_ACTIVITY_NEW_TASK}를 +{@link android.app.Activity#startActivity startActivity()}에 전달하는 인텐트에 포함시킵니다. +이 플래그를 사용할 수 있는 외부 엔티티가 +호출할 수 있는 액티비티를 가지고 있는 경우, 사용자가 시작된 작업에 돌아갈 수 있는 +방법을 따로 가지고 있어야 합니다. 예를 들어 시작 관리자 아이콘을 이용한다든지 하는 방법입니다(작업의 루트 액티비티에 +{@link android.content.Intent#CATEGORY_LAUNCHER} 인텐트 필터가 있습니다. 아래의 <a href="#Starting">작업 시작하기</a> 섹션을 참조하십시오).</p> +</li> + + <li>액티비티의 <a href="{@docRoot}guide/topics/manifest/activity-element.html#reparent"> +{@code allowTaskReparenting}</a> 속성이 {@code "true"}로 설정된 경우. + <p>이 경우, 액티비티는 자신이 시작한 작업에서 벗어나 유사성을 가진 다른 작업이 전경으로 +나오면 그 작업으로 이동할 수 있습니다.</p> + <p>예를 들어 선택한 몇몇 도시에서 기상 상태를 예보하는 어느 액티비티가 +여행 애플리케이션의 일부로 정의되어 있다고 가정합니다. 이것은 같은 애플리케이션에 있는 +다른 여러 액티비티와 같은 유사성을 가지며(기본 애플리케이션 유사성) 이 속성으로 상위 재지정을 허용하기도 합니다. +액티비티 중 하나가 일기 예보 액티비티를 시작하면, 이는 처음에는 액티비티와 같은 작업에 +속합니다. 하지만 여행 애플리케이션의 작업이 전경으로 불려나오면 +일기 예보 액티비티는 그 작업에 다시 할당되며 그 안에 표시됩니다.</p> +</li> +</ul> + +<p class="note"><strong>팁:</strong> {@code .apk} 파일에 사용자 쪽에서 보기에 하나 이상의 "애플리케이션"이 +들어있는 경우, <a href="{@docRoot}guide/topics/manifest/activity-element.html#aff">{@code taskAffinity}</a> +속성을 사용하여 각 "애플리케이션"과 연관된 액티비티에 서로 다른 유사성을 할당하는 것이 좋습니다.</p> + + + +<h3 id="Clearing">백 스택 지우기</h3> + +<p>사용자가 작업을 오랜 시간 동안 떠나 있으면, 시스템이 루트 액티비티만 빼고 모든 액티비티를 +해당 작업에서 지웁니다. 사용자가 다시 작업으로 돌아오면, 루트 액티비티만 복원됩니다. +시스템이 이런 식으로 동작하는 것은 오랜 시간이 지난 다음에는 사용자가 전에 하던 일을 중단하고 +새로운 일을 시작하기 위해 작업에 돌아올 가능성이 크기 때문입니다. </p> + +<p>이 동작을 수정하는 데 사용할 수 있는 액티비티 속성이 몇 가지 있습니다. </p> + +<dl> +<dt><code><a +href="{@docRoot}guide/topics/manifest/activity-element.html#always">alwaysRetainTaskState</a></code> +</dt> +<dd>이 속성이 작업의 루트 액티비티 안에서 {@code "true"}로 설정되어 있는 경우, +방금 설명한 기본 동작이 일어나지 않습니다. +작업은 오랜 시간이 지난 뒤에도 자신의 스택에 있는 모든 액티비티를 유지합니다.</dd> + +<dt><code><a +href="{@docRoot}guide/topics/manifest/activity-element.html#clear">clearTaskOnLaunch</a></code></dt> +<dd>이 속성이 작업의 루트 액티비티 안에서 {@code "true"}로 설정되어 있는 경우, +사용자가 작업을 떠났다가 다시 돌아올 때마다 스택을 루트 액티비티까지 +지웁니다. 바꿔 말하면, 이것은 +<a href="{@docRoot}guide/topics/manifest/activity-element.html#always"> +{@code alwaysRetainTaskState}</a>와 정반대입니다. 사용자는 항상 작업의 초기 상태로 돌아오게 되며, +이는 아주 잠깐 동안만 작업을 떠난 경우에도 마찬가지입니다.</dd> + +<dt><code><a +href="{@docRoot}guide/topics/manifest/activity-element.html#finish">finishOnTaskLaunch</a></code> +</dt> +<dd>이 속성은 <a href="{@docRoot}guide/topics/manifest/activity-element.html#clear">{@code clearTaskOnLaunch}</a>와 같지만, +작업 전체가 아니라 +하나의 액티비티에서 작동합니다. 이것은 루트 액티비티를 포함한 모든 액티비티가 없어지게 +하기도 합니다. 이것을 {@code "true"}로 설정하면, +액티비티는 현재 세션에 대해서만 작업의 일부로 유지됩니다. 사용자가 작업을 떠났다가 +다시 돌아오면 이 작업은 더 이상 존재하지 않습니다.</dd> +</dl> + + + + +<h3 id="Starting">작업 시작하기</h3> + +<p>액티비티를 작업의 진입 지점으로 설정하려면 여기에 작업에서 지정한 대로 +{@code "android.intent.action.MAIN"}이 있는 인텐트 필터를 부여하고 +{@code "android.intent.category.LAUNCHER"}를 +지정된 카테고리로 설정하면 됩니다. 예:</p> + +<pre> +<activity ... > + <intent-filter ... > + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + ... +</activity> +</pre> + +<p>이런 종류의 인텐트 필터를 사용하면 액티비티에 대한 아이콘과 레이블이 +애플리케이션 시작 관리자에 표시되어 사용자에게 액티비티를 시작할 방법을 부여하며, +액티비티를 시작하고 나면 이것이 생성한 작업에 언제든 돌아올 수 있게 됩니다. +</p> + +<p>이 두 번째 능력이 중요합니다. 사용자는 작업을 떠났다가 이 액티비티 시작 관리자를 사용하여 나중에 작업에 +돌아올 수 있어야 합니다. 이러한 이유로, 액티비티가 항상 작업을 시작하는 것으로 표시하는 <a href="#LaunchModes">시작 +모드</a> 두 가지, 즉 {@code "singleTask"}와 +{@code "singleInstance"}는 액티비티에 +{@link android.content.Intent#ACTION_MAIN} + 및 {@link android.content.Intent#CATEGORY_LAUNCHER} 필터가 있을 때에만 사용해야 합니다. 예를 들어 필터가 누락되면 다음과 같은 일이 +발생합니다. 어느 인텐트가 {@code "singleTask"} 액티비티를 시작하여 새 작업을 시작하고, +사용자가 이 작업에서 일하며 어느 정도 시간을 보냅니다. 그런 다음 사용자가 <em>홈</em> + 버튼을 누릅니다. 이제 이 작업은 배경으로 전송되었으며 눈에 보이지 않습니다. 이제 사용자가 작업으로 되돌아갈 +방법이 없어졌습니다. 이는 애플리케이션 시작 관리자에 표시되지 않기 때문입니다.</p> + +<p>사용자가 액티비티로 되돌아갈 수 있도록 하는 것을 원치 않는 경우, +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> + 요소의 +<a href="{@docRoot}guide/topics/manifest/activity-element.html#finish">{@code finishOnTaskLaunch}</a> +를 {@code "true"}로 설정하면 됩니다(<a href="#Clearing">스택 지우기</a>를 참조하십시오).</p> + +<p>작업과 액티비티가 개요 화면에서 어떻게 표시되고 관리되는지에 대한 +자세한 정보는 <a href="{@docRoot}guide/components/recents.html"> +개요 화면</a>에서 확인하실 수 있습니다.</p> + +<!-- +<h2>Beginner's Path</h2> + +<p>For more information about how to use intents to +activate other application components and publish the intents to which your components +respond, continue with the <b><a +href="{@docRoot}guide/components/intents-filters.html">Intents and Intent +Filters</a></b> document.</p> +--> |