diff options
Diffstat (limited to 'docs/html-intl/intl/ko')
13 files changed, 2474 insertions, 0 deletions
diff --git a/docs/html-intl/intl/ko/training/basics/activity-lifecycle/index.jd b/docs/html-intl/intl/ko/training/basics/activity-lifecycle/index.jd new file mode 100644 index 0000000..e66e8d1 --- /dev/null +++ b/docs/html-intl/intl/ko/training/basics/activity-lifecycle/index.jd @@ -0,0 +1,72 @@ +page.title=액티비티 수명 주기 관리하기 +page.tags=activity lifecycle +helpoutsWidget=true + +trainingnavtop=true +startpage=true + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + + +<h2>종속관계 및 전제조건</h2> +<ul> + <li>Android 프로젝트를 생성하는 방법(<a href="{@docRoot}training/basics/firstapp/creating-project.html">Android +프로젝트 생성</a> 참조)</li> +</ul> + + +<h2>필독 항목</h2> +<ul> + <li><a href="{@docRoot}guide/components/activities.html">액티비티</a></li> +</ul> + + +<h2>실습하기</h2> + +<div class="download-box"> + <a href="http://developer.android.com/shareables/training/ActivityLifecycle.zip" class="button">데모 다운로드</a> + <p class="filename">ActivityLifecycle.zip</p> +</div> + +</div> +</div> + +<p>사용자가 앱을 탐색하고, 앱에서 나가고, 앱으로 다시 돌아가면, 앱의 +{@link android.app.Activity} 인스턴스는 +수명 주기 안에서 서로 다른 상태 간에 전환됩니다. 예를 들어 +액티비티가 처음 시작되는 경우, 시스템의 전면에 표시되어 사용자의 +포커스를 받습니다. 이 과정에서 Android 시스템은 사용자 인터페이스 및 다른 구성요소에 설정된 +액티비티에 대해 일련의 수명 주기 메서드를 호출합니다. 사용자가 다른 액티비티를 시작하거나 다른 앱으로 전환하는 +작업을 수행하면, 백그라운드(액티비티가 더 +이상 보이지 않지만 인스턴스와 해당 상태는 그대로 유지되는 상태)로 전환되면서 시스템은 액티비티에 대해 또 다른 +수명 주기 메서드 세트를 호출합니다.</p> + +<p>사용자가 액티비티를 떠났다가 다시 돌아왔을 때 +액티비티가 어떤 식으로 동작할지에 대해 수명 주기 콜백 메서드 내에 선언할 수 있습니다. 예를 들어 스트리밍 비디오 플레이어를 구축하는 경우, +사용자가 다른 +앱으로 전환할 때 비디오를 일시정지하고 네트워크 연결을 종료할 수 있습니다. 사용자가 돌아오면, 네트워크를 다시 연결하고 사용자가 일시정지한 지점에서 +비디오를 다시 시작할 수 있게 할 수 있습니다.</p> + +<p>이 클래스에서는 각각의 {@link +android.app.Activity} 인스턴스가 수신하는 중요한 수명 주기 콜백 메서드를 설명합니다. 또한 이러한 콜백 메서드를 사용하여 액티비티가 +사용자가 원하는 작업을 하고, 액티비티가 필요로 하지 않을 때 시스템 리소스 소비를 방지하는 방법에 대해서도 설명합니다.</p> + +<h2>과정</h2> + +<dl> + <dt><b><a href="starting.html">액티비티 시작하기</a></b></dt> + <dd>액티비티 수명 주기의 기본사항, 사용자가 앱을 시작하는 방법, 그리고 기본 액티비티 생성 작업을 수행하는 +방법에 대해 설명합니다.</dd> + <dt><b><a href="pausing.html">액티비티 일시정지 및 재개하기</a></b></dt> + <dd>액티비티가 일시정지(부분적으로 가려져 있음)되었다가 재개되는 경우 어떤 동작이 발생하는지, 그리고 이러한 상태 전환 과정에서 수행해야 하는 +작업은 무엇인지 설명합니다.</dd> + <dt><b><a href="stopping.html">액티비티 정지 및 재시작하기</a></b></dt> + <dd>사용자가 액티비티를 완전히 떠났다가 돌아왔을 때 어떤 동작이 발생하는지 설명합니다.</dd> + <dt><b><a href="recreating.html">액티비티 재생성하기</a></b></dt> + <dd>액티비티가 소멸되면 어떤 동작이 발생하는지, 그리고 필요 시 액티비티 +상태를 재구축하는 방법에 대해 설명합니다.</dd> +</dl> + diff --git a/docs/html-intl/intl/ko/training/basics/activity-lifecycle/pausing.jd b/docs/html-intl/intl/ko/training/basics/activity-lifecycle/pausing.jd new file mode 100644 index 0000000..b0497cd --- /dev/null +++ b/docs/html-intl/intl/ko/training/basics/activity-lifecycle/pausing.jd @@ -0,0 +1,147 @@ +page.title=액티비티 일시정지 및 재개하기 +page.tags=activity lifecycle +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> + <div id="tb"> + + <h2>이 과정에서 다루는 내용</h2> + <ol> + <li><a href="#Pause">액티비티 일지정지하기</a></li> + <li><a href="#Resume">액티비티 재개하기</a></li> + </ol> + + <h2>필독 항목</h2> + <ul> + <li><a href="{@docRoot}guide/components/activities.html">액티비티</a> + </li> + </ul> + +<h2>실습하기</h2> + +<div class="download-box"> + <a href="http://developer.android.com/shareables/training/ActivityLifecycle.zip" class="button">데모 다운로드</a> + <p class="filename">ActivityLifecycle.zip</p> +</div> + + </div> +</div> + +<p>일반적인 앱 사용 중에 가끔 다른 +비주얼 구성요소로 인해 전면의 액티비티가 <em>일시정지</em>되는 경우가 있습니다. 예를 들어, 대화 상자 스타일과 같은 반투명 +액티비티가 열리면, 그 이전 액티비티는 일시정지됩니다. 액티비티가 +부분적으로 보이지만 현재 포커스 내에 있지 않는 한 일시정지된 상태로 유지됩니다.</p> + +<p>하지만 액티비티가 보이지 않고 완전히 가려지면, <em>정지</em> 상태가 됩니다(이에 대해서는 +다음 과정에서 설명함).</p> + +<p>액티비티가 일시정지 상태로 전환되면 시스템은 {@link android.app.Activity}에 대해 {@link +android.app.Activity#onPause onPause()} 메서드를 호출합니다. 이를 통해 +비디오와 같이 일시정지되어 있는 동안에는 진행되어서는 안 되는 지속적인 작업을 정지하거나, 사용자가 앱에서 떠나더라도 영구 저장되어야 하는 정보를 +유지할 수 있습니다. 사용자가 +일시정지 상태에서 액티비티로 돌아오는 경우, 시스템은 액티비티를 재개하고 {@link android.app.Activity#onResume onResume()} 메서드를 +호출합니다.</p> + +<p class="note"><strong>참고:</strong> 액티비티가 {@link +android.app.Activity#onPause()}에 대한 호출을 수신하는 경우, 이는 액티비티가 +잠시 일시정지되는 것이며, 사용자가 이 액티비티로 포커스를 다시 돌릴 수 있음을 나타냅니다. 하지만 이는 일반적으로 사용자가 액티비티를 떠남을 나타내는 +첫 번째 표시입니다.</p> + +<img src="{@docRoot}images/training/basics/basic-lifecycle-paused.png" /> +<p class="img-caption"><strong>그림 1.</strong> 액티비티를 +반투명 액티비티가 가릴 경우, 시스템은 {@link android.app.Activity#onPause onPause()}를 호출하고 액티비티는 일시정지된 상태로 +대기하게 됩니다(1). 사용자가 여전히 일시정지된 상태의 액티비티로 돌아오는 경우, +시스템은 {@link android.app.Activity#onResume onResume()}을 호출합니다(2).</p> + + +<h2 id="Pause">액티비티 일지정지하기</h2> + +<p>시스템이 액티비티에 대해 {@link android.app.Activity#onPause()}를 호출하면, 이는 +엄밀해 말해 액티비티가 여전히 부분적으로 보일 수 있음을 의미하지만, +대개의 경우 사용자가 액티비티를 떠나 곧 정지 상태로 전환될 것임을 나타냅니다. 일반적으로 다음 작업을 수행할 때 +{@link android.app.Activity#onPause()} 콜백을 사용해야 합니다.</p> + +<ul> + <li>애니메이션 또는 CPU 소비를 야기할 수 있는 기타 지속적인 작업 정지</li> + <li>저장되지 않은 변경 내용 커밋. 단, 사용자가 나갈 때 +이러한 변경 내용이 영구적으로 저장되기를 기대하는 경우에 한합니다(예: 이메일 임시저장).</li> + <li>브로드캐스트 리시버와 같은 시스템 리소스, GPS와 +같은 센서에 대한 핸들 또는 액티비티가 일지정지된 동안 배터리 수명에 영향을 미칠 수 있으며 사용자가 +필요로 하지 않는 모든 리소스 해제</li> +</ul> + +<p>예를 들어 애플리케이션이 {@link android.hardware.Camera}를 사용하는 경우 +{@link android.app.Activity#onPause()} 메서드는 이를 해제할 수 있는 좋은 위치입니다.</p> + +<pre> +@Override +public void onPause() { + super.onPause(); // Always call the superclass method first + + // Release the Camera because we don't need it when paused + // and other activities might need to use it. + if (mCamera != null) { + mCamera.release() + mCamera = null; + } +} +</pre> + +<p>일반적으로 폼에 입력된 개인 정보와 같은 +사용자의 변경 내용을 {@link android.app.Activity#onPause()}를 사용하여 영구 저장소에 저장하면 <strong>안 됩니다</strong>. 이메일 +임시보관과 같이 사용자가 자동저장을 원할 것이란 확신이 있을 경우에만 {@link android.app.Activity#onPause()} +내에서 사용자의 변경 내용을 영구 저장소에 유지해야 합니다. +하지만 데이터베이스에 쓰기와 같이 CPU를 많이 소모하는 작업은 {@link +android.app.Activity#onPause()} 중에는 수행하지 않아야 합니다. 그 이유는 이로 인해 다음 액티비티로 넘어가는 시각적 +전환이 느려질 수 있기 때문입니다(그 대신, 로드가 많은 셧다운 작업은 {@link android.app.Activity#onStop onStop()} +중에 수행해야 함).</p> + +<p>액티비티가 실제로 정지된 경우 사용자가 다음 +대상으로 신속하게 전환할 수 있도록 {@link android.app.Activity#onPause +onPause()} 메서드 내에서 수행되는 작업의 양을 비교적 단순하게 유지해야 합니다.</p> + +<p class="note"><strong>참고:</strong> 액티비티가 일시정지되면 {@link +android.app.Activity} 인스턴스는 메모리에 계속 유지되며, 액티비티가 재개되면 다시 호출됩니다. +최상위 상태가 재개됨 상태인 콜백 메서드 중에 +생성된 구성요소는 다시 초기화할 필요가 없습니다.</p> + + + +<h2 id="Resume">액티비티 재개하기</h2> + +<p>사용자가 일시정지됨 상태에서 액티비티를 재개하면, 시스템은 {@link +android.app.Activity#onResume()} 메서드를 호출합니다.</p> + +<p>액티비티가 처음 생성되는 경우를 +포함하여 액티비티가 전면에 표시될 때마다 시스템이 이 메서드를 호출한다는 것을 유의해야 합니다. 이와 같이, {@link +android.app.Activity#onResume()}을 구현하여 {@link +android.app.Activity#onPause()} 중에 해제하는 구성요소를 초기화하고, 액티비티가 +재개됨 상태로 전환될 때마다 필요한 다른 초기화 작업도 수행해야 합니다(예: 애니메이션 시작 및 액티비티가 사용자 포커스를 받을 때에만 +사용되는 구성 요소 초기화).</p> + +<p>다음 {@link android.app.Activity#onResume()} 예제는 위에 표시된 +{@link android.app.Activity#onPause()} 예제와 대응관계에 있습니다. 따라서 이 예제에서는 액티비티가 일시정지될 때 +해제되는 카메라를 초기화합니다.</p> + +<pre> +@Override +public void onResume() { + super.onResume(); // Always call the superclass method first + + // Get the Camera instance as the activity achieves full user focus + if (mCamera == null) { + initializeCamera(); // Local method to handle camera init + } +} +</pre> + + + + + + + diff --git a/docs/html-intl/intl/ko/training/basics/activity-lifecycle/recreating.jd b/docs/html-intl/intl/ko/training/basics/activity-lifecycle/recreating.jd new file mode 100644 index 0000000..79fb92b --- /dev/null +++ b/docs/html-intl/intl/ko/training/basics/activity-lifecycle/recreating.jd @@ -0,0 +1,178 @@ +page.title=액티비티 재생성하기 +page.tags=activity lifecycle +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> + <div id="tb"> + + <h2>이 과정에서 다루는 내용</h2> + <ol> + <li><a href="#SaveState">액티비티 상태 저장하기</a></li> + <li><a href="#RestoreState">액티비티 상태 복원하기</a></li> + </ol> + + <h2>필독 항목</h2> + <ul> + <li><a href="{@docRoot}training/basics/supporting-devices/screens.html">다양한 +화면 지원</a></li> + <li><a href="{@docRoot}guide/topics/resources/runtime-changes.html">런타임 변경 처리하기</a></li> + <li><a href="{@docRoot}guide/components/activities.html">액티비티</a> + </li> + </ul> + + </div> +</div> + +<p>정상적인 앱 동작으로 인해 액티비티가 소멸되는 몇 가지 시나리오가 있습니다. +예를 들어 사용자가 <em>뒤로</em> 버튼을 누르거나 액티비티가 {@link android.app.Activity#finish()}를 호출하여 +자체적인 소멸 신호를 보내는 경우입니다. 또한 액티비티가 +현재 정지되어 있고 장시간 사용되지 않거나, 전면에 있는 액티비티가 더 많은 +리소스를 필요로 하여 시스템이 백그라운드 프로세스를 종료해서 메모리를 회수해야 하는 경우에도 시스템이 액티비티를 소멸시킬 수 있습니다.</p> + +<p>사용자가 <em>뒤로</em> 버튼을 누르거나 액티비티가 +스스로 종료되어 소멸된 경우, {@link android.app.Activity} 인스턴스에 대한 시스템의 개념은 완전히 사라지게 됩니다. 왜냐하면 +동작이 액티비티가 더 이상 필요치 않다는 것을 나타내기 때문입니다. 하지만 시스템이 정상적인 앱 동작이 아닌 시스템 제약 조건으로 인해 +액티비티를 소멸한 경우, 실제 +{@link android.app.Activity} 인스턴스는 사라지지만 시스템은 그것이 존재하고 있었음을 기억합니다. 예를 들어 +사용자가 다시 해당 액티비티를 탐색하면, 시스템은 소멸된 액티비티의 상태를 설명하는 +저장된 데이터 세트를 사용하여 액티비티의 새 인스턴스를 생성합니다. 시스템이 +이전 상태를 복원하기 위해 사용하는 저장된 데이터를 "인스턴스 상태"라고 하며, 이는 {@link android.os.Bundle} 개체에 저장된 +키-값 쌍의 컬렉션입니다.</p> + +<p class="caution"><strong>주의:</strong> 사용자가 화면을 회전할 때마다 +액티비티가 소멸되고 재생성됩니다. 화면 방향이 바뀌면 시스템은 +전면에 있는 액티비티를 소멸하고 재생성합니다. 그 이유는 화면 구성이 바뀌었으며, 액티비티가 레이아웃과 같은 대체 리소스를 +로딩해야 할 수 있기 때문입니다.</p> + +<p>기본적으로 시스템은 {@link android.os.Bundle} 인스턴스 상태를 사용하여 액티비티 레이아웃의 각 {@link android.view.View} 개체에 대한 +정보를 저장합니다(예: {@link android.widget.EditText} 개체에 +입력된 텍스트 값 등). 따라서 액티비티 인스턴스가 소멸되고 +재생성된 경우, 레이아웃의 상태는 +별도의 코드 요청 없이 이전 상태로 복원됩니다. 하지만 +액티비티에서 사용자 진행 상태를 +추적하는 멤버 변수처럼 액티비티에 복원하고자 하는 상태 정보가 더 많이 있는 경우도 있습니다.</p> + +<p class="note"><strong>참고:</strong> Android 시스템이 액티비티에서 +보기의 상태를 복원하기 위해서는 +<a href="{@docRoot}reference/android/view/View.html#attr_android:id">{@code +android:id}</a> 특성으로 제공되는 <strong>고유 ID가 각 보기마다 있어야 합니다</strong>.</p> + +<p>액티비티 상태에 대한 추가 데이터를 저장하려면 +{@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} 콜백 메서드를 재정의해야 합니다. +시스템은 사용자가 액티비티를 떠날 경우 이 메서드를 호출하며, +액티비티가 예기치 않게 소멸될 경우 저장되는 +{@link android.os.Bundle} 개체로 전달합니다. 시스템이 +나중에 액티비티 인스턴스를 재생성해야 하는 경우, 동일한 {@link +android.os.Bundle} 개체를 {@link android.app.Activity#onRestoreInstanceState +onRestoreInstanceState()} 및 {@link android.app.Activity#onCreate onCreate()} +메서드 모두에 전달합니다.</p> + +<img src="{@docRoot}images/training/basics/basic-lifecycle-savestate.png" /> +<p class="img-caption"><strong>그림 2.</strong> 시스템은 액티비티 중지 작업을 시작할 때 +{@link android.app.Activity#onSaveInstanceState onSaveInstanceState()}(1)를 호출합니다. +따라서 {@link android.app.Activity} 인스턴스가 +재생성되어야 하는 경우 저장하고자 하는 추가 상태 데이터를 지정할 수 있습니다. +액티비티가 소멸되고 동일한 인스턴스가 재생성되어야 하는 경우, 시스템은 {@link android.app.Activity#onCreate onCreate()} 메서드(2) +및 {@link android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} 메서드(3) +모두에 (1)에서 정의된 상태 +데이터를 전달합니다.</p> + + + +<h2 id="SaveState">액티비티 상태 저장하기</h2> + +<p>액티비티의 정지 작업 시작 시 시스템은 {@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()}를 호출합니다. 이에 따라 액티비티는 키-값 +쌍 컬렉션과 함께 상태 정보를 저장할 수 있습니다. 메서드의 기본 구현은 {@link android.widget.EditText} 위젯 내 텍스트 또는 {@link android.widget.ListView}의 스크롤 +위치와 같은 액티비티의 +보기 계층 구조에 대한 정보를 저장합니다.</p> + +<p>액티비티에 대한 추가 상태 정보를 저장하려면 {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()}를 +구현한 다음 {@link android.os.Bundle} 개체에 +키-값 쌍을 추가해야 합니다. 예를 들면 다음과 같습니다.</p> + +<pre> +static final String STATE_SCORE = "playerScore"; +static final String STATE_LEVEL = "playerLevel"; +... + +@Override +public void onSaveInstanceState(Bundle savedInstanceState) { + // Save the user's current game state + savedInstanceState.putInt(STATE_SCORE, mCurrentScore); + savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel); + + // Always call the superclass so it can save the view hierarchy state + super.onSaveInstanceState(savedInstanceState); +} +</pre> + +<p class="caution"><strong>주의:</strong> 항상 {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}의 슈퍼클래스 구현을 호출하여 기본 구현에서 +보기 계층 구조를 저장할 수 있도록 합니다.</p> + + + +<h2 id="RestoreState">액티비티 상태 복원하기</h2> + +<p>액티비티가 이전에 소멸된 후 재생성되면, 시스템이 액티비티를 +전달한 {@link android.os.Bundle}로부터 저장된 +상태를 복구할 수 있습니다. {@link android.app.Activity#onCreate onCreate()} 및 {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} 콜백 메서드 둘 다 인스턴스 상태 정보를 포함하는 +동일한 {@link android.os.Bundle}을 수신합니다.</p> + +<p>{@link android.app.Activity#onCreate onCreate()} 메서드는 +시스템이 액티비티의 새 인스턴스를 생성하든, 이전 인스턴스를 재생성하든 상관없이 호출되므로, 읽기를 시도하기 전에 상태 {@link android.os.Bundle}이 null인지 +반드시 확인해야 합니다. null일 경우, +시스템은 이전에 소멸된 액티비티의 인스턴스를 +복원하지 않고 새 인스턴스를 생성합니다.</p> + +<p>다음은 {@link android.app.Activity#onCreate +onCreate()}에서 몇 가지 상태 데이터를 복원하는 방법에 대한 예제입니다.</p> + +<pre> +@Override +protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); // Always call the superclass first + + // Check whether we're recreating a previously destroyed instance + if (savedInstanceState != null) { + // Restore value of members from saved state + mCurrentScore = savedInstanceState.getInt(STATE_SCORE); + mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL); + } else { + // Probably initialize members with default values for a new instance + } + ... +} +</pre> + +<p>{@link android.app.Activity#onCreate onCreate()} 중에 상태를 복원하는 +대신, 시스템이 {@link android.app.Activity#onStart()} 메서드 후에 +호출하는 {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()}를 구현하도록 선택할 수 있습니다. 시스템은 복원할 저장 +상태가 있을 경우에만 {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()}를 호출합니다. 따라서 {@link android.os.Bundle}이 null인지 확인할 필요가 없습니다.</p> + +<pre> +public void onRestoreInstanceState(Bundle savedInstanceState) { + // Always call the superclass so it can restore the view hierarchy + super.onRestoreInstanceState(savedInstanceState); + + // Restore state members from saved instance + mCurrentScore = savedInstanceState.getInt(STATE_SCORE); + mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL); +} +</pre> + +<p class="caution"><strong>주의:</strong> 항상 {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}의 슈퍼클래스 구현을 호출하여 기본 구현에서 보기 계층 구조의 상태를 +복원할 수 있도록 합니다.</p> + +<p>런타임에 +재시작 이벤트로 인한 액티비티 재생성과 관련한 자세한 내용은 <a href="{@docRoot}guide/topics/resources/runtime-changes.html">런타임 변경 처리하기</a>를 참조하세요.</p> + diff --git a/docs/html-intl/intl/ko/training/basics/activity-lifecycle/starting.jd b/docs/html-intl/intl/ko/training/basics/activity-lifecycle/starting.jd new file mode 100644 index 0000000..71ecf06 --- /dev/null +++ b/docs/html-intl/intl/ko/training/basics/activity-lifecycle/starting.jd @@ -0,0 +1,285 @@ +page.title=액티비티 시작하기 +page.tags=activity lifecycle +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + + +<div id="tb-wrapper"> + <div id="tb"> + + <h2>이 과정에서 다루는 내용</h2> +<ol> + <li><a href="#lifecycle-states">수명 주기 콜백 이해하기</a></li> + <li><a href="#launching-activity">앱 시작 관리자 액티비티 지정하기</a></li> + <li><a href="#Create">새로운 인스턴스 생성하기</a></li> + <li><a href="#Destroy">액티비티 소멸하기</a></li> +</ol> + + <h2>필독 항목</h2> + <ul> + <li><a href="{@docRoot}guide/components/activities.html">액티비티</a></li> + </ul> + +<h2>실습하기</h2> + +<div class="download-box"> + <a href="http://developer.android.com/shareables/training/ActivityLifecycle.zip" class="button">데모 다운로드</a> + <p class="filename">ActivityLifecycle.zip</p> +</div> + + </div> +</div> + +<p>{@code main()} 메서드로 앱이 실행되는 다른 프로그래밍 패러다임과는 달리, +Android 시스템은 {@link android.app.Activity} 인스턴스 수명 주기의 특정 단계에 부합하는 특정 +콜백 메서드를 호출하여 해당 인스턴스 내 코드를 +실행합니다. 액티비티를 시작하는 순차적인 콜백 메서드가 있으며, 액티비티를 소멸시키는 +순차적인 콜백 메서드가 있습니다.</p> + +<p>이 과정에서는 가장 중요한 수명 주기 메서드에 대해 간략히 설명하며, 액티비티의 새 인스턴스를 생성하는 첫 번째 수명 주기 콜백을 +처리하는 방법을 보여줍니다.</p> + + + +<h2 id="lifecycle-states">수명 주기 콜백 이해하기</h2> + +<p>액티비티 수명 동안, 시스템은 계단식 피라미드와 비슷하게 수명 주기 메서드의 핵심 세트를 +순차적으로 호출합니다. 즉, +액티비티 수명 주기의 각 단계는 피라미드의 각 계단과 같습니다. 시스템이 새 액티비티 인스턴스를 생성하면, +각각의 콜백 메서드는 액티비티 상태를 한 단계 상향 이동합니다. 피라미드의 최상층은 +액티비티가 전면에서 실행 중이며 사용자가 이러한 액티비티와 상호 작용할 수 있는 단계를 뜻합니다.</p> + +<p>사용자가 액티비티를 나가면, 시스템은 액티비티를 해체하기 위해 액티비티 +상태를 피라미드에서 하향 이동하는 다른 메서드를 호출합니다. 경우에 따라, 액티비티가 +피라미드에서 완전히 하향 이동되지 않고 기다립니다(예: 사용자가 다른 앱으로 전환한 경우). 이런 경우, +액티비티는 다시 피라미드 최상층으로 상향 이동될 수 있으며(사용자가 액티비티로 돌아갈 경우), 사용자는 종료한 지점에서 +작업을 재개할 수 있습니다.</p> + + +<img src="{@docRoot}images/training/basics/basic-lifecycle.png" /> +<p class="img-caption"><strong>그림 1.</strong> 액티비티 +수명 주기를 계단식 피라미드로 간략하게 표현한 그림. 이 그림에서는 최상층에 있는 +재개됨(Resumed) 상태를 향하여 액티비티를 한 단계 상향 이동하는 데 사용되는 각 콜백마다 하위 단계로 +액티비티를 하향 이동하는 콜백 메서드가 있음을 보여줍니다. 액티비티는 +일시정지됨(Paused) 및 정지됨(Stopped) 상태에서도 재개 상태로 돌아갈 수 있습니다.</p> + + +<p>액티비티의 복잡한 정도에 따라, 모든 +수명 주기 메서드를 구현할 필요가 없는 경우도 있습니다. 하지만 각각의 수명 주기 메서드를 이해하고, 사용자가 예상한 대로 +앱이 동작하도록 필요한 수명 주기 메서드를 구현하는 것이 중요합니다. 액티비티 수명 주기 메서드를 올바르게 구현하면, 다음을 포함하여 여러 가지 방법으로 앱이 제대로 동작하도록 +보장할 수 있습니다.</p> +<ul> + <li>사용자가 앱을 사용하는 +도중에 전화가 걸려오거나 다른 앱으로 전환할 때 충돌하지 않도록 합니다.</li> + <li>사용자가 앱을 활발하게 사용하지 않는 경우, 소중한 시스템 리소스를 소비하지 +않도록 합니다.</li> + <li>사용자가 앱에서 나갔다가 나중에 돌아왔을 때 사용자의 진행 상태를 +손실하지 않도록 합니다.</li> + <li>화면이 가로 방향과 세로 방향 간에 회전할 경우, 충돌하거나 사용자의 +진행 상태를 손실하지 않도록 합니다.</li> +</ul> + +<!-- +<p class="table-caption"><strong>Table 1.</strong> Activity lifecycle state pairs and callback +methods.</p> +<table> + <tr> + <th scope="col">Lifecycle State</th> + <th scope="col">Startup Method</th> + <th scope="col">Teardown Method</th> + </tr> + <tr> + <td>Created / Destroyed</td> + <td>{@link android.app.Activity#onCreate onCreate()}</td> + <td>{@link android.app.Activity#onDestroy()}</td> + </tr> + <tr> + <td>Started / Stopped</td> + <td>{@link android.app.Activity#onStart()}</td> + <td>{@link android.app.Activity#onStop()}</td> + </tr> + <tr> + <td>Resumed / Resumed</td> + <td>{@link android.app.Activity#onResume()}</td> + <td>{@link android.app.Activity#onPause()}</td> + </tr> +</table> +--> + +<p>다음 과정에서 확인할 수 있듯이, 액티비티가 그림 1에 나와 있는 상태 간을 +전환하는 여러 가지 상황이 있습니다. 하지만, 이들 상태 +중 세 가지만 유일하게 정적일 수 있습니다. 즉, 액티비티는 +이 세 가지 상태에 한하여 그중 하나에서 장시간 동안 존재할 수 있습니다.</p> +<dl> + <dt>재개됨(Resumed)</dt> + <dd>이 상태에서는 액티비티가 전면에 있으며 사용자가 액티비티와 상호 작용할 수 +있습니다(경우에 따라 "실행 중" 상태라고도 함).</dd> + <dt>일시정지됨(Paused)</dt> + <dd>이 상태에서는 액티비티가 다른 액티비티로 인해 부분적으로 가려져 있습니다. 이런 경우, 전면에 있는 +다른 액티비티는 반투명이거나 전체 화면을 차지하지 않습니다. 일시정지된 +액티비티는 사용자 입력을 받지 않으며, 어떠한 코드도 실행할 수 없습니다. + <dt>정지됨(Stopped)</dt> + <dd>이 상태에서는 액티비티가 완전히 가려져서 사용자에게 보이지 않습니다. 이 상태는 액티비티가 백그라운드에 있는 것으로 +간주됩니다. 정지되어 있는 동안 액티비티 인스턴스 및 멤버 변수와 같은 모든 상태 +정보가 유지되지만, 어떠한 코드도 실행할 수 없습니다.</dd> +</dl> + +<p>이외의 상태(생성됨(Created) 및 시작됨(Started))는 일시적이며, 시스템은 다음 수명 주기 콜백 메서드를 호출하여 +다음 상태로 신속하게 전환합니다. 즉, 시스템은 +{@link android.app.Activity#onCreate onCreate()}를 호출한 후 바로 {@link +android.app.Activity#onStart()}를 호출하고, 또 그 즉시 {@link +android.app.Activity#onResume()}을 호출합니다.</p> + +<p>이것으로 기본적인 액티비티 수명 주기에 대한 설명을 마칩니다. 이제, +구체적인 수명 주기 동작에 대해 알아보도록 하겠습니다.</p> + + + +<h2 id="launching-activity">앱 시작 관리자 액티비티 지정하기</h2> + +<p>사용자가 홈 화면에서 앱 아이콘을 선택하면, 시스템이 앱에서 "시작 관리자"(또는 "메인") 액티비티로 선언한 {@link android.app.Activity}에 대한 {@link +android.app.Activity#onCreate onCreate()} 메서드를 +호출합니다. 이 액티비티는 앱의 사용자 인터페이스에 대한 +주 진입점 역할을 합니다.</p> + +<p>Android 매니페스트 파일인 <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">{@code AndroidManifest.xml}</a>에 메인 액티비티로 사용할 액티비티를 정의할 수 있습니다. 이 파일은 +프로젝트 디렉터리의 루트에 있습니다.</p> + +<p>앱의 메인 액티비티는 {@link +android.content.Intent#ACTION_MAIN MAIN} 작업 및{@link android.content.Intent#CATEGORY_LAUNCHER LAUNCHER} 카테고리를 포함하는 <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a>와 함께 +매니페스트 파일에 선언되어야 합니다. 예를 들면 다음과 같습니다.</p> + +<pre> +<activity android:name=".MainActivity" android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> +</activity> +</pre> + +<p class="note"><strong>참고:</strong> Android SDK +Tools를 사용하여 새 Android 프로젝트를 생성하는 경우, 매니페스트에서 이 필터와 함께 +선언된 {@link android.app.Activity} 클래스가 기본 프로젝트 파일에 포함됩니다.</p> + +<p>{@link android.content.Intent#ACTION_MAIN MAIN} 작업 또는 +{@link android.content.Intent#CATEGORY_LAUNCHER LAUNCHER} 카테고리가 +액티비티 중 하나에 대해 선언되어 있지 않을 경우, 홈 화면의 앱 목록에 앱 아이콘이 표시되지 않습니다.</p> + + + +<h2 id="Create">새로운 인스턴스 생성하기</h2> + +<p>대부분의 앱은 사용자가 다양한 작업을 수행할 수 있도록 여러 액티비티를 포함하고 있습니다. +액티비티가 사용자가 앱 아이콘을 클릭할 때 생성되는 메인 액티비티든, 사용자 작업에 +대한 응답으로 앱이 시작하는 다른 액티비티든 간에, +시스템은 {@link +android.app.Activity#onCreate onCreate()} 메서드를 호출하여 매번 {@link android.app.Activity}의 새 인스턴스를 생성합니다.</p> + +<p>액티비티의 전체 수명 주기 동안 한 번만 발생하는 기본 +애플리케이션 시작 논리를 수행하도록 {@link android.app.Activity#onCreate onCreate()} 메서드를 구현해야 합니다. 예를 +들어 {@link android.app.Activity#onCreate onCreate()}의 구현은 +사용자 인터페이스를 정의해야 하며, 몇몇 클래스 범위의 변수를 인스턴스화해야 할 수 있습니다.</p> + +<p>예를 들어 다음 {@link android.app.Activity#onCreate onCreate()} +메서드에 대한 예제는 +사용자 인터페이스 선언(XML 레이아웃 파일에 정의), 멤버 변수 정의, +몇몇 UI 구성과 같은 액티비티에 대한 몇 가지 기본 설정을 수행하는 코드를 보여줍니다.</p> + +<pre> +TextView mTextView; // Member variable for text view in the layout + +@Override +public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Set the user interface layout for this Activity + // The layout file is defined in the project res/layout/main_activity.xml file + setContentView(R.layout.main_activity); + + // Initialize member TextView so we can manipulate it later + mTextView = (TextView) findViewById(R.id.text_message); + + // Make sure we're running on Honeycomb or higher to use ActionBar APIs + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + // For the main activity, make sure the app icon in the action bar + // does not behave as a button + ActionBar actionBar = getActionBar(); + actionBar.setHomeButtonEnabled(false); + } +} +</pre> + +<p class="caution"><strong>주의:</strong> {@link android.os.Build.VERSION#SDK_INT}를 사용하여 +이전 시스템이 새 API를 실행하지 못하도록 방지하는 방법은 Android 2.0(API 레벨 +5) 이상에서만 가능합니다. 이전 버전에서는 런타임 예외가 발생합니다.</p> + +<p>{@link android.app.Activity#onCreate onCreate()}가 실행을 마치면 시스템은 +{@link android.app.Activity#onStart()} 및 {@link android.app.Activity#onResume()} 메서드를 +연달아 호출합니다. 액티비티가 생성됨(Created) 또는 시작됨(Started) 상태에서 머무르는 경우는 없습니다. 엄밀히 말하면 +{@link android.app.Activity#onStart()}가 호출되면 액티비티가 사용자에게 보이지만, +{@link android.app.Activity#onResume()}이 바로 뒤따르고, 어떠한 상황(예: 전화가 오거나, +사용자가 다른 액티비티로 전환하거나, 기기 화면이 꺼진 경우)이 발생하기 전까지는 액티비티가 +재개됨(Resumed) 상태로 유지됩니다.</p> + +<p>앞으로 진행되는 과정에서는 다른 시작 메서드인 {@link +android.app.Activity#onStart()} 및 {@link android.app.Activity#onResume()}이 일시정지됨(Paused) 또는 정지됨(Stopped) 상태에서 +액티비티를 재개하기 위해 사용된 경우 액티비티의 수명 주기 동안 어떤 식으로 유용한지에 대해 설명합니다.</p> + +<p class="note"><strong>참고:</strong> {@link android.app.Activity#onCreate onCreate()} +메서드는 <code>savedInstanceState</code>라고 하는 매개변수를 포함합니다. 이 매개변수는 <a href="recreating.html">액티비티 재생성하기</a>에 대한 +마지막 과정에서 설명합니다.</p> + + +<img src="{@docRoot}images/training/basics/basic-lifecycle-create.png" /> +<p class="img-caption"><strong>그림 2.</strong> 액티비티의 새 인스턴스를 생성할 때 +시스템이 순차적으로 호출하는 {@link android.app.Activity#onCreate onCreate()}, {@link android.app.Activity#onStart()} 및 {@link android.app.Activity#onResume()}의 +세 가지 메인 콜백을 강조하여 보여주는 액티비티의 수명 주기 구조에 +대한 또 다른 그림. 이러한 순차적인 +콜백이 완료되면, 액티비티는 재개됨(Resumed) 상태가 됩니다. 이 상태에서는 사용자가 다른 +액티비티로 전환하기 전까지 사용자가 액티비티와 상호 작용할 수 있습니다.</p> + + + + + + + +<h2 id="Destroy">액티비티 소멸하기</h2> + +<p>액티비티의 첫 번째 수명 주기 콜백은 {@link android.app.Activity#onCreate +onCreate()}이고, 마지막 콜백은 {@link android.app.Activity#onDestroy}입니다. 시스템은 +시스템 메모리에서 액티비티 인스턴스가 완전히 제거된다는 내용의 마지막 +신호로 액티비티에서 이 메서드를 호출합니다.</p> + +<p>대부분의 앱은 이 메서드를 구현할 필요가 없습니다. 왜냐하면 액티비티와 +함께 로컬 클래스 참조가 소멸되고, 액티비티가 {@link +android.app.Activity#onPause} 및 {@link android.app.Activity#onStop} 중에 대부분 정리 작업을 수행하기 때문입니다. 하지만 +액티비티가 {@link +android.app.Activity#onCreate onCreate()} 중에 생성한 백그라운드 스레드 또는 제대로 닫지 않으면 메모리 누수를 +야기할 수 있는 다른 장시간 실행되는 리소스를 포함하는 경우, {@link +android.app.Activity#onDestroy} 중에 액티비티를 중단시켜야 합니다.</p> + +<pre> +@Override +public void onDestroy() { + super.onDestroy(); // Always call the superclass + + // Stop method tracing that the activity started during onCreate() + android.os.Debug.stopMethodTracing(); +} +</pre> + +<p class="note"><strong>참고:</strong> 시스템은 한 가지 경우, +즉 {@link android.app.Activity#onCreate onCreate()} 메서드에서 {@link +android.app.Activity#finish()}를 호출하는 경우를 제외하고는, {@link android.app.Activity#onPause} 및 {@link +android.app.Activity#onStop}을 먼저 호출한 후에 {@link android.app.Activity#onDestroy}를 +호출합니다. 경우에 따라(예: 액티비티가 다른 액티비티를 +실행하기 위한 임시 의사 결정자 역할을 할 경우), {@link +android.app.Activity#onCreate onCreate()} 내에서 {@link android.app.Activity#finish()}를 호출하여 액티비티를 소멸시킬 수도 있습니다. 이 경우, 시스템이 +다른 수명 주기 메서드를 호출하지 않고 즉시 +{@link android.app.Activity#onDestroy}를 호출합니다.</p> diff --git a/docs/html-intl/intl/ko/training/basics/activity-lifecycle/stopping.jd b/docs/html-intl/intl/ko/training/basics/activity-lifecycle/stopping.jd new file mode 100644 index 0000000..79e713a --- /dev/null +++ b/docs/html-intl/intl/ko/training/basics/activity-lifecycle/stopping.jd @@ -0,0 +1,187 @@ +page.title=액티비티 정지 및 재시작하기 +page.tags=activity lifecycle +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> + <div id="tb"> + + <h2>이 과정에서 다루는 내용</h2> + <ol> + <li><a href="#Stop">액티비티 정지하기</a></li> + <li><a href="#Start">액티비티 시작/재시작하기</a></li> + </ol> + + <h2>필독 항목</h2> + <ul> + <li><a href="{@docRoot}guide/components/activities.html">액티비티</a> + </li> + </ul> + +<h2>실습하기</h2> + +<div class="download-box"> + <a href="http://developer.android.com/shareables/training/ActivityLifecycle.zip" class="button">데모 다운로드</a> + <p class="filename">ActivityLifecycle.zip</p> +</div> + + </div> +</div> + +<p>액티비티 수명 주기에서 액티비티를 제대로 정지하고 재시작하는 것은 중요한 프로세스입니다. +그래야만 사용자가 앱이 항상 작동 상태를 유지하고 진행 상태를 손실하지 않는다고 인식하도록 할 수 있습니다. +다음과 같이 액티비티가 정지되었다가 재시작되는 몇 가지 중요한 시나리오가 있습니다.</p> + +<ul> + <li>사용자가 최근 앱 창을 열고 다른 액티비티로 전환하면 현재 전면에 있는 앱의 +액티비티가 정지됩니다. 사용자가 홈 화면 시작 관리자 아이콘 또는 최근 앱 창에서 +앱으로 돌아오면 액티비티가 재시작됩니다.</li> + <li>사용자가 앱에서 새 액티비티를 시작하는 작업을 수행합니다. 두 번째 액티비티가 +생성되면 현재 액티비티가 정지됩니다. 사용자가 <em>뒤로</em> +버튼을 누르면 첫 번째 액티비티가 재시작됩니다.</li> + <li>사용자가 앱을 사용하는 도중에 전화가 걸려옵니다.</li> +</ul> + +<p>{@link android.app.Activity} 클래스는 두 가지 수명 주기 메서드인 {@link +android.app.Activity#onStop()} 및 {@link android.app.Activity#onRestart()}를 제공합니다. 이 메서드를 사용하면 액티비티의 정지 및 재시작 처리 방법을 +명확하게 지정할 수 있습니다. 부분적인 UI 차단을 식별하는 +일시정지됨 상태와 달리, 정지됨 상태는 UI가 더 이상 보이지 않으며, +사용자의 포커스가 별도의 액티비티 또는 완전히 별도의 앱에 있음을 보장합니다.</p> + +<p class="note"><strong>참고:</strong> {@link android.app.Activity} +인스턴스가 정지되면 시스템이 이 인스턴스를 시스템 메모리에 유지하기 때문에 +{@link android.app.Activity#onStop()} 및 {@link android.app.Activity#onRestart()} 또는 {@link +android.app.Activity#onStart()} 메서드 조차 구현할 필요가 없을 수도 있습니다. 비교적 간단한 액티비티의 경우 대부분, +액티비티가 별 문제 없이 정지되고 재시작됩니다. 따라서 {@link +android.app.Activity#onPause()}를 사용하여 진행 중인 작업을 일시정지하고 시스템 리소스와의 연결을 끊기만 하면 됩니다.</p> + +<img src="{@docRoot}images/training/basics/basic-lifecycle-stopped.png" /> +<p class="img-caption"><strong>그림 1.</strong> 사용자가 액티비티를 떠나면, 시스템은 +{@link android.app.Activity#onStop onStop()}을 호출하여 액티비티를 정지시킵니다(1). 액티비티가 정지된 상태에서 +사용자가 돌아올 경우, 시스템은 {@link android.app.Activity#onRestart onRestart()}(2) +를 호출하고, 곧바로 {@link android.app.Activity#onStart onStart()}(3) 및 {@link +android.app.Activity#onResume()}(4)을 호출합니다. 어떤 시나리오로 인해 액티비티가 +정지되든 간에, 시스템은 항상 {@link +android.app.Activity#onStop onStop()}을 호출하기 전에 {@link android.app.Activity#onPause onPause()}를 호출합니다.</p> + + + +<h2 id="Stop">액티비티 정지하기</h2> + +<p>{@link android.app.Activity#onStop()} 메서드에 대한 호출을 수신한 액티비티는 더 +이상 보이지 않게 되고, 사용자가 +사용하지 않는 동안 필요하지 않은 거의 모든 리소스를 해제해야 합니다. 액티비티가 정지되고 나면, 시스템이 시스템 메모리를 +회수해야 하는 경우 해당 인스턴스를 소멸할 수도 있습니다. 심한 경우, 시스템이 액티비티의 최종 +{@link android.app.Activity#onDestroy()} 콜백을 호출하지 않고 앱 프로세스만 단순하게 중단시킬 수도 있습니다. 따라서 {@link android.app.Activity#onStop()}을 사용하여 +메모리 누수를 야기시킬 수 있는 리소스를 해제하는 것이 중요합니다.</p> + +<p>{@link android.app.Activity#onStop()} 전에 {@link android.app.Activity#onPause onPause()} +메서드가 호출되기는 하지만, 데이터베이스에 정보를 쓰는 +작업과 같이 규모가 크고 CPU를 많이 사용하는 종료 작업을 수행하는 경우 +{@link android.app.Activity#onStop onStop()}을 사용해야 합니다.</p> + +<p>다음은 영구적인 저장소에 노트 원고의 내용을 +저장하는 {@link android.app.Activity#onStop onStop()}을 구현하는 예제입니다.</p> + +<!-- TODO: Find a better example for onStop, because this kind of thing should probably use a +separate thread but that's too complicated to show here. --> +<pre> +@Override +protected void onStop() { + super.onStop(); // Always call the superclass method first + + // Save the note's current draft, because the activity is stopping + // and we want to be sure the current note progress isn't lost. + ContentValues values = new ContentValues(); + values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText()); + values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle()); + + getContentResolver().update( + mUri, // The URI for the note to update. + values, // The map of column names and new values to apply to them. + null, // No SELECT criteria are used. + null // No WHERE columns are used. + ); +} +</pre> + +<p>액티비티가 정지되면, {@link android.app.Activity} 개체는 메모리에 계속 유지되며, +액티비티가 재개되면 다시 호출됩니다. 최상위 상태가 재개됨 상태인 콜백 메서드 중에 +생성된 구성요소는 다시 초기화할 필요가 없습니다. 또한 시스템은 레이아웃에 있는 각 {@link android.view.View}의 현재 상태도 +기록합니다. 따라서 사용자가 {@link android.widget.EditText} 위젯에 텍스트를 +입력하면 해당 내용이 저장되기 때문에 이를 저장 및 복원할 +필요가 없습니다.</p> + +<p class="note"><strong>참고:</strong> 시스템이 정지된 상태의 액티비티를 소멸시키는 경우에도, +{@link android.os.Bundle}(키-값 쌍 Blob) 내에 {@link +android.widget.EditText}와 같은 {@link android.view.View} 개체의 상태를 유지하고, 사용자가 +액티비티의 같은 인스턴스로 돌아오면 이를 복원합니다. 액티비티가 소멸되고 재생성될 경우, {@link android.os.Bundle}을 사용하여 +다른 상태 데이터를 저장하는 방법에 대해서는 <a href="recreating.html">다음 과정</a>에서 자세히 설명합니다.</p> + + + +<h2 id="Start">액티비티 시작/재시작하기</h2> + +<p>액티비티가 정지된 상태에서 전면으로 되돌아올 때 +{@link android.app.Activity#onRestart()}에 대한 호출을 수신합니다. 시스템은 {@link +android.app.Activity#onStart()} 메서드도 호출합니다. 이 동작은 재시작되든, 아니면 처음 생성되든지에 상관없이 +액티비티가 보이게 될 때마다 발생합니다. 하지만 {@link +android.app.Activity#onRestart()} 메서드는 액티비티가 +정지된 상태에서 재개되는 경우에만 호출됩니다. 따라서 +액티비티가 소멸된 것이 아니라 이전에 정지된 경우에 한하여 필요할 수 있는 특수 복원 작업을 수행하는 데 이 메서드를 사용할 수 있습니다.</p> + +<p>액티비티의 상태 +복원 작업에 앱이 {@link android.app.Activity#onRestart()}를 사용해야 하는 경우는 드물기 때문에 +일반 앱에 적용되는 이 메서드에 대한 가이드라인은 없습니다. 하지만 {@link android.app.Activity#onStop()} +메서드는 모든 액티비티 리소스를 기본적으로 정리해야 하기 때문에, 액티비티가 재개되면 +다시 인스턴스화해야 합니다. 하지만 액티비티가 처음 +생성되는 경우(액티비티의 기존 인스턴스가 없을 경우)에도 인스턴스화해야 합니다. 따라서 +일반적으로 {@link android.app.Activity#onStart()} 콜백 메서드를 {@link android.app.Activity#onStop()} 메서드에 대한 상대 +메서드로 사용해야 합니다. 그 이유는 시스템이 액티비티를 생성할 때와 +액티비티를 정지된 상태에서 재시작할 때 모두 {@link +android.app.Activity#onStart()}를 호출하기 때문입니다.</p> + +<p>예를 들어 사용자가 장시간 앱을 떠났다가 다시 앱으로 +돌아올 수 있기 때문에, {@link android.app.Activity#onStart()} 메서드는 +필요한 시스템 기능이 활성화되었는지 확인하기 위한 좋은 위치입니다.</p> + +<pre> +@Override +protected void onStart() { + super.onStart(); // Always call the superclass method first + + // The activity is either being restarted or started for the first time + // so this is where we should make sure that GPS is enabled + LocationManager locationManager = + (LocationManager) getSystemService(Context.LOCATION_SERVICE); + boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); + + if (!gpsEnabled) { + // Create a dialog here that requests the user to enable GPS, and use an intent + // with the android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS action + // to take the user to the Settings screen to enable GPS when they click "OK" + } +} + +@Override +protected void onRestart() { + super.onRestart(); // Always call the superclass method first + + // Activity being restarted from stopped state +} +</pre> + + + + +<p>시스템이 액티비티를 소멸시킬 경우, {@link android.app.Activity}에 대해 {@link android.app.Activity#onDestroy()} +메서드를 호출합니다. {@link +android.app.Activity#onDestroy()}에 대한 호출을 수신하기 전에 {@link android.app.Activity#onStop()}을 사용하여 대부분의 +리소스를 해제했을 것이기 때문에 대부분의 앱의 경우 수행할 작업이 거의 없습니다. 이 메서드는 메모리 누수를 야기할 수 있는 리소스를 정리할 수 있는 +마지막 기회입니다. 이에 따라 +추가 스레드를 소멸시키고 메서드 추적과 같이 시간이 걸리는 기타 작업도 +정지되도록 할 수 있습니다.</p> + diff --git a/docs/html-intl/intl/ko/training/basics/data-storage/databases.jd b/docs/html-intl/intl/ko/training/basics/data-storage/databases.jd new file mode 100644 index 0000000..e7ca166 --- /dev/null +++ b/docs/html-intl/intl/ko/training/basics/data-storage/databases.jd @@ -0,0 +1,317 @@ +page.title=SQL 데이터베이스에 데이터 저장하기 +page.tags=data storage +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>이 과정에서 다루는 내용</h2> +<ol> + <li><a href="#DefineContract">스키마 및 계약 정의하기</a></li> + <li><a href="#DbHelper">SQL Helper를 사용하여 데이터베이스 생성하기</a></li> + <li><a href="#WriteDbRow">데이터베이스에 정보 삽입하기</a></li> + <li><a href="#ReadDbRow">데이터베이스에서 정보 읽어오기</a></li> + <li><a href="#DeleteDbRow">데이터베이스에서 정보 삭제하기</a></li> + <li><a href="#UpdateDbRow">데이터베이스 업데이트하기</a></li> +</ol> + +<h2>필독 항목</h2> +<ul> + <li><a href="{@docRoot}guide/topics/data/data-storage.html#db">데이터베이스 사용하기</a></li> +</ul> + +<!-- +<h2>Try it out</h2> + +<div class="download-box"> + <a href="{@docRoot}shareables/training/Sample.zip" class="button">Download the sample</a> + <p class="filename">Sample.zip</p> +</div> +--> + +</div> +</div> + + +<p>데이터베이스에 데이터를 저장하는 작업은 +연락처 정보와 같이 반복적이거나 구조적인 데이터에 이상적입니다. 이 클래스에서는 사용자가 SQL 데이터베이스에 대한 기본 사항에 +익숙하다는 전제하에 +Android에서 SQLite 데이터베이스를 시작하는 데 도움이 되는 유용한 정보를 제공합니다. Android에서 +데이터베이스를 사용할 때 필요한 API는 {@link android.database.sqlite} 패키지에 있습니다.</p> + + +<h2 id="DefineContract">스키마 및 계약 정의하기</h2> + +<p>SQL 데이터베이스의 기본 원칙 중 하나는 스키마입니다. 스키마는 데이터베이스의 구성 체계에 대한 공식적인 +선언입니다. 스키마는 개발자가 데이터베이스를 생성할 때 사용하는 SQL +문에 반영됩니다. <em>계약</em> 클래스라고 하는 +도우미 클래스를 생성하면 도움이 될 수 있습니다. 계약 클래스는 체계적이고 +자기 문서화 방식으로 스키마의 레이아웃을 명시적으로 지정합니다.</p> + +<p>계약 클래스는 URI, 테이블 및 +컬럼의 이름을 정의하는 상수를 유지하는 컨테이너입니다. 계약 클래스를 사용하면 동일한 패키지 내 +모든 클래스에 동일한 상수를 사용할 수 있습니다. 즉, 어느 한 장소에서 컬럼 +이름을 변경하면 코드 전체에 변경 사항이 반영됩니다.</p> + +<p>계약 클래스를 구성하는 좋은 방법은 클래스의 루트 레벨에 전체 데이터베이스에 +전역적인 정의를 추가하는 것입니다. 그런 다음 컬럼을 열거하는 각 테이블에 대해 내부 +클래스를 생성합니다.</p> + +<p class="note"><strong>참고:</strong> {@link +android.provider.BaseColumns} 인터페이스를 구현함으로써, 내부 클래스는 {@code _ID}라고 하는 기본 +키 필드를 상속할 수 있습니다. 커서 어댑터와 같은 일부 Android 클래스의 경우 +내부 클래스가 이러한 기본 키 필드를 가지고 있을 것이라 예상합니다. 내부 클래스는 반드시 필요한 것은 아니지만, 데이터베이스가 +Android 프레임워크와 조화롭게 작업하는 데 도움이 될 수 있습니다.</p> + +<p>예를 들어, 다음 스니펫은 테이블 이름과 +단일 테이블의 컬럼 이름을 정의합니다.</p> + + +<pre> +public final class FeedReaderContract { + // To prevent someone from accidentally instantiating the contract class, + // give it an empty constructor. + public FeedReaderContract() {} + + /* Inner class that defines the table contents */ + public static abstract class FeedEntry implements BaseColumns { + public static final String TABLE_NAME = "entry"; + public static final String COLUMN_NAME_ENTRY_ID = "entryid"; + public static final String COLUMN_NAME_TITLE = "title"; + public static final String COLUMN_NAME_SUBTITLE = "subtitle"; + ... + } +} +</pre> + + + +<h2 id="DbHelper">SQL Helper를 사용하여 데이터베이스 생성하기</h2> + +<p>데이터베이스의 모양을 정의한 후에는 데이터베이스 및 테이블을 생성 및 유지하는 +메서드를 구현해야 합니다. 다음은 테이블을 생성하고 삭제하는 몇 가지 일반적인 +명령문입니다.</P> + +<pre> +private static final String TEXT_TYPE = " TEXT"; +private static final String COMMA_SEP = ","; +private static final String SQL_CREATE_ENTRIES = + "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" + + FeedEntry._ID + " INTEGER PRIMARY KEY," + + FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP + + FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP + + ... // Any other options for the CREATE command + " )"; + +private static final String SQL_DELETE_ENTRIES = + "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME; +</pre> + +<p>기기의 <a href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">내부 +저장소</a>에 저장하는 파일과 같이, Android는 데이터베이스를 +애플리케이션과 관련된 개인 디스크 공간에 저장합니다. 기본적으로 이 공간은 다른 애플리케이션이 +액세스할 수 없기 때문에 저장된 데이터는 안전하게 유지됩니다.</p> + +<p>유용한 API 집합이 {@link +android.database.sqlite.SQLiteOpenHelper} 클래스에서 제공됩니다. +데이터베이스에 대한 참조를 가져오기 위해 이 클래스를 사용하는 경우, 시스템은 +필요한 경우에 한해서만 데이터베이스 생성 및 업데이트와 같이 +장시간 실행될 수 있는 작업을 +수행하며, <em>앱이 시작되는 동안에는 이러한 작업을 수행하지 않습니다</em>. +{@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} 또는 +{@link android.database.sqlite.SQLiteOpenHelper#getReadableDatabase}를 호출하기만 하면 됩니다.</p> + +<p class="note"><strong>참고:</strong> 이러한 작업은 장시간 실행될 수도 있기 때문에 +{@link android.os.AsyncTask} 또는 {@link android.app.IntentService}와 같이 백그라운드 스레드에서 {@link +android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} 또는 {@link +android.database.sqlite.SQLiteOpenHelper#getReadableDatabase}를 +호출해야 합니다.</p> + +<p>{@link android.database.sqlite.SQLiteOpenHelper}를 사용하려면 {@link +android.database.sqlite.SQLiteOpenHelper#onCreate onCreate()}, {@link +android.database.sqlite.SQLiteOpenHelper#onUpgrade onUpgrade()} 및 {@link +android.database.sqlite.SQLiteOpenHelper#onOpen onOpen()} 콜백 메서드를 +재정의하는 하위 클래스를 생성합니다. 반드시 필요한 것은 아니지만 +{@link android.database.sqlite.SQLiteOpenHelper#onDowngrade onDowngrade()}도 구현해야 하는 +경우가 있을 수 있습니다.</p> + +<p>다음은 위에 표시된 명령 중 일부를 사용하여 구현한 {@link +android.database.sqlite.SQLiteOpenHelper}의 예입니다.</p> + +<pre> +public class FeedReaderDbHelper extends SQLiteOpenHelper { + // If you change the database schema, you must increment the database version. + public static final int DATABASE_VERSION = 1; + public static final String DATABASE_NAME = "FeedReader.db"; + + public FeedReaderDbHelper(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + public void onCreate(SQLiteDatabase db) { + db.execSQL(SQL_CREATE_ENTRIES); + } + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + // This database is only a cache for online data, so its upgrade policy is + // to simply to discard the data and start over + db.execSQL(SQL_DELETE_ENTRIES); + onCreate(db); + } + public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { + onUpgrade(db, oldVersion, newVersion); + } +} +</pre> + +<p>데이터베이스에 액세스하려면 {@link +android.database.sqlite.SQLiteOpenHelper}의 하위 클래스를 인스턴스화합니다.</p> + +<pre> +FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext()); +</pre> + + + + +<h2 id="WriteDbRow">데이터베이스에 정보 삽입하기</h2> + +<p>{@link android.content.ContentValues} +개체를 {@link android.database.sqlite.SQLiteDatabase#insert insert()} 메서드에 전달하여 데이터를 데이터베이스에 삽입합니다.</p> + +<pre> +// Gets the data repository in write mode +SQLiteDatabase db = mDbHelper.getWritableDatabase(); + +// Create a new map of values, where column names are the keys +ContentValues values = new ContentValues(); +values.put(FeedEntry.COLUMN_NAME_ENTRY_ID, id); +values.put(FeedEntry.COLUMN_NAME_TITLE, title); +values.put(FeedEntry.COLUMN_NAME_CONTENT, content); + +// Insert the new row, returning the primary key value of the new row +long newRowId; +newRowId = db.insert( + FeedEntry.TABLE_NAME, + FeedEntry.COLUMN_NAME_NULLABLE, + values); +</pre> + +<p>{@link android.database.sqlite.SQLiteDatabase#insert insert()}의 +첫 번째 인수는 테이블 이름입니다. 두 번째 인수는 +컬럼 이름을 제공합니다. 프레임워크는 {@link android.content.ContentValues}가 비어있을 경우 +여기에 NULL을 삽입할 수 있습니다. 만약 이를 {@code "null"}로 설정할 경우, +값이 없으면 프레임워크가 행을 삽입하지 않습니다.</p> + + + + +<h2 id="ReadDbRow">데이터베이스에서 정보 읽어오기</h2> + +<p>데이터베이스로부터 정보를 읽어오려면 {@link android.database.sqlite.SQLiteDatabase#query query()} +메서드를 사용하고, 선택 기준과 원하는 컬럼을 전달합니다. +이 메서드는 {@link android.database.sqlite.SQLiteDatabase#insert insert()} +및 {@link android.database.sqlite.SQLiteDatabase#update update()} 요소를 결합합니다. 단, 삽입하려는 데이터보다는 가져오려는 데이터를 +정의하는 컬럼 목록은 예외입니다. 쿼리 결과는 +{@link android.database.Cursor} 개체로 반환됩니다.</p> + +<pre> +SQLiteDatabase db = mDbHelper.getReadableDatabase(); + +// Define a <em>projection</em> that specifies which columns from the database +// you will actually use after this query. +String[] projection = { + FeedEntry._ID, + FeedEntry.COLUMN_NAME_TITLE, + FeedEntry.COLUMN_NAME_UPDATED, + ... + }; + +// How you want the results sorted in the resulting Cursor +String sortOrder = + FeedEntry.COLUMN_NAME_UPDATED + " DESC"; + +Cursor c = db.query( + FeedEntry.TABLE_NAME, // The table to query + projection, // The columns to return + selection, // The columns for the WHERE clause + selectionArgs, // The values for the WHERE clause + null, // don't group the rows + null, // don't filter by row groups + sortOrder // The sort order + ); +</pre> + +<p>커서 안의 행을 보려면 {@link android.database.Cursor} 이동 +메서드 중 하나를 사용합니다. 이 메서드는 값을 읽어오기 전에 항상 먼저 호출해야 합니다. 일반적으로 결과의 +처음 항목에 "읽기 위치"를 배치하는 {@link android.database.Cursor#moveToFirst}를 먼저 +호출해야 합니다. {@link android.database.Cursor#getString +getString()} 또는 {@link android.database.Cursor#getLong getLong()}과 같은 {@link android.database.Cursor} 가져오기 메서드 +중 하나를 호출하여 각 행에 대한 컬럼 값을 읽어올 수 있습니다. 가져오기 메서드 각각에 대해 +원하는 컬럼의 인덱스 위치를 전달해야 하며, 이는 +{@link android.database.Cursor#getColumnIndex getColumnIndex()} 또는 +{@link android.database.Cursor#getColumnIndexOrThrow getColumnIndexOrThrow()}를 호출하여 가져올 수 있습니다. +예를 들면 다음과 같습니다.</p> + +<pre> +cursor.moveToFirst(); +long itemId = cursor.getLong( + cursor.getColumnIndexOrThrow(FeedEntry._ID) +); +</pre> + + + + +<h2 id="DeleteDbRow">데이터베이스에서 정보 삭제하기</h2> + +<p>테이블에서 행을 삭제하려면 행을 +식별하는 선택 기준을 제공해야 합니다. 데이터베이스 API는 SQL 삽입을 방지하는 선택 +기준을 생성하는 메커니즘을 제공합니다. 이 메커니즘은 +선택 사양을 선택 절과 선택 인수로 나눕니다. 절은 +보려는 컬럼을 정의하고, 이를 통해 컬럼 +테스트를 결합할 수 있습니다. 인수는 절 안에 묶여 테스트되는 값입니다. +이 결과는 일반 SQL 문과 같이 처리되지 않기 때문에 SQL 삽입의 +영향을 받지 않습니다.</p> + +<pre> +// Define 'where' part of query. +String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?"; +// Specify arguments in placeholder order. +String[] selectionArgs = { String.valueOf(rowId) }; +// Issue SQL statement. +db.delete(table_name, selection, selectionArgs); +</pre> + + + +<h2 id="UpdateDbRow">데이터베이스 업데이트하기</h2> + +<p>데이터베이스 값의 하위 세트를 수정해야 하는 경우, {@link +android.database.sqlite.SQLiteDatabase#update update()} 메서드를 사용합니다.</p> + +<p>테이블을 업데이트하면 {@link +android.database.sqlite.SQLiteDatabase#insert insert()}의 콘텐츠 값 구문과 +{@link android.database.sqlite.SQLiteDatabase#delete delete()}의 {@code where} 구문이 결합됩니다.</p> + +<pre> +SQLiteDatabase db = mDbHelper.getReadableDatabase(); + +// New value for one column +ContentValues values = new ContentValues(); +values.put(FeedEntry.COLUMN_NAME_TITLE, title); + +// Which row to update, based on the ID +String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?"; +String[] selectionArgs = { String.valueOf(rowId) }; + +int count = db.update( + FeedReaderDbHelper.FeedEntry.TABLE_NAME, + values, + selection, + selectionArgs); +</pre> + diff --git a/docs/html-intl/intl/ko/training/basics/data-storage/files.jd b/docs/html-intl/intl/ko/training/basics/data-storage/files.jd new file mode 100644 index 0000000..71652b5 --- /dev/null +++ b/docs/html-intl/intl/ko/training/basics/data-storage/files.jd @@ -0,0 +1,379 @@ +page.title=파일 저장하기 +page.tags=data storage +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>이 과정에서 다루는 내용</h2> +<ol> + <li><a href="#InternalVsExternalStorage">내부 또는 외부 저장소 선택하기</a></li> + <li><a href="#GetWritePermission">외부 저장소에 대한 권한 취득하기</a></li> + <li><a href="#WriteInternalStorage">내부 저장소에 파일 저장하기</a></li> + <li><a href="#WriteExternalStorage">외부 저장소에 파일 저장하기</a></li> + <li><a href="#GetFreeSpace">여유 공간 쿼리하기</a></li> + <li><a href="#DeleteFile">파일 삭제하기</a></li> +</ol> + +<h2>필독 항목</h2> +<ul> + <li><a href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">내부 + 저장소 사용하기</a></li> + <li><a href="{@docRoot}guide/topics/data/data-storage.html#filesExternal">외부 + 저장소 사용하기</a></li> +</ul> + +</div> +</div> + +<p>Android는 다른 플랫폼의 디스크 기반 파일 시스템과 +유사한 파일 시스템을 사용합니다. 이 과정에서는 Android 파일 시스템에서 {@link java.io.File} +API를 사용하여 파일을 읽고 쓰는 방법을 +설명합니다.</p> + +<p>{@link java.io.File} 개체는 대량의 데이터를 건너뛰지 않고 +처음부터 끝까지 순서대로 읽거나 쓸 때 적합합니다. 예를 들어 이미지 파일이나 +네트워크에서 교환되는 모든 항목에 적합합니다.</p> + +<p>이 과정에서는 앱에서 기본적인 파일 관련 작업을 수행하는 방법을 보여줍니다. +여기서는 사용자가 Linux 파일 시스템에 대한 기본 사항과 {@link java.io}의 +표준 파일 입출력 API에 익숙하다고 가정합니다.</p> + + +<h2 id="InternalVsExternalStorage">내부 또는 외부 저장소 선택하기</h2> + +<p>모든 Android 기기에는 "내부" 및 "외부" 저장소의 두 가지 파일 저장소 영역이 있습니다. 이 두 저장소의 이름은 +Android 초기에 만들어졌습니다. 당시 대부분의 기기가 비휘발성 내장 메모리(내부 저장소)와 +마이크로 SD 카드와 같은 이동식 저장 장치(외부 저장소)를 제공했습니다. +일부 기기는 이동식 저장 장치 없이도 영구 저장소 공간을 "내부" 그리고 "외부" 파티션으로 나누어 +항상 두 개의 저장소 공간을 제공하며, +API의 동작은 외부 저장소의 이동식 유무에 상관없이 일관적입니다. +다음 목록에서는 각각의 저장소 공간에 대한 특징을 요약하여 보여줍니다.</p> + +<div class="col-5" style="margin-left:0"> +<p><b>내부 저장소:</b></p> +<ul> +<li>항상 사용 가능합니다.</li> +<li>여기에 저장된 파일은 기본적으로 자신의 앱에서만 액세스할 수 있습니다.</li> +<li>사용자가 앱을 삭제하면 시스템이 +내장 저장소에서 앱의 모든 파일을 제거합니다.</li> +</ul> +<p>내부 저장소는 사용자와 다른 앱이 자신의 파일에 +액세스하는 것을 원치 않을 때 가장 적합합니다.</p> +</div> + +<div class="col-7" style="margin-right:0"> +<p><b>외부 저장소:</b></p> +<ul> +<li>항상 사용 가능하지는 않습니다. 그 이유는 사용자가 USB 저장소와 같은 외부 저장소를 마운트하고 +경우에 따라 기기에서 외부 저장소를 제거할 수 있기 때문입니다.</li> +<li>모든 사람이 읽을 수 있기 때문에 자신이 제어할 수 있는 범위 외부에서 다른 사람이 여기에 저장된 +파일을 읽을 수도 있습니다.</li> +<li>사용자가 앱을 삭제하면 {@link android.content.Context#getExternalFilesDir +getExternalFilesDir()}의 디렉터리에 저장한 앱 파일에 한해서 +시스템이 제거합니다.</li> +</ul> +<p>외부 저장소는 +액세스 제한이 필요치 않은 파일과 다른 앱과 공유하기 +원하는 파일 또는 사용자가 컴퓨터에서 액세스할 수 있도록 허용하는 파일에 적합합니다.</p> +</div> + + +<p class="note" style="clear:both"> +<strong>팁:</strong> 기본적으로 +앱은 내부 저장소에 설치되지만 매니페스트에 <a href="{@docRoot}guide/topics/manifest/manifest-element.html#install">{@code +android:installLocation}</a> 특성을 지정하여 외부 저장소에 +설치할 수도 있습니다. 사용자는 APK 크기가 매우 크고 내부 저장소 공간보다 +외부 저장소 공간이 더 클 때 이 옵션을 유용하게 활용할 수 있습니다. 자세한 +내용은 <a href="{@docRoot}guide/topics/data/install-location.html">앱 설치 위치</a>를 참조하세요.</p> + + +<h2 id="GetWritePermission">외부 저장소에 대한 권한 취득하기</h2> + +<p>외부 저장소에 데이터를 쓰려면 <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">매니페스트 파일</a>에서 +{@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} 권한을 요청해야 합니다.</p> + +<pre> +<manifest ...> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + ... +</manifest> +</pre> + +<div class="caution"><p><strong>주의:</strong> +현재는 모든 앱에서 특별한 권한 없이도 외부 저장소를 +읽을 수 있습니다. 하지만 이는 향후 릴리스에서 바뀔 예정입니다. 앱이 +외부 저장소에 데이터를 쓰지는 않고 읽어야만 해야 할 경우, {@link +android.Manifest.permission#READ_EXTERNAL_STORAGE} 권한을 선언해야 합니다. 앱이 예상한 대로 계속 +작동하도록 하려면 변경 내용이 적용되기 전에 지금 이 권한을 선언해야 합니다.</p> +<pre> +<manifest ...> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + ... +</manifest> +</pre> +<p>하지만 앱이 {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} +권한을 사용하는 경우, 외부 저장소를 읽을 수 있는 권한도 묵시적으로 포함되어 있습니다.</p> +</div> + +<p>내부 +저장소에 파일을 저장할 때는 어떠한 권한도 필요하지 않습니다. 애플리케이션은 내부 저장소 디렉터리에 있는 파일을 항상 읽고 +쓸 수 있는 권한을 가집니다.</p> + + + + + +<h2 id="WriteInternalStorage">내부 저장소에 파일 저장하기</h2> + +<p>내부 저장소에 파일을 저장할 경우, 다음 두 메서드 중 하나를 호출하여 적합한 디렉터리를 +{@link java.io.File}로 얻을 수 있습니다.</p> + +<dl> + <dt>{@link android.content.Context#getFilesDir}</dt> + <dd>앱에 대한 내부 디렉터리를 나타내는 {@link java.io.File}을 반환합니다.</dd> + <dt>{@link android.content.Context#getCacheDir}</dt> + <dd>앱의 임시 +캐시 파일에 대한 내부 디렉터리를 나타내는 {@link java.io.File}을 반환합니다. 더 이상 +필요하지 않은 파일은 모두 삭제하고 언제든지 +사용할 수 있는 메모리 크기에 합리적인 크기 제한(예. 1MB)을 구현해야 합니다. 저장 공간이 부족해지기 시작하면 경고 없이 시스템이 캐시 파일을 +삭제할 수도 있습니다.</dd> +</dl> + +<p>이러한 디렉터리 중 하나에서 새 파일을 생성하려면 {@link +java.io.File#File(File,String) File()} 생성자를 사용하고 내부 저장소 디렉터리를 지정하는 상기 메서드 중 +하나를 통해 제공되는 {@link java.io.File}을 전달하면 됩니다. 예를 들면 다음과 같습니다.</p> + +<pre> +File file = new File(context.getFilesDir(), filename); +</pre> + +<p>또한, {@link +android.content.Context#openFileOutput openFileOutput()}을 호출하여 내부 디렉터리의 파일에 데이터를 쓰는 +{@link java.io.FileOutputStream}을 가져올 수도 있습니다. 다음 예는 +몇몇 텍스트를 파일에 쓰는 방법을 보여줍니다.</p> + +<pre> +String filename = "myfile"; +String string = "Hello world!"; +FileOutputStream outputStream; + +try { + outputStream = openFileOutput(filename, Context.MODE_PRIVATE); + outputStream.write(string.getBytes()); + outputStream.close(); +} catch (Exception e) { + e.printStackTrace(); +} +</pre> + +<p>또는, 파일을 캐싱해야 할 경우 {@link +java.io.File#createTempFile createTempFile()}을 대신 사용합니다. 예를 들어 다음 메서드는 {@link java.net.URL}에서 +파일 이름을 추출한 후 해당 이름을 사용하여 +앱의 내부 캐시 디렉터리에 파일을 생성합니다.</p> + +<pre> +public File getTempFile(Context context, String url) { + File file; + try { + String fileName = Uri.parse(url).getLastPathSegment(); + file = File.createTempFile(fileName, null, context.getCacheDir()); + catch (IOException e) { + // Error while creating file + } + return file; +} +</pre> + +<p class="note"><strong>참고:</strong> +앱의 내부 저장소 디렉터리는 Android 파일 시스템의 특별한 위치에 있는 앱의 패키지 이름으로 +지정됩니다. +엄밀히 말하면, 파일 모드를 +읽기 가능으로 설정할 경우 다른 앱이 내부 파일을 읽을 수 있습니다. 하지만 이는 다른 앱도 여러분 자신의 앱 패키지 +이름 및 파일 이름을 알아야 가능합니다. 다른 앱은 여러분 자신의 내부 디렉터리를 탐색할 수 없으며 명시적으로 +읽기 가능 및 쓰기 가능으로 파일을 설정하지 않으면 파일을 읽거나 쓸 수 없습니다. 따라서 +{@link android.content.Context#MODE_PRIVATE}을 내부 저장소 내 파일에 사용하는 한, +다른 앱이 이러한 파일에 액세스할 수 없습니다.</p> + + + + + +<h2 id="WriteExternalStorage">외부 저장소에 파일 저장하기</h2> + +<p>사용자가 +외부 저장소를 PC에 마운트했거나 외부 저장소를 제공하는 SD 카드를 제거한 경우 등에는 외부 저장소를 사용할 수 없기 때문에 액세스하기 전 +항상 볼륨이 사용 가능한지 확인해야 합니다. {@link android.os.Environment#getExternalStorageState}를 호출하여 외부 +저장소 상태를 쿼리할 수 있습니다. 반환된 +상태가 {@link android.os.Environment#MEDIA_MOUNTED}와 동일하다면 파일을 읽거나 파일에 +쓸 수 있습니다. 예를 들어 다음 메서드는 저장소의 +가용성을 확인하는 데 유용합니다.</p> + +<pre> +/* Checks if external storage is available for read and write */ +public boolean isExternalStorageWritable() { + String state = Environment.getExternalStorageState(); + if (Environment.MEDIA_MOUNTED.equals(state)) { + return true; + } + return false; +} + +/* Checks if external storage is available to at least read */ +public boolean isExternalStorageReadable() { + String state = Environment.getExternalStorageState(); + if (Environment.MEDIA_MOUNTED.equals(state) || + Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { + return true; + } + return false; +} +</pre> + +<p>외부 저장소는 사용자 및 다른 앱에 의해 수정될 수 있으나, 여기에 저장할 수 있는 파일로는 두 가지 +범주가 있습니다.</p> + +<dl> + <dt>공용 파일</dt> + <dd>다른 앱 및 사용자가 자유롭게 사용할 수 있는 +파일입니다. 사용자가 앱을 제거해도 사용자는 +이러한 파일을 여전히 사용할 수 있습니다. + <p>예를 들어 앱으로 캡처된 사진 또는 기타 다운로드된 파일이 이에 해당합니다.</p> + </dd> + <dt>개인 파일</dt> + <dd>앱에 속한 파일이며, 사용자가 +앱을 제거하면 같이 삭제됩니다. 이러한 파일은 +엄밀히 말해 외부 저장소에 저장된 파일이기 때문에 사용자 및 다른 앱의 액세스가 가능하긴 하지만, 앱 외부에서 +사용자에게 값을 실제로 제공하지는 않습니다. 사용자가 앱을 제거하면 앱의 외부 개인 디렉터리 내 모든 파일을 시스템에서 +삭제합니다. + <p>예를 들어 앱에서 다운로드한 추가 리소스 또는 임시 미디어 파일이 이에 해당합니다.</p> + </dd> +</dl> + +<p>외부 저장소에 공용 파일을 저장하려는 경우 +{@link android.os.Environment#getExternalStoragePublicDirectory +getExternalStoragePublicDirectory()} 메서드를 사용하여 외부 저장소에 적절한 디렉터리를 나타내는 +{@link java.io.File}을 가져옵니다. 이 메서드는 {@link android.os.Environment#DIRECTORY_MUSIC} 또는 {@link +android.os.Environment#DIRECTORY_PICTURES}와 같은 다른 공개 +파일과 논리적으로 구성될 수 있도록 저장하고자 하는 파일의 유형을 +지정하는 인수를 받습니다. 예를 들면 다음과 같습니다.</p> + +<pre> +public File getAlbumStorageDir(String albumName) { + // Get the directory for the user's public pictures directory. + File file = new File(Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_PICTURES), albumName); + if (!file.mkdirs()) { + Log.e(LOG_TAG, "Directory not created"); + } + return file; +} +</pre> + + +<p>개인 파일을 앱에 저장하고자 하는 경우, {@link +android.content.Context#getExternalFilesDir getExternalFilesDir()}을 호출하고 원하는 디렉터리 유형을 +나타내는 이름을 전달하여 +적절한 디렉터리를 얻을 수 있습니다. 이런 식으로 생성된 각 디렉터리는 부모 +디렉터리에 추가됩니다. 이 디렉터리는 +사용자가 앱을 제거할 때 시스템이 삭제하는 앱의 모든 외부 저장소 파일을 캡슐화합니다.</p> + +<p>예를 들어, 다음은 개인 사진 앨범을 위한 디렉터리 생성 시 사용 가능한 메서드입니다.</p> + +<pre> +public File getAlbumStorageDir(Context context, String albumName) { + // Get the directory for the app's private pictures directory. + File file = new File(context.getExternalFilesDir( + Environment.DIRECTORY_PICTURES), albumName); + if (!file.mkdirs()) { + Log.e(LOG_TAG, "Directory not created"); + } + return file; +} +</pre> + +<p>미리 정의된 하위 디렉터리 이름 중 파일에 알맞은 이름이 없을 경우 대신 {@link +android.content.Context#getExternalFilesDir getExternalFilesDir()}을 호출하고 {@code null}을 전달할 수 있습니다. 그러면 +앱의 외부 저장소 내 개인 디렉터리의 루트 디렉터리가 반환됩니다.</p> + +<p>사용자가 앱을 제거할 때 {@link android.content.Context#getExternalFilesDir getExternalFilesDir()}이 +삭제된 디렉터리 내에 디렉터리를 생성한다는 것을 기억하세요. +앱이 카메라이고 사용자가 사진을 계속 간직하고자 하는 경우처럼, 사용자가 +앱을 제거한 후에도 본인이 저장하는 파일을 사용 가능하게 유지해야 하는 경우 {@link android.os.Environment#getExternalStoragePublicDirectory +getExternalStoragePublicDirectory()}를 +대신 사용해야 합니다.</p> + + +<p>공유 파일에 {@link +android.os.Environment#getExternalStoragePublicDirectory +getExternalStoragePublicDirectory()}를 사용하든, +개인 파일에 {@link android.content.Context#getExternalFilesDir +getExternalFilesDir()}을 사용하든지에 관계없이 {@link android.os.Environment#DIRECTORY_PICTURES}와 +같이 API 상수로 제공되는 +디렉터리 이름을 사용해야 합니다. 이러한 디렉터리 이름은 +시스템이 파일을 적절하게 처리할 수 있게 해줍니다. 예를 들어 {@link +android.os.Environment#DIRECTORY_RINGTONES}에 저장된 파일은 시스템 미디어 스캐너에 의해 음악 +대신 벨소리로 분류됩니다.</p> + + + + +<h2 id="GetFreeSpace">여유 공간 쿼리하기</h2> + +<p>저장하는 데이터의 크기를 미리 알고 있을 경우, {@link java.io.File#getFreeSpace} 또는 {@link +java.io.File#getTotalSpace}를 호출하여 {@link +java.io.IOException}을 초래하지 않고 사용 공간이 충분한지 +확인할 수 있습니다. 이러한 메서드는 각각 저장소 볼륨에서 현재 사용 가능한 공간 및 +전체 공간을 알려줍니다. 이 정보는 일정 임계치를 초과하는 수준으로 +저장소 볼륨이 차는 것을 방지하는 데도 유용합니다.</p> + +<p>하지만 시스템은 {@link java.io.File#getFreeSpace}로 +지정된 만큼의 바이트를 쓸 수 있다고 보장하지 않습니다. 저장하고자 하는 데이터의 크기보다 반환된 숫자가 +몇 MB 더 클 경우 또는 파일 시스템이 90% +미만으로 찼을 경우 안심하고 작업을 진행할 수 있습니다. +그렇지 않다면 저장소에 데이터를 쓰지 않는 것이 좋습니다.</p> + +<p class="note"><strong>참고:</strong> 파일을 저장하기 전에 +사용 가능한 공간을 꼭 확인할 필요는 없습니다. 대신, 파일을 곧바로 쓴 후 {@link java.io.IOException}이 발생하는 경우 이를 +캐치하면 됩니다. 필요한 공간을 정확히 모르는 경우 +이러한 방법을 사용할 수 있습니다. 예를 들어 파일을 저장하기 전에 PNG 이미지를 +JPEG로 변환하여 파일 인코딩을 +변경하는 경우, 파일의 크기를 사전에 알 수 없습니다.</p> + + + + +<h2 id="DeleteFile">파일 삭제하기</h2> + +<p>더 이상 필요하지 않은 파일은 항상 삭제해야 합니다. 파일을 삭제하는 가장 간단한 방법은 열린 +파일 참조가 {@link java.io.File#delete}를 직접 호출하도록 하는 것입니다.</p> + +<pre> +myFile.delete(); +</pre> + +<p>파일이 내부 저장소에 저장되어 있는 경우, {@link android.content.Context}에 위치를 요청하고 {@link android.content.Context#deleteFile deleteFile()}을 호출하여 파일을 +삭제할 수도 있습니다.</p> + +<pre> +myContext.deleteFile(fileName); +</pre> + +<div class="note"> +<p><strong>참고:</strong> 사용자가 앱을 제거하면 Android 시스템이 +다음 항목을 삭제합니다.</p> +<ul> +<li>내부 저장소에 저장한 모든 파일</li> +<li>{@link +android.content.Context#getExternalFilesDir getExternalFilesDir()}을 사용해 외부 저장소에 저장한 모든 파일</li> +</ul> +<p>하지만 +{@link android.content.Context#getCacheDir()}로 생성된 모든 캐시 파일과 더 이상 필요치 않은 +다른 파일은 정기적으로 직접 삭제해야 합니다.</p> +</div> + diff --git a/docs/html-intl/intl/ko/training/basics/data-storage/index.jd b/docs/html-intl/intl/ko/training/basics/data-storage/index.jd new file mode 100644 index 0000000..d6c118d --- /dev/null +++ b/docs/html-intl/intl/ko/training/basics/data-storage/index.jd @@ -0,0 +1,57 @@ +page.title=데이터 저장하기 +page.tags=data storage,files,sql,database,preferences +helpoutsWidget=true + +trainingnavtop=true +startpage=true + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>종속관계 및 전제조건</h2> +<ul> + <li>Android 1.6(API 레벨 4) 또는 그 이상</li> + <li>맵 키-값 컬렉션에 정통한 지식</li> + <li>Java file I/O API에 정통한 지식</li> + <li>SQL 데이터베이스에 정통한 지식</li> +</ul> + +<h2>필독 항목</h2> +<ul> + <li><a href="{@docRoot}guide/topics/data/data-storage.html">저장소 옵션</a></li> +</ul> + +</div> +</div> + +<p>대부분의 Android 앱은 데이터를 저장해야 합니다. 하다못해 {@link android.app.Activity#onPause onPause()} 동안에도 사용자의 진행 상태가 손실되지 않도록 +앱 상태에 대한 정보만이라도 저장합니다. 또한, 대부분의 +단순하지 않은 앱은 사용자 설정을 저장해야 하며, 어떤 앱은 파일 및 데이터베이스에서 대량의 +정보를 관리해야 합니다. 이 클래스에서는 다음 사항을 포함해 Android의 +주요 데이터 저장소 옵션에 대해 소개합니다.</p> + +<ul> + <li>공유 +기본 설정 파일에 간단한 데이터 유형의 키-값 쌍 저장하기</li> + <li>Android 파일 시스템에 임의의 파일 저장하기</li> + <li>SQLite로 관리되는 데이터베이스 사용하기</li> +</ul> + + +<h2>과정</h2> + +<dl> + <dt><b><a href="shared-preferences.html">키-값 세트 저장하기</a></b></dt> + <dd>키-값 쌍 +내에 소량의 정보를 저장하기 위해 공유 기본 설정 파일을 사용하는 방법을 설명합니다.</dd> + + <dt><b><a href="files.html">파일 저장하기</a></b></dt> + <dd>일반적으로 순서대로 읽혀지는 +긴 데이터 시퀀스를 저장하는 것과 같이 기본적인 파일 저장에 대해 설명합니다.</dd> + + <dt><b><a href="databases.html">SQL 데이터베이스에 데이터 저장하기</a></b></dt> + <dd>SQLite 데이터베이스를 사용하여 구조화된 데이터를 읽고 쓰는 방법에 대해 설명합니다.</dd> + +</dl> diff --git a/docs/html-intl/intl/ko/training/basics/data-storage/shared-preferences.jd b/docs/html-intl/intl/ko/training/basics/data-storage/shared-preferences.jd new file mode 100644 index 0000000..f47d7e7 --- /dev/null +++ b/docs/html-intl/intl/ko/training/basics/data-storage/shared-preferences.jd @@ -0,0 +1,120 @@ +page.title=키-값 세트 저장하기 +page.tags=data storage +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>이 과정에서 다루는 내용</h2> +<ol> + <li><a href="#GetSharedPreferences">SharedPreferences에 대한 핸들 가져오기</a></li> + <li><a href="#WriteSharedPreference">SharedPreferences에 쓰기</a></li> + <li><a href="#ReadSharedPreference">SharedPreferences에서 읽어오기</a></li> +</ol> + +<h2>필독 항목</h2> +<ul> + <li><a href="{@docRoot}guide/topics/data/data-storage.html#pref">SharedPreferences 사용하기</a></li> +</ul> + +</div> +</div> + + +<p>저장하고자 하는 키-값 컬렉션이 비교적 작은 경우, +{@link android.content.SharedPreferences} API를 사용합니다. +{@link android.content.SharedPreferences} 개체는 +키-값 쌍을 포함하는 파일을 가리키며, 키-값 쌍을 읽고 쓸 수 있는 간단한 메서드를 제공합니다. 각 +{@link android.content.SharedPreferences} 파일은 프레임워크로 +관리되며, 전용 또는 공유일 수 있습니다.</p> + +<p>이 클래스에서는 {@link android.content.SharedPreferences} API를 사용하여 단순한 값을 저장하고 +가져오는 방법을 보여줍니다.</p> + +<p class="note"><strong>참고:</strong> {@link android.content.SharedPreferences} API는 +키-값 쌍을 읽고 쓰는 용도로만 사용됩니다. 또한, (앱 설정을 저장하는 데 대한 구현으로 {@link android.content.SharedPreferences}를 +사용하기는 하지만) 앱 설정을 위한 +사용자 인터페이스 구축을 지원하는 +{@link android.preference.Preference} API와 혼동해서는 안 됩니다. {@link +android.preference.Preference} API 사용에 대한 자세한 내용은 <a href="{@docRoot}guide/topics/ui/settings.html">설정</a> 가이드를 참조하세요.</p> + +<h2 id="GetSharedPreferences">SharedPreferences에 대한 핸들 가져오기</h2> + +<p>다음 두 메서드 중 +하나를 호출하여 새로운 공유 기본 설정 파일을 생성하거나 기존 파일에 액세스할 수 있습니다.</p> +<ul> + <li>{@link android.content.Context#getSharedPreferences(String,int) +getSharedPreferences()} — 여러 공유 기본 설정 파일이 필요한 경우 이 메서드를 사용합니다. 이러한 파일은 첫 번째 매개변수로 지정하는 +이름으로 식별됩니다. 앱 내부의 모든 +{@link android.content.Context}에서 이 메서드를 호출할 수 있습니다.</li> + <li>{@link android.app.Activity#getPreferences(int) getPreferences()} — 액티비티에 +하나의 공유 기본 설정 파일만 사용해야 하는 경우 +{@link android.app.Activity}에서 이 메서드를 사용합니다. 이 메서드는 액티비티에 속한 기본 공유 +기본 설정 파일을 가져오기 때문에 이름을 제공할 필요가 없습니다.</li> +</ul> + +<p>예를 들어 다음 코드는 {@link android.app.Fragment} 내부에서 실행됩니다. +이 코드는 리소스 문자열 {@code R.string.preference_file_key}에 의해 +식별되는 공유 기본 설정 파일에 액세스하며, 본인의 앱만 파일에 액세스할 수 있도록 전용 모드에서 +파일을 엽니다.</p> + +<pre> +Context context = getActivity(); +SharedPreferences sharedPref = context.getSharedPreferences( + getString(R.string.preference_file_key), Context.MODE_PRIVATE); +</pre> + +<p>공유 기본 설정 파일의 이름을 지정할 때는 {@code "com.example.myapp.PREFERENCE_FILE_KEY"}와 같이 앱에 +대한 고유 식별 이름을 사용해야 합니다.</p> + +<p>또는, 액티비티에 공유 기본 설정 파일만 필요할 경우 +{@link android.app.Activity#getPreferences(int) getPreferences()} 메서드를 사용할 수 있습니다.</p> + +<pre> +SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE); +</pre> + +<p class="caution"><strong>주의:</strong> {@link android.content.Context#MODE_WORLD_READABLE} 또는 {@link +android.content.Context#MODE_WORLD_WRITEABLE}을 +사용하여 공유 기본 설정 파일을 생성하는 경우, 파일 식별자를 인식하는 기타 모든 앱이 +데이터에 액세스할 수 있습니다.</p> + + +<h2 id="WriteSharedPreference">SharedPreferences에 쓰기</h2> + +<p>공유 기본 설정 파일에 데이터를 쓰려면 {@link android.content.SharedPreferences}에서 {@link +android.content.SharedPreferences#edit}를 호출하여 {@link +android.content.SharedPreferences.Editor}를 생성해야 합니다.</p> + +<p>{@link +android.content.SharedPreferences.Editor#putInt putInt()} 및 {@link +android.content.SharedPreferences.Editor#putString putString()}과 같은 메서드를 사용하여 쓰고자 하는 키와 값을 전달합니다. 그런 다음 {@link +android.content.SharedPreferences.Editor#commit}을 호출하여 변경 내용을 저장합니다. 예를 들면 다음과 같습니다.</p> + +<pre> +SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE); +SharedPreferences.Editor editor = sharedPref.edit(); +editor.putInt(getString(R.string.saved_high_score), newHighScore); +editor.commit(); +</pre> + + +<h2 id="ReadSharedPreference">SharedPreferences에서 읽어오기</h2> + +<p>공유 기본 설정 파일에서 값을 가져오려면 원하는 값에 +대한 키를 제공하고 원하는 경우 키가 +없을 경우 반환할 기본값을 제공하여 {@link +android.content.SharedPreferences#getInt getInt()} 및 {@link +android.content.SharedPreferences#getString getString()}과 같은 메서드를 호출해야 합니다. 예를 들면 다음과 같습니다.</p> + +<pre> +SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE); +int defaultValue = getResources().getInteger(R.string.saved_high_score_default); +long highScore = sharedPref.getInt(getString(R.string.saved_high_score), defaultValue); +</pre> + diff --git a/docs/html-intl/intl/ko/training/basics/intents/filters.jd b/docs/html-intl/intl/ko/training/basics/intents/filters.jd new file mode 100644 index 0000000..265534c --- /dev/null +++ b/docs/html-intl/intl/ko/training/basics/intents/filters.jd @@ -0,0 +1,236 @@ +page.title=다른 앱이 자신의 액티비티를 시작하도록 허용하기 +page.tags=intents +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> + <div id="tb"> + +<h2>이 과정에서 다루는 내용</h2> +<ol> + <li><a href="#AddIntentFilter">인텐트 필터 추가하기</a></li> + <li><a href="#HandleIntent">액티비티에서 인텐트 처리하기</a></li> + <li><a href="#ReturnResult">결과 반환하기</a></li> +</ol> + +<h2>필독 항목</h2> +<ul> + <li><a href="{@docRoot}training/sharing/index.html">간단한 데이터 공유하기</a></li> + <li><a href="{@docRoot}training/secure-file-sharing/index.html">파일 공유하기</a> +</ul> + </div> +</div> + +<p>앞의 두 과정은 이야기의 한 면인 자신의 앱에서 +다른 앱의 액티비티를 시작하는 것에 초점을 뒀습니다. 그러나 자신의 앱이 다른 앱에 유용할 수 있는 작업을 수행할 수 있는 경우 +다른 앱의 작업 요청에 응답할 수 있도록 준비되어 있어야 합니다. 예를 들어, 사용자가 +친구와 메시지 또는 사진을 공유할 수 있는 소셜 앱을 만드는 경우 사용자가 다른 앱에서 +"공유" 작업을 시작하고 이 작업을 수행하기 위해 여러분 자신의 앱을 시작할 수 있도록 +{@link android.content.Intent#ACTION_SEND} 인텐트를 지원하는 것이 좋습니다.</p> + +<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>앱이 기기에 설치되면 시스템이 인텐트 +필터를 식별한 후 설치된 모든 앱에서 지원되는 인텐트의 내부 카탈로그에 해당 정보를 추가합니다. +앱이 암묵적인 인텐트를 사용하여 {@link android.app.Activity#startActivity +startActivity()} 또는 {@link android.app.Activity#startActivityForResult startActivityForResult()}를 +호출하면 시스템이 인텐트에 응답 가능한 액티비티(또는 여러 액티비티)를 +찾습니다.</p> + + + +<h2 id="AddIntentFilter">인텐트 필터 추가하기</h2> + +<p>액티비티가 처리 가능한 인텐트를 올바르게 정의하려면 액티비티가 +받아들이는 데이터와 작업 유형 측면에서 +추가하는 인텐트 필터가 최대한 구체적이어야 합니다.</p> + +<p>액티비티의 인텐트 필터가 {@link android.content.Intent} 개체의 다음 기준을 충족할 +경우, 시스템이 주어진 {@link android.content.Intent}를 해당 액티비티에 보낼 수 있습니다.</p> + +<dl> + <dt>작업</dt> + <dd>수행할 작업의 이름을 지정하는 문자열입니다. 일반적으로, 플랫폼에서 정의하는 값 중 +하나입니다(예: {@link android.content.Intent#ACTION_SEND} 또는 {@link android.content.Intent#ACTION_VIEW}). + <p><a href="{@docRoot}guide/topics/manifest/action-element.html">{@code <action>}</a> 요소를 사용하여 인텐트 필터에 지정합니다. +이 요소에 지정하는 값은 API 상수 대신 +작업의 전체 문자열 이름이어야 합니다(다음 예제 참조).</p></dd> + + <dt>데이터</dt> + <dd>인텐트와 관련된 데이터에 대한 설명입니다. + <p><a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a> 요소를 사용하여 인텐트 필터에 지정합니다. 이 요소에서 +하나 이상의 특성을 사용하여 MIME 유형, URI 접두사, URI 스키마, 또는 +이들의 조합 그리고 수락된 데이터 유형을 나타내는 다른 요소들을 지정할 수 +있습니다.</p> + <p class="note"><strong>참고:</strong> 액티비티가 URI가 아닌 +다른 종류의 "엑스트라" 데이터를 처리할 때와 같이, 데이터 +{@link android.net.Uri}에 대한 세부사항을 선언할 필요가 없는 경우, {@code text/plain} 또는 {@code image/jpeg}와 같이 액티비티가 처리하는 데이터 +유형을 선언하는 데 {@code android:mimeType} 특성만 지정하면 됩니다.</p> +</dd> + <dt>카테고리</dt> + <dd>인텐트를 처리하는 액티비티의 특징을 지정할 수 있는 추가적인 방법을 제공합니다. 일반적으로, 사용자 제스처 또는 이러한 제스처가 시작된 위치와 +관련되어 있습니다. 시스템이 지원하는 카테고리는 +여러 가지가 있지만 대부분은 거의 사용되지 않습니다. 하지만 모든 암묵적인 인텐트는 기본적으로 +{@link android.content.Intent#CATEGORY_DEFAULT}로 정의됩니다. + <p><a href="{@docRoot}guide/topics/manifest/category-element.html">{@code <category>}</a> 요소를 사용하여 인텐트 필터에 +지정합니다.</p></dd> +</dl> + +<p>인텐트 필터에서 액티비티가 허용하는 기준을 선언할 수 있습니다. +이는 이러한 기준 각각을 <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a> 요소 내에 해당 XML 요소를 중첩하여 선언하면 +가능합니다.</p> + +<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"/> + <data android:mimeType="image/*"/> + </intent-filter> +</activity> +</pre> + +<p>수신되는 인텐트는 각각 하나의 작업 및 하나의 데이터 유형만 지정합니다. 하지만 +<a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a> 각각에 <a href="{@docRoot}guide/topics/manifest/action-element.html">{@code +<action>}</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>작업 및 데이터의 두 쌍이 상호 배타적으로 +동작할 경우, 어떤 데이터 유형과 페어링되었을 때 어떤 작업이 허용 가능한지를 지정하는 +인텐트 필터를 각각 따로 생성해야 합니다.</p> + +<p>예를 들어 액티비티가 {@link +android.content.Intent#ACTION_SEND} 및 {@link +android.content.Intent#ACTION_SENDTO} 인텐트 모두에서 텍스트와 이미지 모두를 처리한다고 가정합니다. 이런 경우, 두 작업 각각에 별도의 +인텐트 필터를 정의해야 합니다. 그 이유는 {@link +android.content.Intent#ACTION_SENDTO} 인텐트는 데이터 {@link android.net.Uri}를 사용해서 {@code send} 또는 {@code sendto} URI 스키마를 사용하는 +수신자 주소를 지정해야 하기 때문입니다. 예를 들면 다음과 같습니다.</p> + +<pre> +<activity android:name="ShareActivity"> + <!-- filter for sending text; accepts SENDTO action with sms URI schemes --> + <intent-filter> + <action android:name="android.intent.action.SENDTO"/> + <category android:name="android.intent.category.DEFAULT"/> + <data android:scheme="sms" /> + <data android:scheme="smsto" /> + </intent-filter> + <!-- filter for sending text or images; accepts SEND action and text or image data --> + <intent-filter> + <action android:name="android.intent.action.SEND"/> + <category android:name="android.intent.category.DEFAULT"/> + <data android:mimeType="image/*"/> + <data android:mimeType="text/plain"/> + </intent-filter> +</activity> +</pre> + +<p class="note"><strong>참고:</strong> 암묵적인 인텐트를 수신하기 위해서는 +{@link android.content.Intent#CATEGORY_DEFAULT} 카테고리를 인텐트 필터에 포함해야 합니다. {@link +android.app.Activity#startActivity startActivity()} 및 {@link +android.app.Activity#startActivityForResult startActivityForResult()} 메서드는 {@link android.content.Intent#CATEGORY_DEFAULT} 카테고리를 +선언한 것으로 모든 인텐트를 간주합니다. 인텐트 필터에서 이 카테고리를 +선언하지 않으면 어떠한 암묵적인 인텐트도 액티비티로 확인되지 않습니다.</p> + +<p>소셜 공유 동작을 수행하는 {@link android.content.Intent#ACTION_SEND} +인텐트의 송수신에 대한 자세한 내용은 <a href="{@docRoot}training/sharing/receive.html">다른 앱에서 간단한 데이터 수신하기</a> 과정을 참조하세요.</p> + + +<h2 id="HandleIntent">액티비티에서 인텐트 처리하기</h2> + +<p>액티비티를 시작하는 데 사용된 {@link +android.content.Intent}를 읽어 액티비티에서 취할 작업을 결정할 수 있습니다.</p> + +<p>액티비티가 시작되면, {@link android.app.Activity#getIntent()}를 호출하여 +액티비티를 시작한 {@link android.content.Intent}를 검색합니다. 이 작업은 액티비티의 +수명 주기 동안 언제든지 가능하지만, 일반적으로 +{@link android.app.Activity#onCreate onCreate()} 또는 {@link android.app.Activity#onStart()}와 같은 초기 콜백 과정에서 수행합니다.</p> + +<p>예를 들면 다음과 같습니다.</p> + +<pre> +@Override +protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.main); + + // Get the intent that started this activity + Intent intent = getIntent(); + Uri data = intent.getData(); + + // Figure out what to do based on the intent type + if (intent.getType().indexOf("image/") != -1) { + // Handle intents with image data ... + } else if (intent.getType().equals("text/plain")) { + // Handle intents with text ... + } +} +</pre> + + +<h2 id="ReturnResult">결과 반환하기</h2> + +<p>액티비티를 호출한 액티비티로 결과를 반환하고자 하는 경우, 간단하게 {@link +android.app.Activity#setResult(int,Intent) setResult()}를 호출하여 결과 코드 및 결과 {@link +android.content.Intent}를 지정하면 됩니다. 작업이 끝나고 사용자가 원래 +액티비티로 되돌아갈 경우, {@link android.app.Activity#finish()}를 호출하여 액티비티를 종료(및 소멸)합니다. 예를 들면 +다음과 같습니다.</p> + +<pre> +// Create intent to deliver some kind of result data +Intent result = new Intent("com.example.RESULT_ACTION", Uri.parse("content://result_uri"); +setResult(Activity.RESULT_OK, result); +finish(); +</pre> + +<p>결과 코드는 항상 결과와 함께 지정해야 합니다. 일반적으로 {@link +android.app.Activity#RESULT_OK} 또는 {@link android.app.Activity#RESULT_CANCELED}입니다. 그런 다음 필요한 경우 {@link android.content.Intent}를 사용하여 추가 데이터를 +제공할 수 있습니다.</p> + +<p class="note"><strong>참고:</strong> 결과는 기본적으로 {@link +android.app.Activity#RESULT_CANCELED}로 설정됩니다. 따라서 작업을 완료하기 전과 +개발자가 결과를 설정하기 전에 사용자가 <em>뒤로</em> 버튼을 누를 경우, 원래의 액티비티는 +"취소" 결과를 받게 됩니다.</p> + +<p>단순히 여러 가지 결과 옵션 중 하나를 나타내는 정수만 반환하면 되는 경우, +결과 코드를 0보다 큰 임의의 값으로 설정하면 됩니다. 결과 코드를 사용하여 정수만 제공하고 +{@link android.content.Intent}를 포함할 필요는 없는 경우, {@link +android.app.Activity#setResult(int) setResult()}를 호출하고 결과 코드만 전달하면 됩니다. 예를 들면 다음과 같습니다.</p> + +<pre> +setResult(RESULT_COLOR_RED); +finish(); +</pre> + +<p>이런 경우, 가능한 결과는 몇 개에 불과할 것이므로 결과 코드는 로컬로 +정의된 정수입니다(0보다 큼). 이는 자신의 앱에서 액티비티에 결과를 반환할 때 +유용합니다. 왜냐하면 결과를 수신한 액티비티가 공용 상수를 참조하여 결과 코드의 +값을 확인할 수 있기 때문입니다.</p> + +<p class="note"><strong>참고:</strong> {@link android.app.Activity#startActivity startActivity()} +또는 {@link +android.app.Activity#startActivityForResult startActivityForResult()}로 액티비티가 +시작되었는지 확인할 필요는 없습니다. 액티비티를 시작한 +인텐트가 결과를 원할 경우, {@link android.app.Activity#setResult(int,Intent) setResult()}를 +호출하기만 하면 됩니다. 원래의 액티비티가 {@link +android.app.Activity#startActivityForResult startActivityForResult()}를 호출한 경우, 시스템은 개발자가 {@link android.app.Activity#setResult(int,Intent) setResult()}에 제공하는 +결과를 액티비티에 제공합니다. 그렇지 않을 경우 +결과는 무시됩니다.</p> + + + + + + diff --git a/docs/html-intl/intl/ko/training/basics/intents/index.jd b/docs/html-intl/intl/ko/training/basics/intents/index.jd new file mode 100644 index 0000000..d4aa0cb --- /dev/null +++ b/docs/html-intl/intl/ko/training/basics/intents/index.jd @@ -0,0 +1,62 @@ +page.title=다른 앱과 상호 작용하기 +page.tags=intents,activity +helpoutsWidget=true + +trainingnavtop=true +startpage=true + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>종속관계 및 전제조건</h2> +<ul> + <li>액티비티 수명 주기에 대한 기본 지식(<a href="{@docRoot}training/basics/activity-lifecycle/index.html">액티비티 +수명 주기 관리하기</a> 참조)</li> +</ul> + + +<h2>필독 항목</h2> +<ul> + <li><a href="{@docRoot}training/sharing/index.html">간단한 데이터 공유하기</a></li> + <li><a href="{@docRoot}training/secure-file-sharing/index.html">파일 공유하기</a> + <li><a href="http://android-developers.blogspot.com/2009/11/integrating-application-with-intents.html"> +인텐트와 애플리케이션 통합하기(블로그 게시물)</a></li> + <li><a href="{@docRoot}guide/components/intents-filters.html">인텐트 및 인텐트 +필터</a></li> +</ul> + +</div> +</div> + +<p>Android 앱은 보통 여러 <a href="{@docRoot}guide/components/activities.html">액티비티</a>를 가지고 있습니다. 각 액티비티는 +사용자가 지도 보기 및 사진 촬영과 같은 특정 작업을 수행할 수 있게 해주는 사용자 인터페이스를 표시합니다. +사용자가 하나의 액티비티에서 다른 액티비티로 전환되도록 하려면 {@link +android.content.Intent}를 사용하여 앱의 "인텐트"가 어떠한 작업을 하도록 정의해야 합니다. {@link +android.app.Activity#startActivity startActivity()}와 같은 메서드로 시스템에 +{@link android.content.Intent}를 전달하면, 시스템은 {@link +android.content.Intent}를 사용하여 적절한 앱 구성요소를 식별하고 시작합니다. 인텐트를 사용하면 +자신의 앱에서 별도의 앱에 포함된 액티비티를 시작하도록 할 수 있습니다.</p> + +<p>{@link android.content.Intent}는 특정 +구성요소(특정 {@link android.app.Activity} 인스턴스)를 시작하기 위한 <em>명시적인 인텐트</em>이거나, "사진 캡처"와 같은 의도된 작업을 처리할 수 있는 +임의의 구성요소를 시작하기 위한 <em>암묵적인 인텐트</em>일 수 있습니다.</p> + +<p>이 클래스에서는 {@link android.content.Intent}를 사용하여 다른 앱과 몇 가지 기본적인 +상호 작용을 수행하는 방법을 보여줍니다. 이러한 상호 작용에는 다른 앱 시작, 해당 앱에서 결과 수신하기, +다른 앱의 인텐트에 응답할 수 있도록 앱 구성하기 등이 있습니다.</p> + +<h2>과정</h2> + +<dl> + <dt><b><a href="sending.html">다른 앱으로 사용자 보내기</a></b></dt> + <dd>작업을 수행할 수 있는 다른 앱을 시작하기 위한 +암묵적인 인텐트를 생성하는 방법을 보여줍니다.</dd> + <dt><b><a href="result.html">액티비티로부터 결과 가져오기</a></b></dt> + <dd>다른 액티비티를 시작하고 해당 액티비티로부터 결과를 수신하는 방법을 보여줍니다.</dd> + <dt><b><a href="filters.html">다른 앱이 자신의 액티비티를 시작하도록 허용하기</a></b></dt> + <dd>앱이 수락하는 암묵적인 인텐트를 선언하는 +인텐트 필터를 정의하여 자신의 앱의 액티비티를 다른 앱이 사용할 수 있도록 공개하는 방법을 보여줍니다.</dd> +</dl> + diff --git a/docs/html-intl/intl/ko/training/basics/intents/result.jd b/docs/html-intl/intl/ko/training/basics/intents/result.jd new file mode 100644 index 0000000..7b25eaf8 --- /dev/null +++ b/docs/html-intl/intl/ko/training/basics/intents/result.jd @@ -0,0 +1,178 @@ +page.title=액티비티로부터 결과 가져오기 +page.tags=intents +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> + <div id="tb"> + +<h2>이 과정에서 다루는 내용</h2> +<ol> + <li><a href="#StartActivity">액티비티 시작하기</a></li> + <li><a href="#ReceiveResult">결과 수신하기</a></li> +</ol> + +<h2>필독 항목</h2> +<ul> + <li><a href="{@docRoot}training/sharing/index.html">간단한 데이터 공유하기</a></li> + <li><a href="{@docRoot}training/secure-file-sharing/index.html">파일 공유하기</a> +</ul> + + </div> +</div> + +<p>다른 액티비티를 시작하는 것이 단방향일 필요는 없습니다. 다른 액티비티를 시작하고 그 액티비티로부터 결과를 +수신할 수도 있습니다. 결과를 수신하려면 {@link android.app.Activity#startActivityForResult +startActivityForResult()}를 호출합니다({@link android.app.Activity#startActivity +startActivity()} 대신).</p> + +<p>예를 들어 자신의 앱에서 카메라 앱을 시작하고, 그 결과로 캡처된 사진을 수신할 수 있습니다. 또는, +피플 앱을 시작하여 사용자가 연락처를 선택할 수 있도록 할 +수 있으며, 그 결과로 연락처 상세정보를 수신할 수 있습니다.</p> + +<p>물론, 응답하는 액티비티는 결과를 반환하도록 설계되어 있어야 합니다. 그럴 경우, 이 액티비티는 +또 다른 {@link android.content.Intent} 개체의 형태로 결과를 전송합니다. 그러면 액티비티가 {@link android.app.Activity#onActivityResult onActivityResult()} 콜백으로 +이 결과를 수신합니다.</p> + +<p class="note"><strong>참고:</strong> {@link android.app.Activity#startActivityForResult startActivityForResult()}를 +호출할 때 명시적 또는 암묵적인 인텐트를 사용할 수 있습니다. 자신이 정의한 액티비티 중 +하나를 시작하여 결과를 수신하는 경우 예상하는 결과를 수신하도록 보장하려면 명시적인 인텐트를 +사용해야 합니다.</p> + + +<h2 id="StartActivity">액티비티 시작하기</h2> + +<p>결과를 수신하기 위해 액티비티를 시작할 때 사용하는 {@link android.content.Intent} 개체와 +관련하여 특별한 사항은 없습니다. 하지만, {@link +android.app.Activity#startActivityForResult startActivityForResult()} 메서드에 추가적인 정수 인수를 전달해야 합니다.</p> + +<p>정수 인수는 요청을 식별하는 "요청 코드"입니다. 결과 {@link android.content.Intent}를 +수신하는 경우, 앱이 결과를 올바르게 식별하여 이를 처리할 방법을 결정할 수 +있도록 콜백이 이와 똑같은 요청 코드를 제공합니다.</p> + +<p>다음은 사용자가 연락처를 선택할 수 있게 하는 액티비티를 시작하는 방법에 대한 예제입니다.</p> + +<pre> +static final int PICK_CONTACT_REQUEST = 1; // The request code +... +private void pickContact() { + Intent pickContactIntent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts")); + pickContactIntent.setType(Phone.CONTENT_TYPE); // Show user only contacts w/ phone numbers + startActivityForResult(pickContactIntent, PICK_CONTACT_REQUEST); +} +</pre> + + +<h2 id="ReceiveResult">결과 수신하기</h2> + +<p>사용자가 후속 액티비티 작업을 마치고 돌아오면, 시스템은 개발자 자신이 정의한 원래 액티비티의 +{@link android.app.Activity#onActivityResult onActivityResult()} 메서드를 호출합니다. 이 메서드는 다음 세 가지 +인수를 포함합니다.</p> + +<ul> + <li>{@link +android.app.Activity#startActivityForResult startActivityForResult()}에 전달한 요청 코드</li> + <li>두 번째 액티비티가 지정한 결과 코드. 이 코드는 작업이 성공한 경우 {@link +android.app.Activity#RESULT_OK}이고, 사용자가 작업을 취소했거나 작업이 어떠한 +이유로 실패한 경우 {@link +android.app.Activity#RESULT_CANCELED}입니다.</li> + <li>결과 데이터를 전달하는 {@link android.content.Intent}</li> +</ul> + +<p>다음은 "연락처 선택하기" 인텐트에 대한 결과를 처리하는 방법을 보여주는 예제입니다.</p> + +<pre> +@Override +protected void onActivityResult(int requestCode, int resultCode, Intent data) { + // Check which request we're responding to + if (requestCode == PICK_CONTACT_REQUEST) { + // Make sure the request was successful + if (resultCode == RESULT_OK) { + // The user picked a contact. + // The Intent's data Uri identifies which contact was selected. + + // Do something with the contact here (bigger example below) + } + } +} +</pre> + +<p>이 예제에서는 Android 연락처 또는 피플 앱에서 +반환되는 결과 {@link android.content.Intent}가 사용자가 선택한 연락처를 식별하는 {@link android.net.Uri} 콘텐츠를 +제공합니다.</p> + +<p>결과를 성공적으로 처리하기 위해서는 +결과 {@link android.content.Intent}의 형식이 무엇인지 이해하고 있어야 합니다. 결과를 반환하는 액티비티가 자신이 정의한 액티비티 중 +하나일 경우, 그 결과를 이해하기가 쉽습니다. Android 플랫폼에 포함된 앱은 특정한 결과 데이터를 기대할 수 +있는 고유한 API를 제공합니다. 예를 들어, 피플 앱(일부 이전 버전의 경우 +연락처 앱)은 선택된 연락처를 식별하는 콘텐츠 URI와 함께 항상 결과를 반환합니다. 또한 +카메라 앱은 {@code "data"} 엑스트라에 {@link android.graphics.Bitmap}을 반환합니다(<a href="{@docRoot}training/camera/index.html">사진 캡처하기</a> +클래스 참조).</p> + + +<h4>보너스: 연락처 데이터 읽기</h4> + +<p>피플 앱에서 결과를 가져오는 방법을 보여주는 상기 코드는, 결과에서 +데이터를 실제로 읽는 방법에 대해 구체적으로 설명하지는 않습니다. 그 이유는 이를 설명하려면 +<a href="{@docRoot}guide/topics/providers/content-providers.html">콘텐츠 제공자</a>에 대한 심도 있는 논의가 +필요하기 때문입니다. 하지만 이러한 내용이 궁금할 경우, 결과 데이터를 쿼리하여 선택된 +연락처에서 전화번호를 가져오는 방법을 보여주는 다음 코드를 참조하세요.</p> + +<pre> +@Override +protected void onActivityResult(int requestCode, int resultCode, Intent data) { + // Check which request it is that we're responding to + if (requestCode == PICK_CONTACT_REQUEST) { + // Make sure the request was successful + if (resultCode == RESULT_OK) { + // Get the URI that points to the selected contact + Uri contactUri = data.getData(); + // We only need the NUMBER column, because there will be only one row in the result + String[] projection = {Phone.NUMBER}; + + // Perform the query on the contact to get the NUMBER column + // We don't need a selection or sort order (there's only one result for the given URI) + // CAUTION: The query() method should be called from a separate thread to avoid blocking + // your app's UI thread. (For simplicity of the sample, this code doesn't do that.) + // Consider using {@link android.content.CursorLoader} to perform the query. + Cursor cursor = getContentResolver() + .query(contactUri, projection, null, null, null); + cursor.moveToFirst(); + + // Retrieve the phone number from the NUMBER column + int column = cursor.getColumnIndex(Phone.NUMBER); + String number = cursor.getString(column); + + // Do something with the phone number... + } + } +} +</pre> + +<p class="note"><strong>참고:</strong> Android 2.3(API 레벨 9) 이전에서는 위에 표시된 코드와 +같이 {@link android.provider.ContactsContract.Contacts Contacts Provider}에 대해 쿼리를 +수행하려면 앱에서 {@link +android.Manifest.permission#READ_CONTACTS} 권한을 선언해야 합니다(<a href="{@docRoot}guide/topics/security/security.html">보안 및 권한</a> 참조). 하지만 +Android 2.3부터는 연락처 제공자가 결과를 반환할 때 자신의 앱에서 +그 결과를 읽어올 수 있도록 연락처/피플 앱이 임시 권한을 부여합니다. 임시 권한은 해당 연락처 요청에만 +적용되기 때문에, 인텐트의 {@link android.net.Uri}에 +지정된 연락처 외에는 쿼리할 수 없습니다. 다만, {@link +android.Manifest.permission#READ_CONTACTS} 권한을 명시적으로 선언한 경우는 예외입니다.</p> + + + + + + + + + + + + + + + diff --git a/docs/html-intl/intl/ko/training/basics/intents/sending.jd b/docs/html-intl/intl/ko/training/basics/intents/sending.jd new file mode 100644 index 0000000..b3e84eb --- /dev/null +++ b/docs/html-intl/intl/ko/training/basics/intents/sending.jd @@ -0,0 +1,256 @@ +page.title=다른 앱으로 사용자 보내기 +page.tags=intents +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + + +<div id="tb-wrapper"> + <div id="tb"> + +<h2>이 과정에서 다루는 내용</h2> +<ol> + <li><a href="#Build">암묵적인 인텐트 구축하기</a></li> + <li><a href="#Verify">인텐트를 수신할 앱이 있는지 확인하기</a></li> + <li><a href="#StartActivity">인텐트를 사용하여 액티비티 시작하기</a></li> + <li><a href="#AppChooser">앱 선택기 표시하기</a></li> +</ol> + +<h2>필독 항목</h2> +<ul> + <li><a href="{@docRoot}training/sharing/index.html">간단한 데이터 공유하기</a></li> +</ul> + + </div> +</div> + +<p>Android의 가장 중요한 특징 중 하나는 수행하고자 하는 "작업"을 +기반으로 사용자를 다른 앱으로 보낼 수 있는 앱의 기능입니다. 예를 들어 +지도에 나타내고자 하는 사업체의 주소가 앱에 있는 경우, 지도를 보여주는 액티비티를 앱 내에 +구축할 필요가 없습니다. 대신 {@link android.content.Intent}를 +사용하여 주소 보기 요청을 생성할 수 있습니다. 그러면 Android 시스템이 지도에 +주소를 표시할 수 있는 앱을 시작합니다.</p> + +<p>첫 번째 클래스인 <a href="{@docRoot}training/basics/firstapp/index.html">첫 +앱 구축</a>에서 설명했듯이, 인텐트를 사용하여 자체 앱 내에서 액티비티 간을 탐색해야 합니다. 일반적으로 +시작하고자 하는 +구성요소의 정확한 클래스 이름을 정의하는 <em>명시적인 인텐트</em>를 사용하여 이러한 작업을 수행합니다. 하지만 "지도 보기"와 +같은 작업을 별도의 앱에서 수행하도록 하기를 원할 경우, <em>암묵적인 인텐트</em>를 사용해야 합니다.</p> + +<p>이 과정에서는 특정 작업에 대한 암묵적인 인텐트를 생성하는 방법과 이를 사용하여 +다른 앱에서 작업을 수행하는 액티비티를 시작하는 방법에 대해 설명합니다.</p> + + + +<h2 id="Build">암묵적인 인텐트 구축하기</h2> + +<p>암묵적인 인텐트는 시작할 구성요소의 클래스 이름을 선언하지 않고, 그 대신 수행할 +작업을 선언합니다. 작업은 <em>보기</em>, +<em>편집하기</em>, <em>보내기</em> 또는 <em>가져오기</em>와 같이 수행하고자 하는 동작을 지정합니다. 또한 인텐트는 보기 원하는 주소, 또는 전송하기 원하는 이메일 메시지와 같이 작업과 +연결된 데이터를 함께 포함하는 경우가 많습니다. +생성하고자 하는 인텐트에 따라 데이터는 {@link android.net.Uri}이거나 +다른 여러 가지 데이터 유형 중 하나일 수 있으며, 인텐트가 데이터를 전혀 필요로 하지 않을 수도 있습니다.</p> + +<p>데이터가 {@link android.net.Uri}인 경우, 간단한 {@link +android.content.Intent#Intent(String,Uri) Intent()} 생성자를 사용하여 액션 및 +데이터를 정의할 수 있습니다.</p> + +<p>다음은 {@link +android.net.Uri} 데이터를 사용하여 전화번호를 지정하여 전화 걸기를 시작하는 인텐트를 생성하는 방법입니다.</p> + +<pre> +Uri number = Uri.parse("tel:5551234"); +Intent callIntent = new Intent(Intent.ACTION_DIAL, number); +</pre> + +<p>앱이 {@link android.app.Activity#startActivity +startActivity()}를 호출하여 이 인텐트를 호출하면, 전화 앱이 주어진 전화번호로 전화를 겁니다.</p> + +<p>다음은 몇 가지 다른 인텐트와 해당 작업 및 {@link android.net.Uri} 데이터 +쌍입니다.</p> + +<ul> + <li>지도 보기: +<pre> +// Map point based on address +Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California"); +// Or map point based on latitude/longitude +// Uri location = Uri.parse("geo:37.422219,-122.08364?z=14"); // z param is zoom level +Intent mapIntent = new Intent(Intent.ACTION_VIEW, location); +</pre> + </li> + <li>웹 페이지 보기: +<pre> +Uri webpage = Uri.parse("http://www.android.com"); +Intent webIntent = new Intent(Intent.ACTION_VIEW, webpage); +</pre> + </li> +</ul> + +<p>다른 종류의 암묵적인 인텐트는 문자열과 같이 +여러 데이터 유형을 제공하는 "엑스트라" 데이터를 필요로 합니다. 다양한 {@link +android.content.Intent#putExtra(String,String) putExtra()} 메서드를 사용하여 하나 이상의 엑스트라 데이터를 추가할 수 있습니다.</p> + +<p>기본적으로 시스템은 포함된 +{@link android.net.Uri} 데이터를 바탕으로 인텐트가 필요로 하는 적절한 MIME 유형을 결정합니다. {@link android.net.Uri}를 인텐트에 +포함하지 않을 경우, 일반적으로 {@link android.content.Intent#setType setType()}을 사용하여 인텐트와 관련된 데이터의 +유형을 지정해야 합니다. MME 유형을 설정하면 인텐트를 수신할 +액티비티의 종류도 지정됩니다.</p> + +<p>다음은 원하는 작업을 지정하기 위해 엑스트라 데이터를 추가하는 인텐트의 몇 가지 예제입니다.</p> + +<ul> + <li>첨부 파일과 함께 이메일 보내기: +<pre> +Intent emailIntent = new Intent(Intent.ACTION_SEND); +// The intent does not have a URI, so declare the "text/plain" MIME type +emailIntent.setType(HTTP.PLAIN_TEXT_TYPE); +emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"jon@example.com"}); // recipients +emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Email subject"); +emailIntent.putExtra(Intent.EXTRA_TEXT, "Email message text"); +emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://path/to/email/attachment")); +// You can also attach multiple items by passing an ArrayList of Uris +</pre> + </li> + <li>캘린더 이벤트 생성하기: +<pre> +Intent calendarIntent = new Intent(Intent.ACTION_INSERT, Events.CONTENT_URI); +Calendar beginTime = Calendar.getInstance().set(2012, 0, 19, 7, 30); +Calendar endTime = Calendar.getInstance().set(2012, 0, 19, 10, 30); +calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis()); +calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis()); +calendarIntent.putExtra(Events.TITLE, "Ninja class"); +calendarIntent.putExtra(Events.EVENT_LOCATION, "Secret dojo"); +</pre> +<p class="note"><strong>참고:</strong> 캘린더 이벤트에 대한 이 인텐트는 API +레벨 14 이상에서만 지원됩니다.</p> + </li> +</ul> + +<p class="note"><strong>참고:</strong> {@link +android.content.Intent}는 되도록 구체적으로 정의해야 합니다. 예를 들어 {@link android.content.Intent#ACTION_VIEW} 인텐트를 +사용하여 이미지를 표시하고자 할 경우, +{@code image/*}의 MIME 유형을 지정해야 합니다. 그러면 지도 앱과 같이 다른 유형의 데이터를 "볼" 수 있는 앱이 인텐트에 의해 +트리거되는 것이 방지됩니다.</p> + + + +<h2 id="Verify">인텐트를 수신할 앱이 있는지 확인하기</h2> + +<p>Android 플랫폼은 전화, 이메일 또는 캘린더 앱과 같은 +내장 앱 중 하나로 특정 인텐트가 처리되도록 보장하지만, 인텐트를 호출하기 전에 항상 +확인 단계를 포함하는 것이 좋습니다.</p> + +<p class="caution"><strong>주의:</strong> 인텐트를 호출한 후 해당 인텐트를 처리할 수 있는 앱이 +기기에 없을 경우, 앱은 작동을 중단합니다.</p> + +<p>인텐트에 응답할 수 있는 액티비티가 있는지 확인하려면 {@link +android.content.pm.PackageManager#queryIntentActivities queryIntentActivities()}를 호출하여 {@link android.content.Intent}를 처리할 수 있는 액티비티 +목록을 가져와야 합니다. 반환된 {@link +java.util.List}가 비어있지 않을 경우, 안심하고 인텐트를 사용할 수 있습니다. 예를 들면 다음과 같습니다.</p> + +<pre> +PackageManager packageManager = {@link android.content.Context#getPackageManager()}; +List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, + PackageManager.MATCH_DEFAULT_ONLY); +boolean isIntentSafe = activities.size() > 0; +</pre> + +<p><code>isIntentSafe</code>가 <code>true</code>일 경우, 하나 이상의 앱이 +인텐트에 응답합니다. <code>false</code>일 경우, 인텐트를 처리할 수 있는 앱이 없는 것입니다.</p> + +<p class="note"><strong>참고:</strong> 사용자가 인텐트를 +사용하려고 하기 전에 이 인텐트를 사용하는 기능을 해제해야 할 경우, 액티비티가 처음 +시작될 때 이 확인 작업을 수행해야 합니다. 인텐트를 처리할 수 있는 앱을 알고 있을 경우, +사용자가 앱을 다운로드할 수 있도록 링크를 제공할 수도 있습니다(<a href="{@docRoot}distribute/tools/promote/linking.html">Google +Play에서 자신의 제품에 대한 링크를 제공</a>하는 방법 참조).</p> + + +<h2 id="StartActivity">인텐트를 사용하여 액티비티 시작하기</h2> + +<div class="figure" style="width:200px;margin-top:-10px"> + <img src="{@docRoot}images/training/basics/intents-choice.png" alt="" /> + <p class="img-caption"><strong>그림 1.</strong> 인텐트를 처리할 수 있는 앱이 둘 이상 있을 경우 표시되는 선택 대화 상자의 예. +</p> +</div> + +<p>{@link android.content.Intent}를 생성하고 엑스트라 정보를 설정한 후에는 {@link +android.app.Activity#startActivity startActivity()}를 호출하여 시스템에 보내야 합니다. 시스템이 인텐트를 처리할 수 있는 액티비티를 둘 이상 +식별하는 경우, 그림 1과 같이 사용자가 사용할 앱을 +선택할 수 있는 대화 상자를 표시합니다. 인텐트를 +처리할 수 있는 액티비티가 하나밖에 없을 경우, 시스템이 해당 액티비티를 바로 시작합니다.</p> + +<pre> +startActivity(intent); +</pre> + +<p>다음은 지도 보기 인텐트를 생성하고, 인텐트를 처리할 수 있는 +앱의 존재 여부를 확인하고, 앱을 시작하는 방법을 보여주는 완벽한 예제입니다.</p> + +<pre> +// Build the intent +Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California"); +Intent mapIntent = new Intent(Intent.ACTION_VIEW, location); + +// Verify it resolves +PackageManager packageManager = {@link android.content.Context#getPackageManager()}; +List<ResolveInfo> activities = packageManager.queryIntentActivities(mapIntent, 0); +boolean isIntentSafe = activities.size() > 0; + +// Start an activity if it's safe +if (isIntentSafe) { + startActivity(mapIntent); +} +</pre> + + + +<h2 id="AppChooser">앱 선택기 표시하기</h2> + +<div class="figure" style="width:200px;margin-top:-10px"> + <img src="{@docRoot}images/training/basics/intent-chooser.png" alt="" /> + <p class="img-caption"><strong>그림 2.</strong> 선택기 대화 상자.</p> +</div> + +<p>{@link android.content.Intent}를 {@link +android.app.Activity#startActivity startActivity()}에 전달하여 액티비티를 시작하는데 인텐트에 +응답하는 앱이 둘 이상 있는 경우, 사용자는 어떤 앱을 기본으로 사용할지 선택할 수 있습니다(이는 대화 상자 +하단의 확인란을 선택하여 수행할 수 있음. 그림 1 참조). 이는 사용자가 일반적으로 +항상 동일한 앱을 사용하기 원하는 작업을 수행하는 경우에 유용합니다. 예를 들어 웹 페이지를 열 때(사용자는 +대개 하나의 웹 브라우저를 사용함) 또는 사진을 찍을 때(사용자는 대개 하나의 카메라를 선호함) 등이 있습니다.</p> + +<p>하지만 "공유" 작업과 같이 항목을 공유할 앱을 +여러 개 보유하고 있는 경우처럼, 수행할 작업을 처리할 수 있는 앱이 여러 개 있고 사용자가 매번 다른 앱을 +원할 수도 있습니다. 이런 경우, 그림 2와 같이 +선택기 대화 상자를 명시적으로 표시합니다. 선택기 대화 상자는 +사용자가 작업에 사용할 앱을 매번 선택하도록 합니다(사용자는 작업에 사용할 +기본 앱을 선택할 수 없음).</p> + +<p>선택기를 표시하려면 {@link +android.content.Intent#createChooser createChooser()}를 사용하여 {@link android.content.Intent}를 생성한 후 {@link +android.app.Activity#startActivity startActivity()}에 전달합니다. 예를 들면 다음과 같습니다.</p> + +<pre> +Intent intent = 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 chooser +Intent chooser = Intent.createChooser(intent, title); + +// Verify the intent will resolve to at least one activity +if (intent.resolveActivity(getPackageManager()) != null) { + startActivity(chooser); +} +</pre> + +<p>이 예제는 {@link +android.content.Intent#createChooser createChooser()} 메서드에 전달된 인텐트에 응답하는 앱 목록이 포함된 대화 상자를 표시하고 제공된 텍스트를 +대화 상자 제목으로 사용합니다.</p> + + + |