diff options
author | David Friedman <dmail@google.com> | 2015-07-02 00:47:18 +0000 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2015-07-02 00:47:18 +0000 |
commit | 0d3a0f1b8a116863c717869bb9ece73305232e33 (patch) | |
tree | a7f7f506625e87c0bd877d49442e72700329fc00 /docs | |
parent | 6da7931e4797595359a960d28560a79abb76d602 (diff) | |
parent | c5764868bdeef4a944317885b000db50e4092db5 (diff) | |
download | frameworks_base-0d3a0f1b8a116863c717869bb9ece73305232e33.zip frameworks_base-0d3a0f1b8a116863c717869bb9ece73305232e33.tar.gz frameworks_base-0d3a0f1b8a116863c717869bb9ece73305232e33.tar.bz2 |
am c5764868: am e562bb6b: Merge "Docs: Localizations of Android dev on-boarding training docs. Bug: 20503574" into mnc-preview-docs
* commit 'c5764868bdeef4a944317885b000db50e4092db5':
Docs: Localizations of Android dev on-boarding training docs. Bug: 20503574
Diffstat (limited to 'docs')
79 files changed, 14945 insertions, 10 deletions
diff --git a/docs/html-intl/intl/ja/training/basics/activity-lifecycle/index.jd b/docs/html-intl/intl/ja/training/basics/activity-lifecycle/index.jd new file mode 100644 index 0000000..837fc2b --- /dev/null +++ b/docs/html-intl/intl/ja/training/basics/activity-lifecycle/index.jd @@ -0,0 +1,72 @@ +page.title=アクティビティのライフサイクル 管理 +page.tags=アクティビティのライフサイクル +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/ja/training/basics/activity-lifecycle/pausing.jd b/docs/html-intl/intl/ja/training/basics/activity-lifecycle/pausing.jd new file mode 100644 index 0000000..b837a00 --- /dev/null +++ b/docs/html-intl/intl/ja/training/basics/activity-lifecycle/pausing.jd @@ -0,0 +1,147 @@ +page.title=アクティビティの一時停止と再開 +page.tags=アクティビティのライフサイクル +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()}の期間内に永続的なストレージにユーザーの変更を保持する必要があります。ただし、{@link +android.app.Activity#onPause()} メソッド中にはデータベースへの書き込みなどの CPU に高負荷をかける作業を行うことは避けてください。これにより、次のアクティビティへの表示上の遷移を遅らせる可能性があります(代わりに +{@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#onPause()} メソッド中に解放したコンポーネントの初期化、およびアクティビティが再開状態になるたびに発生するその他の初期化(アニメーションの開始や、アクティビティがユーザーのフォーカスを取得したときにのみ使用されるコンポーネントの初期化など)を実行するように、{@link +android.app.Activity#onResume()} を実装する必要があります。 + +</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/ja/training/basics/activity-lifecycle/recreating.jd b/docs/html-intl/intl/ja/training/basics/activity-lifecycle/recreating.jd new file mode 100644 index 0000000..8647375 --- /dev/null +++ b/docs/html-intl/intl/ja/training/basics/activity-lifecycle/recreating.jd @@ -0,0 +1,178 @@ +page.title=アクティビティを再作成する +page.tags=アクティビティのライフサイクル +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 システムがアクティビティのビューの状態を復元できるようにするためには、<strong>各ビューは固有の ID を持っている必要があります</strong>。これは + <a href="{@docRoot}reference/android/view/View.html#attr_android:id">{@code +android:id}</a> によって提供されます。 +</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} インスタンスの再作成の必要がある場合に備えて、保存する追加の状態データを指定できます。アクティビティが破棄され、同じインスタンスを再作成する必要がある場合、システムは(1)で定義された状態データを {@link android.app.Activity#onCreate onCreate()} メソッド(2)と +{@link android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} メソッド(3)の両方に渡します。 + + + + +</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#onRestoreInstanceState onRestoreInstanceState()} のスーパークラスの実装を常に呼び出す必要があります。 +</p> + +<p>実行時の再起動イベント(画面が回転したときなど)によるアクティビティの再作成の詳細については、<a href="{@docRoot}guide/topics/resources/runtime-changes.html">実行時の変更を処理する</a>をお読みください。 +</p> + diff --git a/docs/html-intl/intl/ja/training/basics/activity-lifecycle/starting.jd b/docs/html-intl/intl/ja/training/basics/activity-lifecycle/starting.jd new file mode 100644 index 0000000..7c865a9 --- /dev/null +++ b/docs/html-intl/intl/ja/training/basics/activity-lifecycle/starting.jd @@ -0,0 +1,285 @@ +page.title=アクティビティを開始する +page.tags=アクティビティのライフサイクル +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>アクティビティの存続期間において、システムは、階段ピラミッド型のシーケンスで、ライフサイクル メソッドのコア セットを呼び出します。 +つまり、アクティビティのライフサイクルの各段階は、ピラミッドの個別の段です。 +システムが新しいアクティビティのインスタンスを作成すると、各コールバック メソッドはアクティビティ状態を 1 ステップ上に向かって移動させます。 +ピラミッドの頂点は、アクティビティがフォアグラウンドで実行されるポイントであり、ユーザーはそこで操作を行うことができます。 +</p> + +<p>ユーザーがアクティビティを離れる操作を始めると、システムはアクティビティを破棄するために、アクティビティ状態をピラミッドの下に向かって移動させる、別のメソッドを呼び出します。 +アクティビティがピラミッドの下に向かって途中まで移動し、待機する場合がありますが(ユーザーが別のアプリに切り替えた場合など)、そのポイントからアクティビティは頂点に戻ることができ(ユーザーがアクティビティに戻った場合)、アクティビティはユーザーが離れた場所から再開します。 + + +</p> + + +<img src="{@docRoot}images/training/basics/basic-lifecycle.png" /> +<p class="img-caption"><strong>図 1.</strong> アクティビティのライフサイクルの簡略図は階段ピラミッドとして表現されます。 +ここでは、アクティビティを頂点の再開状態に向かって 1 ステップ移動させるために使用されるすべてのコールバックに対して、下に向かってアクティビティを移動させるコールバック メソッドがどのように分布しているかを示しています。 + +アクティビティはまた、一時停止や停止の状態から再開した状態に復帰することができます。 +</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 に示されているさまざまな状態間で起きるアクティビティ遷移には、いくつかの状況があります。 +ただし、これらの状態のうち 3 つのみが静的なものとなります。 +すなわち、アクティビティが長期間にわたる場合、3 つの状態のいずれかで存在することになります。 +</p> +<dl> + <dt>再開状態</dt> + <dd>この状態では、アクティビティがフォアグラウンドで実行され、ユーザーはそこで操作を行うことができます(「実行」状態とも呼ばれます)。 +</dd> + <dt>一時停止状態</dt> + <dd>この状態では、アクティビティは別のアクティビティによって部分的に隠されています。フォアグラウンドにある別のアクティビティは半透明になっているか、全画面をカバーしていません。 +一時停止したアクティビティはユーザーの入力を受信せず、一切のコードを実行できません。 + + <dt>停止状態</dt> + <dd>この状態では、アクティビティは完全に隠され、ユーザーには表示されません。バックグラウンドに存在するとみなされます。 +停止状態の間、アクティビティ インスタンスとメンバ変数のようなそのすべての状態情報は保持されますが、アクティビティ インスタンスは一切のコードを実行できません。 +</dd> +</dl> + +<p>他の状態(作成や起動の状態)は一過性であり、システムはすぐに次のライフサイクル コールバック メソッドを呼び出して、次の状態に移動します。 +つまり、システムは +{@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 ツールを使用して新しい Android プロジェクトを作成すると、デフォルトのプロジェクト ファイルに、このフィルタを用いてマニフェストで宣言された {@link android.app.Activity} クラスが含まれます。 + +</p> + +<p>自分のアクティビティの 1 つについて、{@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} を使用すると、Android 2.0(API レベル 5)以降でのみこの方法で動作する新しい API が古いシステムで実行してしまうことを回避できます。 + +古いバージョンでは、実行時の例外が発生します。</p> + +<p>{@link android.app.Activity#onCreate onCreate()} の実行が終了すると、システムが引き続き {@link android.app.Activity#onStart()} と {@link android.app.Activity#onResume()} メソッドを呼び出します。 + +アクティビティは、作成や開始の状態で留まることはありません。技術的には、アクティビティは {@link android.app.Activity#onStart()} が呼び出されたときにユーザーに表示されますが、すぐに +{@link android.app.Activity#onResume()} が続き、電話の着信やユーザーが別のアクティビティに移動したり、端末の画面がオフになったりした際のように、状態を変える状況が発生するまで、アクティビティは再開状態のままとなります。 + + +</p> + +<p>この後に続く他のレッスンでは、その他の起動メソッドである {@link +android.app.Activity#onStart()} と {@link android.app.Activity#onResume()} が一時停止状態または停止状態からアクティビティを再開するために使用された場合、これらのメソッドがアクティビティのライフサイクル中にいかに有用であるかを説明します。 +</p> + +<p class="note"><strong>注:</strong> {@link android.app.Activity#onCreate onCreate()}メソッドには、<a href="recreating.html">アクティビティの再作成</a>についての後続のレッスンで説明している <code>savedInstanceState</code> と呼ぶパラメータが含まれています。 + +</p> + + +<img src="{@docRoot}images/training/basics/basic-lifecycle-create.png" /> +<p class="img-caption"><strong>図 2.</strong> アクティビティの新しいインスタンスを作成する際に +システムが順に呼び出す 3 つの主なコールバックである +{@link android.app.Activity#onCreate onCreate()}、{@link +android.app.Activity#onStart()}、{@link android.app.Activity#onResume()} を強調した、アクティビティのライフサイクル構造の別の図です。コールバックのこのシーケンスが完了すると、アクティビティは再開状態に達し、ユーザーは別のアクティビティに切り替えるまではこのアクティビティを操作できます。 + +</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#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#onCreate onCreate()} 内から {@link android.app.Activity#finish()} を呼び出してアクティビティを破棄できます。 +この場合、システムは他のライフサイクルの任意のメソッドを呼び出すことなく、すぐに {@link android.app.Activity#onDestroy} を呼び出します。 + +</p> diff --git a/docs/html-intl/intl/ja/training/basics/activity-lifecycle/stopping.jd b/docs/html-intl/intl/ja/training/basics/activity-lifecycle/stopping.jd new file mode 100644 index 0000000..0007fe6 --- /dev/null +++ b/docs/html-intl/intl/ja/training/basics/activity-lifecycle/stopping.jd @@ -0,0 +1,187 @@ +page.title=アクティビティの停止と再起動 +page.tags=アクティビティのライフサイクル +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>ユーザーが新しいアクティビティを開始するアプリ内のアクションを実行します。2 番目のアクティビティが作成されると現在のアクティビティが停止します。 +ユーザーが <em>[戻る]</em> ボタンを押すと、最初のアクティビティが再起動されます。 +</li> + <li>ユーザーが自分の携帯電話でアプリの使用中に電話を受けます。</li> +</ul> + +<p>{@link android.app.Activity} クラスでは、{@link +android.app.Activity#onStop()} と {@link android.app.Activity#onRestart()} の 2 つのライフサイクル メソッドが提供され、アクティビティの停止動作と再起動動作を明確に制御できます。 +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#onPause onPause()} メソッドが +{@link android.app.Activity#onStop()} の前に呼び出されますが、データベースに情報を書き込むような、規模が大きく 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}(ひとまとまりのキー値のペア)の {@link android.view.View} オブジェクト({@link +android.widget.EditText} のテキストなど)の状態を保持し、ユーザーがアクティビティの同じインスタンスに復帰した場合には、それらを復元します(次の<a href="recreating.html">レッスン</a>では、アクティビティが破棄され、再作成された場合に、その他の状態データを保存するために {@link android.os.Bundle} を使用する方法の詳細について説明します)。 + + +</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#onStop()} メソッドへの対応として、通常は {@link android.app.Activity#onStart()} コールバック メソッドを使用する必要があります。なぜなら、アクティビティを作成したときと停止状態からアクティビティを再開したときの両方において、システムが {@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#onStop()} を使用してリソースのほとんどを解放している可能性があるため、{@link +android.app.Activity#onDestroy()} の呼び出しを受信する時点では、大抵のアプリでは必要な作業は少なくなっています。 +このメソッドは、メモリ リークにつながる可能性を持つリソースを一掃する最後のチャンスであるため、付加的なスレッドが破棄され、さらにメソッドのトレースのような長時間実行するその他のアクションも停止するようにする必要があります。 + + +</p> + diff --git a/docs/html-intl/intl/ja/training/basics/data-storage/databases.jd b/docs/html-intl/intl/ja/training/basics/data-storage/databases.jd new file mode 100644 index 0000000..d0a4d8f --- /dev/null +++ b/docs/html-intl/intl/ja/training/basics/data-storage/databases.jd @@ -0,0 +1,317 @@ +page.title=SQL データベースにデータを保存する +page.tags=データ保存 +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 ヘルパーを使用してデータベースを作成する</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 データベースの重要な要素の 1 つがスキーマであり、これはデータベースの編成方法に関する正式な宣言です。 +スキーマは、データベースを作成するために使用する SQL 文に反映されます。 +<em>コントラクト</em> クラスとして知られているコンパニオン クラスの作成が有用です。コンパニオン クラスでは、スキーマのレイアウトを明示的に、そして体系的な自己文書化する方法で指定します。 + +</p> + +<p>コントラクト クラスは、URI、表、列の名前を定義する定数のコンテナです。 +コントラクト クラスを使用すると、同じパッケージ内の他のすべてのクラスで、同じ定数を使用することができます。 +これにより、1 つの場所で列名を変更した場合に、それをコード全体にプロパゲートすることができます。 +</p> + +<p>コントラクト クラスを編成するお勧めの方法の 1 つは、クラスのルート レベルでデータベース全体に対しグローバルな定義を設定することです。 +その後、その列を列挙する各表の内部クラスを作成します。 +</p> + +<p class="note"><strong>注:</strong> {@link +android.provider.BaseColumns} インターフェースを実装することで、内部クラスでは、カーソル アダプタのようないくつかの Android クラスでも存在を想定されている、 +{@code _ID} と呼ばれるプライマリキーフィールドを継承することができます。 +これは必須ではありませんが、データベースが 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 ヘルパーを使用してデータベースを作成する</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()}の最初の引数は単なる表の名前です。 +2 番目の引数は列の名前を指定します。この列では、{@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>Cursor 内の特定の行に注目するには、いずれかの {@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/ja/training/basics/data-storage/files.jd b/docs/html-intl/intl/ja/training/basics/data-storage/files.jd new file mode 100644 index 0000000..dddfe37 --- /dev/null +++ b/docs/html-intl/intl/ja/training/basics/data-storage/files.jd @@ -0,0 +1,379 @@ +page.title=ファイルを保存する +page.tags=データ ストレージ +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 は、他のプラットフォーム上のディスクベースの +ファイル システムと同様なファイルシステムを使用しています。このレッスンでは、 +{@link java.io.File} API を使用したファイルの読み書きに関する Android ファイルシステムの操作方法について説明します。 +</p> + +<p>{@link java.io.File} オブジェクトは、スキップすることなく開始から終了までの順序で大量のデータを読み取りまたは書き込みするのに適しています。 +これは、たとえば画像ファイルやネットワークを介して交換される任意のファイルに対して有効です。 +</p> + +<p>このレッスンでは、自分のアプリで基本的なファイル関連のタスクを実行する方法を紹介します。ここでは、Linux ファイルシステムの基礎と +{@link java.io} での標準的なファイル入力/出力の API に精通していることを前提としています。 +</p> + + +<h2 id="InternalVsExternalStorage">内部または外部ストレージを選択する</h2> + +<p>すべての Android 端末には、「内部」ストレージと「外部」ストレージの 2 つのファイル記憶領域があります。これらの名前は初期の Android の名残として、当時ほとんどの端末が内蔵の不揮発性メモリ(内部ストレージ)を備えていたのに加え、マイクロ SD カードのような取り外し可能な記憶媒体(外部記憶装置)も備えていたことから来ています。一部の端末では、永続的なストレージ領域を「内部」と「外部」のパーティションに分割しており、リムーバブル記憶媒体が備わっていない場合でも常に 2 つのストレージ スペースがあり、外部ストレージが取り外し可能であるか否かにかかわらず、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">App Install Location</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>内部ストレージにファイルを保存する場合は、次の 2 つのメソッドのいずれかを呼び出すことにより、 +{@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} を渡して、{@link +java.io.File#File(File,String) 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>外部ストレージは、ユーザーや他のアプリから変更可能ですが、ここで保存可能なファイルには次の 2 つのカテゴリがあります。 +</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/ja/training/basics/data-storage/index.jd b/docs/html-intl/intl/ja/training/basics/data-storage/index.jd new file mode 100644 index 0000000..9109143 --- /dev/null +++ b/docs/html-intl/intl/ja/training/basics/data-storage/index.jd @@ -0,0 +1,57 @@ +page.title=データの保存 +page.tags=データ ストレージ、ファイル、SQL、データベース、環境設定 +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>Map のキー値コレクションに精通していること</li> + <li>Java ファイルの 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/ja/training/basics/data-storage/shared-preferences.jd b/docs/html-intl/intl/ja/training/basics/data-storage/shared-preferences.jd new file mode 100644 index 0000000..5f6d5ff --- /dev/null +++ b/docs/html-intl/intl/ja/training/basics/data-storage/shared-preferences.jd @@ -0,0 +1,120 @@ +page.title=キー値セットを保存する +page.tags=データ ストレージ +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">共有の環境設定に書き込む</a></li> + <li><a href="#ReadSharedPreference">共有の環境設定から読み取る</a></li> +</ol> + +<h2>関連ドキュメント</h2> +<ul> + <li><a href="{@docRoot}guide/topics/data/data-storage.html#pref">共有の環境設定を使用する</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.preference.Preference} API と混同しないようにしてください(ただし、これらは + {@link android.content.SharedPreferences} を実装として使用してアプリの設定を保存します)。 + +{@link +android.preference.Preference} API の使用方法については、<a href="{@docRoot}guide/topics/ui/settings.html">設定</a>のガイドを参照してください。</p> + +<h2 id="GetSharedPreferences">SharedPreferences を操作する</h2> + +<p>新しい共有の環境設定ファイルを作成したり、次の 2 つのメソッドのいずれかを呼び出すことにより、既存のファイルにアクセスできます。 +</p> +<ul> + <li>{@link android.content.Context#getSharedPreferences(String,int) +getSharedPreferences()} - 最初のパラメータで指定した名前で識別される共有の環境設定ファイルが複数必要な場合に使用します。 +自分のアプリの任意の +{@link android.content.Context} から、これを呼び出すことができます。</li> + <li>{@link android.app.Activity#getPreferences(int) getPreferences()} - アクティビティに対して共有の環境設定ファイルを 1 つのみ使用する必要がある場合、 +{@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>または、アクティビティ用に共有の環境設定ファイルが 1 つのみ必要な場合、 +{@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">共有の環境設定に書き込む</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">共有の環境設定から読み取る</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/ja/training/basics/intents/filters.jd b/docs/html-intl/intl/ja/training/basics/intents/filters.jd new file mode 100644 index 0000000..5f0d69a --- /dev/null +++ b/docs/html-intl/intl/ja/training/basics/intents/filters.jd @@ -0,0 +1,236 @@ +page.title=他のアプリからのアクティビティの開始を許可する +page.tags=インテント +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>前 2 回のレッスンでは、自分のアプリから別のアプリのアクティビティを開始する場合について焦点を当てました。一方、自分のアプリで別のアプリにとって役に立つかもしれないアクションを実行できる場合について、自分のアプリが他のアプリからのアクション要求に応答できるようにする必要があります。 + +たとえば、友人とメッセージや写真を共有できるソーシャル アプリをビルドした場合、ユーザーが別のアプリから「共有」アクションを開始し、こちら側のアプリを起動してそのアクションを実行できるよう、{@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> 要素を使用して、インテント フィルタでこれを指定します。この要素で 1 つ以上の属性を +使用して、MIME タイプのみ、URI 接頭辞のみ、URI スキームのみ、またはこれらと受け入れられるデータ タイプを示すその他の項目の組み合わせを指定することができます。 + +</p> + <p class="note"><strong>注:</strong> {@link android.net.Uri} データに関する詳細を宣言する必要がない場合(アクティビティが 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>受信するインテントはそれぞれ 1 つのアクションと 1 つのデータ タイプのみを指定しますが、各 +<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>アクションとデータのいずれか 2 つのペアが、各自の動作において相互に排他的である場合は、別のインテント フィルタを作成して、どのデータ タイプと組み合わされたときにとのアクションが受け入れられるかを指定する必要があります。 + +</p> + +<p>たとえば、アクティビティが {@link +android.content.Intent#ACTION_SEND} と {@link +android.content.Intent#ACTION_SENDTO} の両方のインテントに関してテキストと画像の両方を処理するとします。この場合は、次の 2 つのアクションのための 2 つの別々のインテント フィルタを定義する必要があります。なぜなら {@link +android.content.Intent#ACTION_SENDTO} インテントは、URI スキームの {@code send} または {@code sendto} を使用して受信者のアドレスを指定するために、 +{@link android.net.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/ja/training/basics/intents/index.jd b/docs/html-intl/intl/ja/training/basics/intents/index.jd new file mode 100644 index 0000000..ac14fd5 --- /dev/null +++ b/docs/html-intl/intl/ja/training/basics/intents/index.jd @@ -0,0 +1,62 @@ +page.title=他のアプリとの相互操作 +page.tags=インテント、アクティビティ +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">Integrating Application with Intents(ブログ投稿)</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/ja/training/basics/intents/result.jd b/docs/html-intl/intl/ja/training/basics/intents/result.jd new file mode 100644 index 0000000..62c06ae --- /dev/null +++ b/docs/html-intl/intl/ja/training/basics/intents/result.jd @@ -0,0 +1,178 @@ +page.title=アクティビティから結果を取得する +page.tags=インテント +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#startActivity +startActivity()} ではなく) {@link android.app.Activity#startActivityForResult +startActivityForResult()} を呼び出します。</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()} メソッドを呼び出します。このメソッドには、次の 3 つの引数が含まれます。 +</p> + +<ul> + <li>{@link +android.app.Activity#startActivityForResult startActivityForResult()} に渡した要求コード。</li> + <li>第 2 のアクティビティによって指定された結果コード。これは、操作が成功した場合の {@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} の形式がどうなるかを理解する必要があります。これは、結果を返すアクティビティが自分のアクティビティの 1 つである場合には簡単です。 +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.Manifest.permission#READ_CONTACTS} パーミッションを宣言した場合を除き、インテントの {@link android.net.Uri} で指定したもの以外の連絡先はクエリできません。 + +</p> + + + + + + + + + + + + + + + diff --git a/docs/html-intl/intl/ja/training/basics/intents/sending.jd b/docs/html-intl/intl/ja/training/basics/intents/sending.jd new file mode 100644 index 0000000..586bc15 --- /dev/null +++ b/docs/html-intl/intl/ja/training/basics/intents/sending.jd @@ -0,0 +1,256 @@ +page.title=別のアプリにユーザーを送る +page.tags=インテント +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>第 1 回のクラス<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>ここでは他の 2 つのインテント、およびそのアクションと{@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()} メソッドを使用して、特別データを 1 つ以上追加することができます。</p> + +<p>デフォルトでは、インテントに含まれる +{@link android.net.Uri} データに基づいて、インテントに必要な適切な MIME タイプをシステムが決定します。インテントに {@link android.net.Uri} が含まれていない場合は、通常は {@link android.content.Intent#setType setType()} を使用して、インテントに関連するデータのタイプを指定する必要があります。 + +MIME タイプの詳細な指定により、どの種類のアクティビティがインテントを受け取るかが指定されます。 +</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> であれば、少なくとも 1 つのアプリがインテントに応答します。 +<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 に示すように使用するアプリを選択できるダイアログをユーザーに表示します。 + +インテントを処理できるアクティビティが 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 参照)に注意してください。 + +これは、(1 つのウェブ ブラウザのみを使用する傾向にあるユーザーが)ウェブ ページを開いたり、(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> + + + 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> + + + diff --git a/docs/html-intl/intl/pt-br/training/basics/activity-lifecycle/index.jd b/docs/html-intl/intl/pt-br/training/basics/activity-lifecycle/index.jd new file mode 100644 index 0000000..5912058 --- /dev/null +++ b/docs/html-intl/intl/pt-br/training/basics/activity-lifecycle/index.jd @@ -0,0 +1,72 @@ +page.title=Como gerenciar o ciclo de vida da atividade +page.tags=ciclo de vida da atividade +helpoutsWidget=true + +trainingnavtop=true +startpage=true + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + + +<h2>Dependências e pré-requisitos</h2> +<ul> + <li>Como criar um projeto do Android (consulte <a href="{@docRoot}training/basics/firstapp/creating-project.html">Como criar um projeto +do Android</a>)</li> +</ul> + + +<h2>Leia também</h2> +<ul> + <li><a href="{@docRoot}guide/components/activities.html">Atividades</a></li> +</ul> + + +<h2>Tente</h2> + +<div class="download-box"> + <a href="http://developer.android.com/shareables/training/ActivityLifecycle.zip" class="button">Baixar a demonstração</a> + <p class="filename">ActivityLifecycle.zip</p> +</div> + +</div> +</div> + +<p>Conforme o usuário navega, sai e retorna para o seu aplicativo, as +instâncias {@link android.app.Activity} no aplicativo transitam entre diferentes estados no +ciclo de vida. Por exemplo, quando a +atividade começa pela primeira vez, ela fica em primeiro plano no sistema e tem o foco do +usuário. Durante o processo, o sistema Android chama uma série de métodos do ciclo de vida na +atividade, onde você define a interface do usuário e outros componentes. Se o usuário executa uma +ação que inicia outra atividade ou alterna para outro aplicativo, o sistema chama outro conjunto de +métodos do ciclo de vida em sua atividade conforme ela fica em segundo plano (onde a atividade já não +está visível, mas a instância e seu estado permanecem intactos).</p> + +<p>Dentro dos métodos de retorno de chamada do ciclo de vida, você pode declarar como a atividade deve se comportar quando o +usuário sai e retorna da atividade. Por exemplo, se estiver construindo um reprodutor de vídeos de transmissão em sequência, +você pode pausar o vídeo e encerrar a conexão da rede quando o usuário alternar para outro +aplicativo. Quando o usuário retornar, será possível reconectar a rede e permitir que ele reinicie o vídeo +de onde parou.</p> + +<p>Essa lição explica a importância dos métodos de retorno de chamada do ciclo de vida que cada instância {@link +android.app.Activity} recebe e como utilizá-los para que a atividade faça o que o +usuário espera e não consuma recursos do sistema quando não estiver em uso.</p> + +<h2>Lições</h2> + +<dl> + <dt><b><a href="starting.html">Iniciando uma atividade</a></b></dt> + <dd>Aprenda os fundamentos sobre ciclo de vida da atividade, como o usuário pode iniciar seu aplicativo e como +executar atividades básicas de criação.</dd> + <dt><b><a href="pausing.html">Pausando e reiniciando uma atividade</a></b></dt> + <dd>Aprenda sobre o que acontece quando sua atividade é pausada (parcialmente obscurecida) e retomada e o que +fazer durante essas mudanças de estados.</dd> + <dt><b><a href="stopping.html">Interrompendo e reiniciando uma atividade</a></b></dt> + <dd>Aprenda sobre o que acontece quando o usuário sai da atividade completamente e retorna.</dd> + <dt><b><a href="recreating.html">Recriando uma atividade</a></b></dt> + <dd>Aprenda sobre o que acontece quando sua atividade é destruída e como reconstruir o estado +da atividade quando necessário.</dd> +</dl> + diff --git a/docs/html-intl/intl/pt-br/training/basics/activity-lifecycle/pausing.jd b/docs/html-intl/intl/pt-br/training/basics/activity-lifecycle/pausing.jd new file mode 100644 index 0000000..55f772e --- /dev/null +++ b/docs/html-intl/intl/pt-br/training/basics/activity-lifecycle/pausing.jd @@ -0,0 +1,147 @@ +page.title=Pausando e reiniciando uma atividade +page.tags=ciclo de vida da atividade +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> + <div id="tb"> + + <h2>Esta lição ensina a</h2> + <ol> + <li><a href="#Pause">Pausar sua atividade</a></li> + <li><a href="#Resume">Reiniciar sua atividade</a></li> + </ol> + + <h2>Leia também</h2> + <ul> + <li><a href="{@docRoot}guide/components/activities.html">Atividades</a> + </li> + </ul> + +<h2>Tente</h2> + +<div class="download-box"> + <a href="http://developer.android.com/shareables/training/ActivityLifecycle.zip" class="button">Baixar a demonstração</a> + <p class="filename">ActivityLifecycle.zip</p> +</div> + + </div> +</div> + +<p>Durante o uso normal do aplicativo, a atividade em primeiro plano as vezes é obstruída por outros +componentes visuais que causam a <em>pausa</em>. Por exemplo, quando uma atividade + semitransparente é aberta (como uma no estilo de um diálogo), a atividade anterior pausa. Enquanto a +atividade estiver parcialmente visível, mas não for o foco da atividade, ela permanecerá pausada.</p> + +<p>No entanto, se a atividade estiver completamente obstruída e não visível, ela <em>para</em> (o que será +discutido na próxima lição).</p> + +<p>Conforme a atividade entra no estado pausado, o sistema chama o método {@link +android.app.Activity#onPause onPause()} em seu {@link android.app.Activity}, que permite +interromper ações em andamento que não devem continuar enquanto pausadas (como um vídeo) nem persistir +quaisquer informações que devam ser permanentemente salvas caso o usuário continue a sair do aplicativo. Se +o usuário retornar à atividade do estado de pausa, o sistema a reiniciará e chamará o método +{@link android.app.Activity#onResume onResume()}.</p> + +<p class="note"><strong>Observação:</strong> quando a atividade receber o chamado para {@link +android.app.Activity#onPause()}, pode ser um indicativo de que a atividade será pausada por um +momento e o usuário poderá retornar o foco para a atividade. No entanto, geralmente é um indicativo +de que o usuário está saindo da atividade.</p> + +<img src="{@docRoot}images/training/basics/basic-lifecycle-paused.png" /> +<p class="img-caption"><strong>Figura 1.</strong> Quando uma atividade semitransparente obscurece +sua atividade, o sistema chama {@link android.app.Activity#onPause onPause()} e a atividade +aguarda no estado Pausa (1). Se o usuário retornar à atividade enquanto ainda estiver pausada, o + sistema chama {@link android.app.Activity#onResume onResume()} (2).</p> + + +<h2 id="Pause">Pausar sua atividade</h2> + +<p>Quando o sistema chama {@link android.app.Activity#onPause()} para sua atividade, teoricamente +significa que a atividade ainda está parcialmente visível, mas geralmente é um indício +de que o usuário está saindo da atividade e logo entrará em estado Interrompido. Use +o retorno de chamada {@link android.app.Activity#onPause()} para:</p> + +<ul> + <li>Interromper animações ou outras ações em andamento que consomem a CPU.</li> + <li>Consolidar alterações não salvas apenas se o usuário esperar que essas alterações sejam permanentemente salvas ao +saírem (como um rascunho de email).</li> + <li>Liberar recursos do sistema, como receptores, cabos para sensores (como +GPS), ou outros recursos que podem afetar a vida da bateria enquanto a atividade estiver pausada e o usuário +não precisar deles.</li> +</ul> + +<p>Por exemplo, se seu aplicativo utiliza o {@link android.hardware.Camera}, o método +{@link android.app.Activity#onPause()} é um bom local para liberá-los.</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>Geralmente, recomenda-se <strong>não</strong> utilizar {@link android.app.Activity#onPause()} para armazenar +alterações do usuário (como informações pessoais digitadas em um formulário) para armazenamento permanente. O único momento +que se deve persistir na mudança do usuário para armazenamento permanente dentro do {@link android.app.Activity#onPause()} +é quando tem certeza de que o usuário espera que as alterações sejam salvas automaticamente (como ao esboçar um email). +No entanto, evite executar trabalhos de uso intensivo da CPU durante {@link +android.app.Activity#onPause()}, como gravar em um banco de dados, porque isso pode retardar a transição +visível para a próxima atividade (execute operações de encerramento pesadas durante +{@link android.app.Activity#onStop onStop()}).</p> + +<p>Simplifique a quantidade de operações feitas no método {@link android.app.Activity#onPause +onPause()} para permitir uma transição mais rápida para o próximo +destino do usuário se a atividade for realmente interrompida.</p> + +<p class="note"><strong>Observação:</strong> quando a atividade está pausada, a instância {@link +android.app.Activity} é mantida na memória e chamada novamente quando a atividade é retomada. +Não é necessário reiniciar componentes que foram criados durante qualquer método de retorno de chamada que +leve ao estado Reiniciado.</p> + + + +<h2 id="Resume">Reiniciar sua atividade</h2> + +<p>Quando o usuário reinicia a atividade do estado Pausado, o sistema chama o método {@link +android.app.Activity#onResume()}.</p> + +<p>Certifique-se de que o sistema chama esse método sempre que a atividade entrar no primeiro plano, +mesmo quando estiver sendo criada. Dessa forma, implemente o {@link +android.app.Activity#onResume()} para inicializar os componentes liberados durante {@link +android.app.Activity#onPause()} e execute quaisquer outras inicializações que devem ocorrer sempre que a +atividade entrar em estado Reiniciado (como ao iniciar animações e componentes de inicialização usados apenas +enquanto a atividade tiver o foco do usuário).</p> + +<p>O seguinte exemplo de {@link android.app.Activity#onResume()} é uma contrapartida ao +exemplo {@link android.app.Activity#onPause()} acima. Portanto, ele inicializa a câmera que é + liberada quando a atividade entra em pausa.</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/pt-br/training/basics/activity-lifecycle/recreating.jd b/docs/html-intl/intl/pt-br/training/basics/activity-lifecycle/recreating.jd new file mode 100644 index 0000000..7cb122f --- /dev/null +++ b/docs/html-intl/intl/pt-br/training/basics/activity-lifecycle/recreating.jd @@ -0,0 +1,178 @@ +page.title=Recriando uma atividade +page.tags=ciclo de vida da atividade +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> + <div id="tb"> + + <h2>Esta lição ensina a</h2> + <ol> + <li><a href="#SaveState">Salvar o estado da atividade</a></li> + <li><a href="#RestoreState">Restaurar o estado da atividade</a></li> + </ol> + + <h2>Leia também</h2> + <ul> + <li><a href="{@docRoot}training/basics/supporting-devices/screens.html">Compatibilidade + com diferentes telas</a></li> + <li><a href="{@docRoot}guide/topics/resources/runtime-changes.html">Tratar alterações no tempo de execução</a></li> + <li><a href="{@docRoot}guide/components/activities.html">Atividades</a> + </li> + </ul> + + </div> +</div> + +<p>Estas são algumas possibilidade onde a atividade é destruída devido ao comportamento normal do aplicativo, como +quando o usuário pressiona o botão <em>Voltar</em> ou sua atividade sinaliza sua própria destruição +chamando {@link android.app.Activity#finish()}. O sistema também pode destruir a atividade se +for interrompida e não tiver sido utilizada por um longo período de tempo ou a atividade do primeiro plano exigir mais +recursos, então, o sistema deverá fechar processos de segundo plano para recuperar memória.</p> + +<p>Quando a atividade é destruída porque o usuário pressiona <em>Voltar</em> ou a atividade se +encerra, o conceito do sistema de que a instância {@link android.app.Activity} se perde +porque o comportamento indica que a atividade já não é necessária. No entanto, se o sistema destruir +a atividade devido a limitações do sistema (em vez do comportamento normal do aplicativo), embora a instância real +{@link android.app.Activity} tenha se perdido, o sistema lembra que ela existiu de forma que se +o usuário navegar de volta, o sistema criará uma nova instância da atividade usando um conjunto +de dados salvos que descrevem o estado da atividade quando foi destruída. Os dados salvos +utilizados pelo sistema para restaurar o estado anterior é chamado de “estado da instância” e é uma coleção +de pares de valores-chave armazenados no objeto {@link android.os.Bundle}.</p> + +<p class="caution"><strong>Cuidado:</strong> a atividade é destruída e recriada cada vez +que o usuário girar a tela. Quando a tela altera a orientação, o sistema destrói e recria +a atividade de primeiro plano porque a configuração da tela mudou e a atividade talvez precise +carregar recursos alternativos (como o layout).</p> + +<p>Por padrão, o sistema usa o estado da instância {@link android.os.Bundle} para salvar informações +sobre cada objeto {@link android.view.View} em seu layout de atividade (como o valor do texto informado +em um objeto {@link android.widget.EditText}). Assim, se a instância da atividade for destruída e +recriada, o estado do layout é restaurado em seu estado anterior sem +que haja necessidade de códigos. No entanto, sua +atividade pode conter mais informações de estado do que se deseja restaurar, como varáveis de membro que +rastreiam o progresso do usuário na atividade.</p> + +<p class="note"><strong>Observação:</strong> para que o sistema Android restaure o estado das +visualizações em sua atividade, <strong>cada visualização precisa ter uma ID exclusiva</strong>, fornecido pelo atributo +<a href="{@docRoot}reference/android/view/View.html#attr_android:id">{@code +android:id}</a>.</p> + +<p>Para salvar dados adicionais sobre o estado da atividade, substitua +o método de retorno de chamada {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()}. +O sistema chama este método quando o usuário sai da atividade +e transmite o objeto{@link android.os.Bundle}, que será salvo +caso a atividade seja destruída inesperadamente. Se +o sistema precisar recriar a instância da atividade posteriormente, transmitirá o mesmo objeto {@link +android.os.Bundle} para ambos os métodos {@link android.app.Activity#onRestoreInstanceState +onRestoreInstanceState()} e {@link android.app.Activity#onCreate onCreate()} +.</p> + +<img src="{@docRoot}images/training/basics/basic-lifecycle-savestate.png" /> +<p class="img-caption"><strong>Figura 2.</strong> Conforme o sistema começa a interromper a atividade, ele +chama {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()}(1) para que você possa especificar +outros dados de estado que gostaria de salvar caso a instância {@link android.app.Activity} precise ser +recriada. +Se a atividade for destruída e a mesma instância precisar ser recriada, o sistema transmite os dados do +estado definidos em (1) para ambos os métodos {@link android.app.Activity#onCreate onCreate()} +(2) e {@link android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} +(3).</p> + + + +<h2 id="SaveState">Salvar o estado da atividade</h2> + +<p>Conforme a atividade é interrompida, o sistema chama {@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()} para que a atividade possa salvar informações de estado com uma coleção de pares de +valor-chave. A implementação padrão deste método salva informações sobre o estado da hierarquia de exibições +da atividade, como o texto em um widget {@link android.widget.EditText} ou a posição de rolagem +de um {@link android.widget.ListView}.</p> + +<p>Para salvar informações de estado adicionais para a atividade, +implemente {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} e adicione +pares de valor-chave ao objeto {@link android.os.Bundle}. Por exemplo:</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>Cuidado:</strong> sempre chame a implementação de superclasse de {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} para que a implementação padrão +possa salvar o estado da hierarquia de exibições.</p> + + + +<h2 id="RestoreState">Restaurar o estado da atividade</h2> + +<p>Quando a atividade é recriada depois de ter sido destruída, é possível recuperar o estado +salvo do {@link android.os.Bundle} que o sistema +transmitiu a atividade. Ambos os métodos de retorno de chamada {@link android.app.Activity#onCreate onCreate()} e {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} recebem +o mesmo {@link android.os.Bundle} que contém informações do estado da instância.</p> + +<p>Como o método {@link android.app.Activity#onCreate onCreate()} é chamado se o +sistema estiver criando uma nova instância da atividade ou recriando uma anterior, verifique +se o {@link android.os.Bundle} do estado é null antes de tentar realizar a leitura. Se for null, +o sistema estará criando uma nova instância da atividade em vez de restaurar uma anterior +que tenha sido destruída.</p> + +<p>Por exemplo, esta é uma forma de restaurar alguns dados de estado no {@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>Em vez de restaurar o estado durante {@link android.app.Activity#onCreate onCreate()}, você +pode implementar {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()}, que o sistema chama +depois do método {@link android.app.Activity#onStart()}. O sistema chama {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} se houver um estado +salvo para ser restaurado. Portanto, não é necessário verificar se {@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>Cuidado:</strong> sempre chame a implementação de superclasse de {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} para que a implementação padrão +possa restaurar o estado da hierarquia de exibições.</p> + +<p>Para saber mais sobre recriação de atividades devido a +um evento de reinicialização no tempo de execução (como quando a tela gira), consulte <a href="{@docRoot}guide/topics/resources/runtime-changes.html">Tratar alterações no tempo de execução</a>.</p> + diff --git a/docs/html-intl/intl/pt-br/training/basics/activity-lifecycle/starting.jd b/docs/html-intl/intl/pt-br/training/basics/activity-lifecycle/starting.jd new file mode 100644 index 0000000..1f8f080 --- /dev/null +++ b/docs/html-intl/intl/pt-br/training/basics/activity-lifecycle/starting.jd @@ -0,0 +1,285 @@ +page.title=Iniciando uma atividade +page.tags=ciclo de vida de atividade +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + + +<div id="tb-wrapper"> + <div id="tb"> + + <h2>Esta lição ensina a</h2> +<ol> + <li><a href="#lifecycle-states">Entender o ciclo de vida do retorno de chamada</a></li> + <li><a href="#launching-activity">Especificar a atividade da tela de início do aplicativo</a></li> + <li><a href="#Create">Criar uma nova instância</a></li> + <li><a href="#Destroy">Destruir a atividade</a></li> +</ol> + + <h2>Leia também</h2> + <ul> + <li><a href="{@docRoot}guide/components/activities.html">Atividades</a></li> + </ul> + +<h2>Tente</h2> + +<div class="download-box"> + <a href="http://developer.android.com/shareables/training/ActivityLifecycle.zip" class="button">Baixar a demonstração</a> + <p class="filename">ActivityLifecycle.zip</p> +</div> + + </div> +</div> + +<p>Diferente de outros paradigmas de programação em que os aplicativos são lançados com um método {@code main()}, o +sistema Android inicia o código em uma instância {@link android.app.Activity} chamando métodos específicos +de retorno de chamada que correspondem a determinados estágios do seu +ciclo de vida. Há uma sequência de métodos de retorno de chamada que iniciam uma atividade e uma sequência de métodos +de retorno de chamada que interrompem uma atividade.</p> + +<p>Essa lição proporciona uma visão geral dos métodos do ciclo de vida mais importantes e mostra como +lidar com o primeiro retorno de chamada do ciclo de vida que cria uma nova instância de sua atividade.</p> + + + +<h2 id="lifecycle-states">Entender o ciclo de vida do retorno de chamada</h2> + +<p>Durante a vida de uma atividade, o sistema chama um núcleo principal de métodos do ciclo de vida em +uma sequência parecida com uma pirâmide em degraus. Isto é, cada estágio do +ciclo de vida da atividade corresponde a um degrau da pirâmide. Conforme o sistema cria uma nova instância de atividade, +cada método de retorno de chamada leva o estado da atividade um degrau acima. O topo da pirâmide é o +ponto em que a atividade funciona em primeiro plano e o usuário pode interagir com ela.</p> + +<p>Conforme o usuário começa a sair da atividade, o sistema chama outros métodos que a movem +de volta em direção à base da pirâmide para desfazer a atividade. Em alguns casos, a atividade é movida +parcialmente em direção à base da pirâmide e aguarda (como quando o usuário muda para outro aplicativo), no +ponto em que a atividade é movida novamente em direção ao topo (se o usuário retornar à atividade) e +reinicia de onde o usuário parou.</p> + + +<img src="{@docRoot}images/training/basics/basic-lifecycle.png" /> +<p class="img-caption"><strong>Figura 1.</strong> Ilustração simplificada do ciclo de vida +da atividade, expressa como pirâmide em degraus. Isso mostra que, para cada retorno de chamada usado para levar +uma atividade ao estado de Reiniciado, em direção ao topo, há um método de retorno de chamada +que leva a atividade um degrau abaixo. A atividade também pode retornar ao estado de retomada do +estado de Pausado e Interrompido.</p> + + +<p>Dependendo da complexidade de sua atividade, não há necessidade de implementar todos os +métodos do ciclo de vida. No entanto, é importante compreender cada um e implementar apenas aqueles que +garantem que seu aplicativo tenha o desempenho esperado pelo usuário. A implementação adequada dos métodos do ciclo de vida da atividade +garante que seu aplicativo tenha um bom desempenho em vários sentidos, incluindo que:</p> +<ul> + <li>Não apresente falhas se o usuário receber uma chamada telefônica ou mudar para outro aplicativo +enquanto estiver usando o seu aplicativo.</li> + <li>Não consuma recursos importantes do sistema quando o usuário não estiver utilizando +ativamente o aplicativo.</li> + <li>Não perca o progresso do usuário se ele sair do aplicativo e retornar +mais tarde.</li> + <li>Não apresente falhas nem perca o progresso do usuário quando a orientação da tela mudar entre +paisagem e retrato.</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>Como você aprenderá nas lições seguintes, há várias situações de transições de +atividades entre estados diferentes, como ilustrado na figura 1. No entanto, apenas três +desses estados podem ser estáticos. Isto é, a atividade pode existir em um de apenas três estados por +um período de tempo maior:</p> +<dl> + <dt>Reiniciado</dt> + <dd>Neste estado, a atividade está no primeiro plano e o usuário consegue interagir. +(Também chamado de estado de funcionamento.)</dd> + <dt>Pausado</dt> + <dd>Neste estado, a atividade está parcialmente obscurecida por outra atividade. A +outra atividade que está em primeiro plano é semitransparente ou não cobre totalmente a tela. A +atividade pausada não recebe entrada do usuário e não executa nenhum código. + <dt>Interrompido</dt> + <dd>Neste estado, a atividade está completamente oculta e não é visível para o usuário. Considera-se +que está em segundo plano. Enquanto interrompido, a instância da atividade e todas as +informações de estado, como variáveis de membro, são retidas, mas não é possível executar nenhum código.</dd> +</dl> + +<p>Os outros estados (Criado e Iniciado) são temporários e o sistema rapidamente se move rapidamente de um +estado a outro chamando o próximo método de retorno de chamada do ciclo de vida. Isto é, depois que o sistema chama +{@link android.app.Activity#onCreate onCreate()}, ele rapidamente chama {@link +android.app.Activity#onStart()}, que é seguido por {@link +android.app.Activity#onResume()}.</p> + +<p>Isso é tudo que precisamos dizer sobre o ciclo de vida básico de atividades. Agora você aprenderá sobre alguns dos +comportamentos específicos do ciclo de vida.</p> + + + +<h2 id="launching-activity">Especificar a atividade da tela de início do aplicativo</h2> + +<p>Quando o usuário seleciona seu aplicativo na tela inicial, o sistema chama o método {@link +android.app.Activity#onCreate onCreate()} para {@link android.app.Activity} no aplicativo +que foi declarado como atividade da “inicializador” (ou “principal”). Essa é a atividade que serve como +ponto de entrada principal da interface do usuário do aplicativo.</p> + +<p>É possível definir qual atividade será usada como principal no arquivo manifesto do Android, <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">{@code AndroidManifest.xml}</a>, que está +na raiz do diretório do seu projeto.</p> + +<p>A principal atividade do aplicativo deve ser declarada no manifesto com um <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a> que inclui a ação {@link +android.content.Intent#ACTION_MAIN MAIN} e categoria +{@link android.content.Intent#CATEGORY_LAUNCHER LAUNCHER}. Por exemplo:</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>Observação:</strong> ao criar um novo projeto do Android com ferramentas +SDK Android, os arquivos padrões do projeto incluem uma classe {@link android.app.Activity} que é declarada no +manifesto com esse filtro.</p> + +<p>Se a ação {@link android.content.Intent#ACTION_MAIN MAIN} nem a categoria +{@link android.content.Intent#CATEGORY_LAUNCHER LAUNCHER} forem declaradas para uma de suas +atividades, o ícone do aplicativo não aparecerá na lista de aplicativos da tela inicial.</p> + + + +<h2 id="Create">Criar uma nova instância</h2> + +<p>A maioria dos aplicativos tem várias atividades que permitem ao usuário realizar diferentes ações. +Tanto para a atividade principal criada quando o usuário clica no ícone do aplicativo quanto uma +atividade diferente que o aplicativo inicia em resposta à ação do usuário, o sistema cria +cada nova instância do {@link android.app.Activity} chamando o método {@link +android.app.Activity#onCreate onCreate()}.</p> + +<p>Implemente o método {@link android.app.Activity#onCreate onCreate()} para realizar a lógica básica +de inicialização do aplicativo que deve acontecer apenas uma vez na vida completa da atividade. Por +exemplo, sua implementação do {@link android.app.Activity#onCreate onCreate()} deve definir a +interface do usuário e possivelmente instanciar algumas variáveis de escopo de classe.</p> + +<p>Por exemplo, o seguinte modelo do método {@link android.app.Activity#onCreate onCreate()} + mostra alguns códigos que realizam configurações fundamentais para a atividade, como +declarar a interface do usuário (definida em arquivo de layout XML), definir variáveis de membro +e configurar parte da interface do usuário.</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>Cuidado:</strong> utilizar {@link android.os.Build.VERSION#SDK_INT} para +evitar que sistemas antigos executem novas APIs funciona apenas no Android 2.0 (API Nível +5) e versões posteriores. Versões mais antigas encontram uma exceção no tempo de execução.</p> + +<p>Depois que o {@link android.app.Activity#onCreate onCreate()} termina de executar, o sistema +chama os métodos {@link android.app.Activity#onStart()} e {@link android.app.Activity#onResume()} + em rápida sucessão. A atividade nunca reside nos estados Criado ou Iniciado. Tecnicamente, a +atividade se torna visível para o usuário quando {@link android.app.Activity#onStart()} é chamado, mas +{@link android.app.Activity#onResume()} é executado e a atividade permanece no estado Reiniciado +até que algo diferente ocorra, como o recebimento de uma chamada telefônica, o usuário +navegar para outra atividade ou a tela do dispositivo desligar.</p> + +<p>Nas próximas lições, você verá como outros métodos de inicialização, {@link +android.app.Activity#onStart()} e {@link android.app.Activity#onResume()}, podem ajudar +no ciclo de vida da atividade quando utilizado para reiniciar a atividade dos estados Pausado e Interrompido.</p> + +<p class="note"><strong>Observação:</strong> o método {@link android.app.Activity#onCreate onCreate()} +inclui um parâmetro chamado <code>savedInstanceState</code>, que será discutido mais adiante +na lição <a href="recreating.html">Recriando uma atividade</a>.</p> + + +<img src="{@docRoot}images/training/basics/basic-lifecycle-create.png" /> +<p class="img-caption"><strong>Figura 2.</strong> Outra ilustração da estrutura do ciclo de vida da +atividade com ênfase nos três retornos de chamada principais que o sistema chama na sequência quando +cria uma nova instância da atividade: {@link android.app.Activity#onCreate onCreate()}, {@link +android.app.Activity#onStart()} e{@link android.app.Activity#onResume()}. Depois que esta sequência de +retornos de chamada for concluída, a atividade chega ao estado Reiniciado em que os usuários podem interagir com a +atividade até que mude para outra atividade.</p> + + + + + + + +<h2 id="Destroy">Destruir a atividade</h2> + +<p>Embora o retorno de chamada do primeiro ciclo de vida da atividade seja {@link android.app.Activity#onCreate +onCreate()}, o último retorno de chamada será {@link android.app.Activity#onDestroy}. O sistema chama +este método na atividade como o último +sinal de que a instância da atividade está sendo completamente removida da memória do sistema.</p> + +<p>A maioria dos aplicativos não exige a implementação desse método porque referências de classe locais são destruídas +com a atividade, que realiza a maior parte da limpeza durante {@link +android.app.Activity#onPause} e {@link android.app.Activity#onStop}. No entanto, se a +atividade incluir threads de segundo plano criados durante {@link +android.app.Activity#onCreate onCreate()} ou outro recurso de longa execução que pode +vazar memória se não forem fechados adequadamente, você deve finalizá-los durante {@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>Observação:</strong> o sistema chama {@link android.app.Activity#onDestroy} +depois de ter chamado {@link android.app.Activity#onPause} e {@link +android.app.Activity#onStop} em qualquer situação, exceto uma: quando você chama {@link +android.app.Activity#finish()} pelo método {@link android.app.Activity#onCreate onCreate()} +. Em alguns casos, como quando a atividade assume a posição temporária de tomadora de decisões para +lançar outra atividade, chame {@link android.app.Activity#finish()} pelo {@link +android.app.Activity#onCreate onCreate()} para destruir a atividade. Nesse caso, o sistema +chama imediatamente {@link android.app.Activity#onDestroy} sem chamar qualquer outro + método do ciclo de vida.</p> diff --git a/docs/html-intl/intl/pt-br/training/basics/activity-lifecycle/stopping.jd b/docs/html-intl/intl/pt-br/training/basics/activity-lifecycle/stopping.jd new file mode 100644 index 0000000..2eba377 --- /dev/null +++ b/docs/html-intl/intl/pt-br/training/basics/activity-lifecycle/stopping.jd @@ -0,0 +1,187 @@ +page.title=Interrompendo e reiniciando uma atividade +page.tags=ciclo de vida da atividade +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> + <div id="tb"> + + <h2>Esta lição ensina a</h2> + <ol> + <li><a href="#Stop">Interromper sua atividade</a></li> + <li><a href="#Start">Iniciar/reiniciar sua atividade</a></li> + </ol> + + <h2>Leia também</h2> + <ul> + <li><a href="{@docRoot}guide/components/activities.html">Atividades</a> + </li> + </ul> + +<h2>Tente</h2> + +<div class="download-box"> + <a href="http://developer.android.com/shareables/training/ActivityLifecycle.zip" class="button">Baixar a demonstração</a> + <p class="filename">ActivityLifecycle.zip</p> +</div> + + </div> +</div> + +<p>Interromper e reiniciar adequadamente sua atividade é um processo importante no ciclo de vida da atividade +que garante que o usuário perceba que o aplicativo está sempre ativo e não perca o progresso. +Há alguns cenários fundamentais em que sua atividade é interrompida e reiniciada:</p> + +<ul> + <li>O usuário abre a janela Aplicativos Utilizados Recentemente e alterna de um aplicativo a outro. A +atividade em seu aplicativo atualmente em primeiro plano é interrompida. Se o usuário retornar ao +aplicativo pelo ícone de inicialização da tela inicial ou da janela Aplicativos Utilizados Recentemente, a atividade é reiniciada.</li> + <li>O usuário executar uma ação em seu aplicativo que inicia uma nova atividade. A atividade atual +é interrompida quando a segunda é criada. Se o usuário pressionar o botão <em>Voltar</em> +, a primeira atividade será reiniciada.</li> + <li>Se o usuário receber uma chamada telefônica enquanto estiver usando o seu aplicativo.</li> +</ul> + +<p>A classe {@link android.app.Activity} fornece dois métodos do ciclo de vida, {@link +android.app.Activity#onStop()} e {@link android.app.Activity#onRestart()}, que permite que você +decida exatamente como a atividade responderá à interrupção e reinicialização. Diferentemente do estado pausado, +que identifica obstruções parciais da interface de usuário, o estado interrompido garante que a interface não fique +visível e que o foco do usuário permaneça em outra atividade (ou mesmo outro aplicativo).</p> + +<p class="note"><strong>Observação:</strong> como o sistema retém a instância {@link android.app.Activity} +na memória quando interrompida, talvez não seja necessário implementar os métodos +{@link android.app.Activity#onStop()} e {@link android.app.Activity#onRestart()} (ou mesmo {@link +android.app.Activity#onStart()}. Para a maioria das atividades que são relativamente simples, a +atividade será interrompida e reiniciada normalmente e talvez seja necessário apenas usar {@link +android.app.Activity#onPause()} para pausar ações em andamento e desconectar dos recursos do sistema.</p> + +<img src="{@docRoot}images/training/basics/basic-lifecycle-stopped.png" /> +<p class="img-caption"><strong>Figura 1.</strong> Quando o usuário sai da atividade, o sistema +chama {@link android.app.Activity#onStop onStop()} para interrompê-la (1). Se o usuário retornar +enquanto a atividade estiver interrompida, o sistema chama {@link android.app.Activity#onRestart onRestart()} +(2), rapidamente seguido por {@link android.app.Activity#onStart onStart()} (3) e {@link +android.app.Activity#onResume()} (4). Observe que independentemente do que tenha causado a interrupção +da atividade, o sistema sempre chama {@link android.app.Activity#onPause onPause()} antes de chamar {@link +android.app.Activity#onStop onStop()}.</p> + + + +<h2 id="Stop">Interromper sua atividade</h2> + +<p>Quando sua atividade recebe uma chamada para o método {@link android.app.Activity#onStop()}, não +está mais visível e deve liberar quase todos os recursos que não foram necessários enquanto o usuário não +estiver utilizando. Quando a atividade for interrompida, o sistema pode destruir a instância se for necessário para +recuperar memória do sistema. Em casos extremos, o sistema pode simplesmente desligar o processo do aplicativo sem +chamar o retorno de chamada{@link android.app.Activity#onDestroy()} final da atividade, portanto, é importante +usar {@link android.app.Activity#onStop()} para liberar recursos que podem vazar memória.</p> + +<p>Embora o método {@link android.app.Activity#onPause onPause()} seja chamado antes de +{@link android.app.Activity#onStop()}, use {@link android.app.Activity#onStop onStop()} +para executar operações de desligamento maiores, que exigem mais da CPU, como escrever informação no +banco de dados.</p> + +<p>Por exemplo, esta é uma implementação de {@link android.app.Activity#onStop onStop()} que +salva os conteúdos de uma nota de rascunho no armazenamento persistente:</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>Quando sua atividade é interrompida, o objeto {@link android.app.Activity} é mantido na memória +e é chamado novamente quando a atividade é reiniciada. Não é necessário reiniciar componentes que foram +criados durante qualquer método de retorno de chamada que leve ao estado Reiniciado. O sistema também +tem controle do estado atual de cada {@link android.view.View} no layout. Portanto, se o usuário +inserir um texto em um widget do {@link android.widget.EditText}, o conteúdo será retido e você não +precisará salvar e restaurar.</p> + +<p class="note"><strong>Observação:</strong> mesmo que o sistema destrua sua atividade enquanto estiver interrompida, +ele ainda mantém o estado do objeto {@link android.view.View} (como texto em {@link +android.widget.EditText}) em um {@link android.os.Bundle} (um blob de pares de valores-chave) e os restaura +se o usuário navegar de volta para a mesma instância da atividade (a <a href="recreating.html">próxima lição</a> falará mais sobre uso do {@link android.os.Bundle} para salvar +outros dados do estado caso sua atividade seja destruída e recriada).</p> + + + +<h2 id="Start">Iniciar/reiniciar sua atividade</h2> + +<p>Quando sua atividade voltar ao primeiro plano do estado interrompido, ela recebe uma chamada para +{@link android.app.Activity#onRestart()}. O sistema também chama o método {@link +android.app.Activity#onStart()}, que acontece sempre que a atividade se tornar visível +(ao ser reiniciada ou criada pela primeira vez). No entanto, o método {@link +android.app.Activity#onRestart()} é chamado apenas quando a atividade é reiniciada do +estado interrompido. Portanto, é possível usá-la para executar trabalhos de restauração especiais necessários apenas se +a atividade tiver sido interrompida, mas não destruída.</p> + +<p>Dificilmente um aplicativo precisará usar {@link android.app.Activity#onRestart()} para restaurar +o estado da atividade. Portanto, não há diretrizes para este método que se apliquem à +população geral de aplicativos. Contudo, como espera-se que o método {@link android.app.Activity#onStop()} +limpe todos os recursos da atividade, será necessário instanciá-los +quando a atividade for reiniciada. Ainda assim, será necessário instanciá-los quando a atividade for criada +pela primeira vez (quando não houver instâncias existentes da atividade). Por esse motivo, recomenda-se +utilizar o método de retorno de chamada {@link android.app.Activity#onStart()} como contrapartida +ao método {@link android.app.Activity#onStop()}, porque o sistema chama {@link +android.app.Activity#onStart()} quando cria sua atividade e quando reinicia a +atividade do estado interrompido.</p> + +<p>Por exemplo, como o usuário pode ter ficado longe do aplicativo por um longo período +, o método {@link android.app.Activity#onStart()} é uma boa forma de confirmar se +os recursos do sistema exigidos estão habilitados:</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>Quando o sistema destrói a atividade, ele chama o método {@link android.app.Activity#onDestroy()} +para seu {@link android.app.Activity}. Como talvez a maior parte de +seus recursos tenha sido liberada com {@link android.app.Activity#onStop()}, no momento em que você receber a chamada para {@link +android.app.Activity#onDestroy()}, não haverá muito a ser feito pelos aplicativos. Esse método é sua +última chance de limpar os recursos que levariam a vazamento de memória. Portanto, certifique-se de que +outros threads sejam destruídos e outras ações de longa execução como o rastreamento de métodos também estejam +interrompidas.</p> + diff --git a/docs/html-intl/intl/pt-br/training/basics/data-storage/databases.jd b/docs/html-intl/intl/pt-br/training/basics/data-storage/databases.jd new file mode 100644 index 0000000..37d0d43 --- /dev/null +++ b/docs/html-intl/intl/pt-br/training/basics/data-storage/databases.jd @@ -0,0 +1,317 @@ +page.title=Salvando dados em bancos de dados do SQL +page.tags=armazenamento de dados +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>Esta lição ensina a</h2> +<ol> + <li><a href="#DefineContract">Definir um esquema e contrato</a></li> + <li><a href="#DbHelper">Criar um banco de dados usando de um SQL Helper</a></li> + <li><a href="#WriteDbRow">Colocar informações no banco de dados</a></li> + <li><a href="#ReadDbRow">Ler informações de um banco de dados</a></li> + <li><a href="#DeleteDbRow">Excluir informações de um banco de dados</a></li> + <li><a href="#UpdateDbRow">Atualizar um banco de dados</a></li> +</ol> + +<h2>Leia também</h2> +<ul> + <li><a href="{@docRoot}guide/topics/data/data-storage.html#db">Usando bancos de dados</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>Salvar dados em um banco de dados é ideal para dados que se repetem ou estruturados, +como informações de contato. Esta lição assume que você esteja +familiarizado com bancos de dados do SQL em gera, e o ajuda a começar a trabalhar com bancos de dados +do SQLite no Android. As APIs necessárias para usar um banco de dados +no Android estão disponíveis no pacote {@link android.database.sqlite}.</p> + + +<h2 id="DefineContract">Definir um esquema e contrato</h2> + +<p>Um dos princípios mais importantes de bancos de dados do SQL é o esquema: uma declaração +formal de como o banco de dados é organizado. O esquema é refletido nas declarações SQL +usadas na criação do banco de dados. É aconselhável +criar uma classe de acompanhamento, conhecida como classe de <em>contrato</em>, que especifica claramente +o layout do esquema de forma sistemática e autodocumentada.</p> + +<p>Uma classe de contrato é o contêiner das constantes que definem nomes para URIs, +tabelas e colunas. A classe de contrato permite usar as mesmas constantes +em outras classes no mesmo pacote. Permite que você altere o nome da +coluna em um local e que a mudança se propague pelos seus códigos.</p> + +<p>Uma boa forma de organizar uma classe de contrato é colocar definições que sejam +globais para todo o banco de dados no nível raiz da classe. Crie uma classe +interna para cada tabela que enumera suas colunas.</p> + +<p class="note"><strong>Observação:</strong> implementando a interface {@link +android.provider.BaseColumns}, sua classe interior pode herdar um campo-chave +primário chamado {@code _ID} que algumas classes do Android, como adaptadores de cursor, esperam + que você tenha. Não é uma obrigatório, mas pode ajudar para um trabalho mais harmonioso com o banco de dados +no framework do Android.</p> + +<p>Por exemplo, este trecho define o nome da tabela e das colunas para uma +única tabela:</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">Criar um banco de dados usando de um SQL Helper</h2> + +<p>Uma vez definidos o visual dos bancos de dados, implemente métodos +que criam e cuidam do banco de dados e das tabelas. Aqui estão algumas declarações +comuns para criar e exclui a tabela:</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>Da mesma forma você salva arquivos no <a href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">armazenamento +interno</a> do dispositivo, o Android armazena seu banco de dados no espaço privado do disco associado +ao aplicativo. Seus dados estão protegidos porque, por padrão, essa área não +pode ser acessada por outros aplicativos.</p> + +<p>Um conjunto de APIs está disponível na classe {@link +android.database.sqlite.SQLiteOpenHelper}. +Ao usar esta classe para obter referências para seu banco de dados, o sistema +realiza operações +de possível longa execução para criar e atualizar o banco de dados apenas quando +necessário e <em>não durante a inicialização do aplicativo</em>. Basta chamar +{@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} ou +{@link android.database.sqlite.SQLiteOpenHelper#getReadableDatabase}.</p> + +<p class="note"><strong>Observação:</strong> devido à possibilidade de serem de longa execução, +certifique-se que chamar {@link +android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} ou {@link +android.database.sqlite.SQLiteOpenHelper#getReadableDatabase} em um thread de segundo plano, +como {@link android.os.AsyncTask} ou {@link android.app.IntentService}.</p> + +<p>Para usar {@link android.database.sqlite.SQLiteOpenHelper}, crie uma subclasse que +substitua os métodos de retorno de chamada {@link +android.database.sqlite.SQLiteOpenHelper#onCreate onCreate()}, {@link +android.database.sqlite.SQLiteOpenHelper#onUpgrade onUpgrade()} e {@link +android.database.sqlite.SQLiteOpenHelper#onOpen onOpen()}. Também é possível +implementar {@link android.database.sqlite.SQLiteOpenHelper#onDowngrade onDowngrade()}, +mas não é obrigatório.</p> + +<p>Por exemplo, esta é uma implementação de {@link +android.database.sqlite.SQLiteOpenHelper} que utiliza alguns dos comandos exibidos abaixo:</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>Para acessar seu banco de dados, instancie sua subclasse de {@link +android.database.sqlite.SQLiteOpenHelper}:</p> + +<pre> +FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext()); +</pre> + + + + +<h2 id="WriteDbRow">Colocar informações no banco de dados</h2> + +<p>Coloque dados no banco de dados transmitindo um objeto {@link android.content.ContentValues} + para o método {@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>Este primeiro argumento para {@link android.database.sqlite.SQLiteDatabase#insert insert()} +é apenas o nome da tabela. O segundo argumento fornece +o nome de uma coluna em que o framework pode inserir NULL caso o +{@link android.content.ContentValues} esteja vazio (se você definir como {@code "null"}, +o framework não inserirá uma linha quando não houver valores).</p> + + + + +<h2 id="ReadDbRow">Ler informações de um banco de dados</h2> + +<p>Para ler de um banco de dados, utilize o método {@link android.database.sqlite.SQLiteDatabase#query query()} +, transmitindo seus critérios de seleção e colunas desejadas. +O método combina elementos de {@link android.database.sqlite.SQLiteDatabase#insert insert()} + e {@link android.database.sqlite.SQLiteDatabase#update update()}, exceto que a lista da coluna +define os dados que serão analisados e não os dados a serem inseridos. Os resultados da consulta +são retornados em um objeto {@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>Para ver uma linha no cursor, utilize um dos métodos de movimento {@link android.database.Cursor}, +que sempre deverão ser chamados antes de começar a ler valores. Geralmente, deve-se iniciar +chamando {@link android.database.Cursor#moveToFirst}, que coloca a “posição leitura” na +primeira entrada nos resultados. Para cada linha, você pode ler um valor de coluna chamando um dos métodos GET +{@link android.database.Cursor}, como {@link android.database.Cursor#getString +getString()} ou {@link android.database.Cursor#getLong getLong()}. Para cada um dos métodos GET, +você deve transmitir a posição de índice da coluna desejada, que pode ser obtida chamando +{@link android.database.Cursor#getColumnIndex getColumnIndex()} ou +{@link android.database.Cursor#getColumnIndexOrThrow getColumnIndexOrThrow()}. +Por exemplo:</p> + +<pre> +cursor.moveToFirst(); +long itemId = cursor.getLong( + cursor.getColumnIndexOrThrow(FeedEntry._ID) +); +</pre> + + + + +<h2 id="DeleteDbRow">Excluir informações de um banco de dados</h2> + +<p>Para excluir linhas de uma tabela, forneça os critérios de seleção que as +identifique. A API do banco de dados oferece um mecanismo para criar +critérios de seleção que protegem contra injeção do SQL. O mecanismo divide a +especificação da seleção em uma cláusula e argumentos de seleção. A +cláusula define a coluna a se olhar e também permite combinar testes de +coluna. Os argumentos são valores para testes comparativos que são dependentes dentro de uma cláusula. +Como o resultado não é tratado da mesma forma que uma declaração SQL comum, ele fica +imune à injeção de 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">Atualizar um banco de dados</h2> + +<p>Quando precisar alterar um subconjunto dos valores de seu banco de dados, utilize o método {@link +android.database.sqlite.SQLiteDatabase#update update()}.</p> + +<p>A atualização da tabela combina a sintaxe de valores do conteúdo de {@link +android.database.sqlite.SQLiteDatabase#insert insert()} com a sintaxe {@code where} de +{@link android.database.sqlite.SQLiteDatabase#delete delete()}.</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/pt-br/training/basics/data-storage/files.jd b/docs/html-intl/intl/pt-br/training/basics/data-storage/files.jd new file mode 100644 index 0000000..d071d39 --- /dev/null +++ b/docs/html-intl/intl/pt-br/training/basics/data-storage/files.jd @@ -0,0 +1,379 @@ +page.title=Salvando arquivos +page.tags=armazenamento de dados +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>Esta lição ensina a</h2> +<ol> + <li><a href="#InternalVsExternalStorage">Escolher entre armazenamento interno e externo</a></li> + <li><a href="#GetWritePermission">Obter permissões para armazenamento externo</a></li> + <li><a href="#WriteInternalStorage">Salvar arquivos em armazenamento interno</a></li> + <li><a href="#WriteExternalStorage">Salvar arquivos em armazenamento externo</a></li> + <li><a href="#GetFreeSpace">Consultar espaço livre</a></li> + <li><a href="#DeleteFile">Excluir um arquivo</a></li> +</ol> + +<h2>Leia também</h2> +<ul> + <li><a href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">Utilizando armazenamento +interno</a></li> + <li><a href="{@docRoot}guide/topics/data/data-storage.html#filesExternal">Utilizando armazenamento +externo</a></li> +</ul> + +</div> +</div> + +<p>O Android usa um sistema de arquivos +parecido com sistemas de arquivos em disco de outras plataformas. Esta lição explica +como trabalhar com o sistema de arquivos Android para ler e gravar arquivos com APIs {@link java.io.File} +.</p> + +<p>Um objeto {@link java.io.File} é adequado para ler ou gravar grandes quantidades de dados em +ordens crescente sem pular nenhum item. Por exemplo, é bom para arquivos de imagens ou +qualquer troca executada por uma rede.</p> + +<p>Esta lição demonstra como executar tarefas básicas relacionadas a arquivos em seu aplicativo. +Assume-se que você já esteja familiarizado com os fundamentos do sistema de arquivos Linux e com +APIs de entrada/saída de arquivos padrão no {@link java.io}.</p> + + +<h2 id="InternalVsExternalStorage">Escolher entre armazenamento interno e externo</h2> + +<p>Todos os dispositivos Android têm duas áreas de armazenamento de arquivos: armazenamento “interno” e “externo”. Estes nomes +têm origem no início do Android, quando a maior parte de seus dispositivos oferecia memória embutida não volátil +(armazenamento interno), além de uma mídia de armazenamento removível, como micro cartões SD (armazenamento externo). +Alguns dispositivos dividem o espaço de armazenamento permanente em partições “interna” e “externa”. Assim, mesmo +sem uma mídia de armazenamento removível, sempre há dois espaços de armazenamento e +o comportamento da API permanece inalterado independentemente da remoção do armazenamento externo. +A lista a seguir resume as principais informações sobre cada tipo de espaço de armazenamento.</p> + +<div class="col-5" style="margin-left:0"> +<p><b>Armazenamento interno:</b></p> +<ul> +<li>Está sempre disponível.</li> +<li>Por padrão, os arquivos salvos aqui podem apenas ser acessados pelo seu aplicativo.</li> +<li>Quando o usuário desinstala o aplicativo, o sistema exclui todos os arquivos do aplicativo salvos no +armazenamento interno.</li> +</ul> +<p>O armazenamento interno funciona melhor quando você deseja garantir que o usuário nem outros aplicativos +tenham acesso aos seus arquivos.</p> +</div> + +<div class="col-7" style="margin-right:0"> +<p><b>Armazenamento externo:</b></p> +<ul> +<li>Não está sempre disponível porque o usuário pode montar o armazenamento externo, como um armazenamento USB, + e em alguns casos, removê-lo do dispositivo.</li> +<li>É de leitura universal, ou seja, +arquivos salvos aqui podem ser lidos em outros dispositivos.</li> +<li>Quando o usuário desinstala o aplicativo, o sistema exclui todos os arquivos do aplicativo salvos aqui +apenas se estiverem salvos no diretório de {@link android.content.Context#getExternalFilesDir +getExternalFilesDir()}.</li> +</ul> +<p>O armazenamento externo é o melhor +local para arquivos que não exigem acesso restrito e para os arquivos que você deseja compartilhar +com outros aplicativos ou permitir que o usuário acesse através com um computador.</p> +</div> + + +<p class="note" style="clear:both"> +<strong>Dica:</strong> embora os aplicativos sejam instalados no armazenamento interno por +padrão, é possível especificar o atributo <a href="{@docRoot}guide/topics/manifest/manifest-element.html#install">{@code +android:installLocation}</a> em seu manifesto para que o aplicativo +seja instalado no armazenamento externo. Os usuários se beneficiam dessa opção quando o tamanho do APK é muito grande e +ele dispõe de maior espaço em armazenamento externo do que interno. Para obter mais +informações, consulte <a href="{@docRoot}guide/topics/data/install-location.html">Local de instalação do aplicativo</a>.</p> + + +<h2 id="GetWritePermission">Obter permissões para armazenamento externo</h2> + +<p>Para gravar no armazenamento externo, você deve solicitar a +permissão {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} em seu <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">arquivo de manifesto</a>:</p> + +<pre> +<manifest ...> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + ... +</manifest> +</pre> + +<div class="caution"><p><strong>Cuidado:</strong> +atualmente, os aplicativos podem ler o armazenamento externo +sem precisar de permissão especial. No entanto, isso será alterado em lançamentos futuros. Se seu aplicativo precisar +ler o armazenamento externo (mas não gravar nele), será necessário declarar a permissão {@link +android.Manifest.permission#READ_EXTERNAL_STORAGE}. Para garantir que o aplicativo continue +a funcionar adequadamente, declare essa permissão agora antes que as mudanças entrem em vigor.</p> +<pre> +<manifest ...> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + ... +</manifest> +</pre> +<p>No entanto, se seu aplicativo usa a permissão {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} +, já existe uma permissão implícita para leitura do armazenamento externo.</p> +</div> + +<p>Não é necessária permissão para salvar arquivos no armazenamento +interno. Seu aplicativo sempre terá permissão para ler e +gravar arquivos em seu diretório de armazenamento interno.</p> + + + + + +<h2 id="WriteInternalStorage">Salvar arquivos em armazenamento interno</h2> + +<p>Ao salvar um arquivo no armazenamento interno, você pode obter o diretório adequado como um +{@link java.io.File} chamando um destes dois métodos:</p> + +<dl> + <dt>{@link android.content.Context#getFilesDir}</dt> + <dd>Retorna um {@link java.io.File} que representa um diretório interno para seu aplicativo.</dd> + <dt>{@link android.content.Context#getCacheDir}</dt> + <dd>Retorna um {@link java.io.File} que representa um diretório interno para os arquivos de cache temporários +de seu aplicativo. Certifique-se de excluir cada arquivo assim que não +for mais necessário e estabeleça um limite de tamanho razoável para a quantidade de memória usada em um determinado +período de tempo, como 1MB. Se o sistema começar a ficar com pouco espaço de armazenamento, ele poderá excluir arquivos de cache +sem avisar.</dd> +</dl> + +<p>Para criar um novo arquivo em um desses diretórios, use o construtor {@link +android.Manifest.permission#READ_EXTERNAL_STORAGE}, transmitindo o {@link java.io.File} fornecido por um +dos métodos acima que especifica o diretório de armazenamento interno. Por exemplo:</p> + +<pre> +File file = new File(context.getFilesDir(), filename); +</pre> + +<p>Uma outra alternativa é chamar {@link +android.content.Context#openFileOutput openFileOutput()} para obter um {@link java.io.FileOutputStream} +que grave em um arquivo salvo no seu diretório interno. O exemplo a seguir +mostra como gravar texto em um arquivo:</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>Em alternativa, caso precise colocar arquivos em cache, use {@link +java.io.File#createTempFile createTempFile()}. Por exemplo, o método a seguir extrai o +nome do arquivo de {@link java.net.URL} e cria um arquivo com o mesmo nome +no diretório de cache interno de seu aplicativo.</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>Observação:</strong> +o diretório de armazenamento interno do seu aplicativo é especificado +pelo nome do pacote do aplicativo em um local específico do sistema de arquivos Android. +Teoricamente, outros aplicativos poderão ler seus arquivos internos se você definir +o arquivo para modo leitura. No entanto, o outro aplicativo também precisaria saber o nome do pacote +do seu aplicativo e os nomes dos arquivos. Outros aplicativos não podem navegar nos diretórios internos e não têm +permissão para ler ou gravar a menos que os arquivos sejam explicitamente definidos para permitir tais ações. Portanto, +desde que você utilize {@link android.content.Context#MODE_PRIVATE} para seus arquivos no armazenamento interno, +eles não ficarão acessíveis a outros aplicativos.</p> + + + + + +<h2 id="WriteExternalStorage">Salvar arquivos em armazenamento externo</h2> + +<p>Como o armazenamento externo pode ficar indisponível, como se o usuário ativar o +armazenamento no PC ou remover o cartão SD que fornece armazenamento externo, +você deve sempre verificar se o volume está disponível antes de acessá-lo. Consulte o estado de armazenamento +externo chamando {@link android.os.Environment#getExternalStorageState}. Se o estado +retornado for igual a {@link android.os.Environment#MEDIA_MOUNTED}, os arquivos poderão ser lidos +e gravados. Os métodos a seguir ajudam a determinar a disponibilidade +de armazenamento:</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>Embora o usuário e outros aplicativos possam modificar o armazenamento externo, há duas +categorias de arquivos que deverão ser salvas aqui:</p> + +<dl> + <dt>Arquivos públicos</dt> + <dd>Arquivos que +precisam estar livremente disponíveis ao usuário e outros aplicativos. Ao desinstalar o aplicativo, +o usuário deve continuar a ter acesso a esses arquivos. + <p>Por exemplo, fotos capturadas pelo aplicativo ou outros arquivos baixados.</p> + </dd> + <dt>Arquivos privados</dt> + <dd>Arquivos que pertencem ao aplicativo e que devem ser excluídos na desinstalação +. Embora esses arquivos estejam teoricamente à disposição do usuário e de outros aplicativo por estarem +no armazenamento externo, na verdade são arquivos que não têm valor para o usuário +fora do aplicativo. Ao desinstalar o aplicativo, o sistema exclui +todos os arquivos no diretório privado externo do aplicativo. + <p>Por exemplo, recursos adicionais baixados através do aplicativo ou arquivos de mídia temporários.</p> + </dd> +</dl> + +<p>Para salvar arquivos públicos no armazenamento externo, use o método +{@link android.os.Environment#getExternalStoragePublicDirectory +getExternalStoragePublicDirectory()} para obter um {@link java.io.File} que representa +o diretório correto no armazenamento externo. O método exige um argumento que especifica +o tipo de arquivo que se deseja salvar para que possa ser logicamente organizado com outros arquivos públicos +, como {@link android.os.Environment#DIRECTORY_MUSIC} ou {@link +android.os.Environment#DIRECTORY_PICTURES}. Por exemplo:</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>Se você deseja salvar arquivos privados do aplicativo, obtenha o +diretório correto chamando {@link +android.content.Context#getExternalFilesDir getExternalFilesDir()} e informe um nome indicando +o tipo de diretório desejado. Cada diretório criado dessa forma é adicionado ao diretório principal +que contém todos os arquivos do armazenamento externo do aplicativo que o sistema exclui quando o +usuário faz a desinstalação.</p> + +<p>Por exemplo, este é um método que pode ser usado para criar um diretório para um álbum de fotos individual:</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>Se nenhum dos nomes de subdiretórios predefinidos se adequa aos arquivos, chame {@link +android.content.Context#getExternalFilesDir getExternalFilesDir()} e transmita {@code null}. Isso +retorna o diretório raiz para o diretório privado do aplicativo no armazenamento externo.</p> + +<p>Lembre-se de que {@link android.content.Context#getExternalFilesDir getExternalFilesDir()} +cria um diretório dentro de um diretório que é excluído quando o usuário desinstala o aplicativo. +Se os arquivos salvos precisarem estar disponíveis após a desinstalação do +aplicativo, como +quando seu aplicativo é uma câmera e o usuário deseja manter as fotos, use {@link android.os.Environment#getExternalStoragePublicDirectory +getExternalStoragePublicDirectory()}.</p> + + +<p>Independentemente do uso de {@link +android.os.Environment#getExternalStoragePublicDirectory +getExternalStoragePublicDirectory()} para arquivos compartilhados ou +{@link android.content.Context#getExternalFilesDir +getExternalFilesDir()} para arquivos privados do aplicativo, é importante usar +os nomes de diretório fornecidos pelas constantes de API, como +{@link android.os.Environment#DIRECTORY_PICTURES}. Esses nomes de diretório garantem +que os arquivos sejam tratados de forma adequada pelo sistema. Por exemplo, arquivos salvos em {@link +android.os.Environment#DIRECTORY_RINGTONES} são categorizados pelo scanner de mídia dos sistema como toques +e não como música.</p> + + + + +<h2 id="GetFreeSpace">Consultar espaço livre</h2> + +<p>Se você já souber antecipadamente a quantidade de dados a ser salvo, descubra se +há espaço disponível suficiente sem fazer com que um {@link +java.io.IOException} chame {@link java.io.File#getFreeSpace} ou {@link +java.io.File#getTotalSpace}. Esses métodos informam o espaço disponível atual e o +espaço total no volume de armazenamento. Essa informação ajuda a evitar o preenchimento +do volume de armazenamento além de um determinado limite.</p> + +<p>No entanto, o sistema não garante que será possível gravar a quantidade de bytes indicada +por {@link java.io.File#getFreeSpace}. Se o número retornado tiver +alguns MB além do tamanho dos dados que deseja salvar ou se o sistema de arquivos +estiver abaixo de 90% cheio, é possível continuar com segurança. +Caso contrário, não grave no armazenamento.</p> + +<p class="note"><strong>Observação:</strong> não é obrigatório verificar a quantidade de espaço disponível +antes de salvar o arquivo. É possível tentar gravar o arquivo diretamente e depois +obter um {@link java.io.IOException}, se houver. Essa ação é recomendada +caso você não saiba exatamente quanto espaço será necessário. Por exemplo, se +você alterar a codificação do arquivo antes de salvá-lo convertendo uma imagem PNG em +JPEG, não é possível saber o tamanho do arquivo antecipadamente.</p> + + + + +<h2 id="DeleteFile">Excluir um arquivo</h2> + +<p>Sempre exclua arquivos que não sejam mais necessários. A forma mais simples de apagar um +arquivo é fazer com que o arquivo de referência aberto chame {@link java.io.File#delete} por conta própria.</p> + +<pre> +myFile.delete(); +</pre> + +<p>Se o arquivo estiver salvo em armazenamento interno, é possível solicitar ao {@link android.content.Context} para localizar e +excluir o arquivo chamando {@link android.content.Context#deleteFile deleteFile()}:</p> + +<pre> +myContext.deleteFile(fileName); +</pre> + +<div class="note"> +<p><strong>Observação:</strong> quando o usuário desinstala o aplicativo, o sistema Android também +exclui:</p> +<ul> +<li>Todos os arquivos salvos no armazenamento interno</li> +<li>Todos os arquivos salvos no armazenamento externo usando {@link +android.content.Context#getExternalFilesDir getExternalFilesDir()}.</li> +</ul> +<p>No entanto, recomenda-se exclui manualmente todos os arquivos em cache criados com +{@link android.content.Context#getCacheDir()} regularmente e +outros arquivos que não sejam mais necessários.</p> +</div> + diff --git a/docs/html-intl/intl/pt-br/training/basics/data-storage/index.jd b/docs/html-intl/intl/pt-br/training/basics/data-storage/index.jd new file mode 100644 index 0000000..2b88ce1 --- /dev/null +++ b/docs/html-intl/intl/pt-br/training/basics/data-storage/index.jd @@ -0,0 +1,57 @@ +page.title=Salvando dados +page.tags=armazenamento de dados,arquivos,sql,basededados,preferências +helpoutsWidget=true + +trainingnavtop=true +startpage=true + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>Dependências e pré-requisitos</h2> +<ul> + <li>Android 1.6 (API Nível 4) ou superior</li> + <li>Familiaridade com coleções de valores-chave do Map</li> + <li>Familiaridade com API de E/S de arquivos Java</li> + <li>Familiaridade com bancos de dados do SQL</li> +</ul> + +<h2>Leia também</h2> +<ul> + <li><a href="{@docRoot}guide/topics/data/data-storage.html">Opções de armazenamento</a></li> +</ul> + +</div> +</div> + +<p>A maioria dos aplicativos Android precisa salvar dados, mesmo que seja apenas para salvar informações sobre o estado do aplicativo +durante {@link android.app.Activity#onPause onPause()} para que o progresso do usuário não seja perdido. A maior parte +dos aplicativos não triviais também precisa salvar configurações do usuário e alguns aplicativos precisam gerenciar grandes +quantidades de informação em arquivos e bancos de dados. Esta aula apresenta as +principais opções de armazenamento de dados no Android, incluindo:</p> + +<ul> + <li>Como salvar pares de valores-chave de tipos de dados simples em um arquivo de preferências +compartilhadas</li> + <li>Como salvar arquivos arbitrários no sistema de arquivos do Android</li> + <li>Como usar bancos de dados gerenciadas pelo SQLite</li> +</ul> + + +<h2>Lições</h2> + +<dl> + <dt><b><a href="shared-preferences.html">Salvando conjuntos de valores-chave</a></b></dt> + <dd>Aprenda a como usar um arquivo de preferências compartilhadas para armazenar pequenas quantidades de informação em pares de +valores-chave.</dd> + + <dt><b><a href="files.html">Salvando arquivos</a></b></dt> + <dd>Aprenda a como salvar um arquivo básico, como para armazenar longas sequências de dados que + são geralmente lidas em ordem.</dd> + + <dt><b><a href="databases.html">Salvando dados em bancos de dados do SQL</a></b></dt> + <dd>Aprenda a usar o banco de dados do SQLite para ler e gravar dados estruturados.</dd> + +</dl> diff --git a/docs/html-intl/intl/pt-br/training/basics/data-storage/shared-preferences.jd b/docs/html-intl/intl/pt-br/training/basics/data-storage/shared-preferences.jd new file mode 100644 index 0000000..0c84487 --- /dev/null +++ b/docs/html-intl/intl/pt-br/training/basics/data-storage/shared-preferences.jd @@ -0,0 +1,120 @@ +page.title=Salvando conjuntos de valor-chave +page.tags=armazenamento de dados +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>Esta lição ensina a</h2> +<ol> + <li><a href="#GetSharedPreferences">Obter um identificador para preferências compartilhadas</a></li> + <li><a href="#WriteSharedPreference">Gravar em preferências compartilhadas</a></li> + <li><a href="#ReadSharedPreference">Ler de preferências compartilhadas</a></li> +</ol> + +<h2>Leia também</h2> +<ul> + <li><a href="{@docRoot}guide/topics/data/data-storage.html#pref">Usando preferências compartilhadas</a></li> +</ul> + +</div> +</div> + + +<p>Caso você tenha uma coleção relativamente pequena de valores-chave para salvar, +use as APIs {@link android.content.SharedPreferences}. +Um objeto {@link android.content.SharedPreferences} indica um arquivo que contém +pares de valores-chave e fornece métodos simples para ler e gravar. Cada arquivo +{@link android.content.SharedPreferences} é +gerenciado pelo framework e pode ser privado ou compartilhado.</p> + +<p>Essa lição mostra como usar as APIs {@link android.content.SharedPreferences} para armazenar e +recuperar valores simples.</p> + +<p class="note"><strong>Observação:</strong> as APIs {@link android.content.SharedPreferences} são +usadas apenas para leitura e gravação de pares de valores-chave e não devem ser confundidas com as APIs +{@link android.preference.Preference}, que ajudam a criar uma interface do usuário +para as configurações do seu aplicativo (embora usem {@link android.content.SharedPreferences} como +implementação para salvar as configurações de aplicativo). Para obter mais informação sobre uso de APIs {@link +android.preference.Preference}, consulte a guia <a href="{@docRoot}guide/topics/ui/settings.html">Configurações</a>.</p> + +<h2 id="GetSharedPreferences">Obter um identificador para preferências compartilhadas</h2> + +<p>É possível criar um novo arquivo de preferência compartilhada ou acessar um existente +chamando um destes dois métodos:</p> +<ul> + <li>{@link android.content.Context#getSharedPreferences(String,int) +getSharedPreferences()} — Use este método se precisar identificar vários arquivos de preferência compartilhada +por nome, que devem ser especificados com o primeiro parâmetro. É possível chamá-lo por qualquer +{@link android.content.Context} em seu aplicativo.</li> + <li>{@link android.app.Activity#getPreferences(int) getPreferences()} — Use este método por um +{@link android.app.Activity} se precisar +usar apenas um arquivo de preferência compartilhada para a atividade. Como ele retorna um arquivo de preferência compartilhada +padrão que pertence à atividade, não é necessário fornecer um nome.</li> +</ul> + +<p>Por exemplo, o código a seguir é executado dentro de um {@link android.app.Fragment}. +Ele acessa o arquivo de preferências compartilhadas que é +identificado pela cadeia de caracteres de recursos {@code R.string.preference_file_key} e o abre usando +o modo privado para que o arquivo possa ser acessado apenas pelo seu aplicativo.</p> + +<pre> +Context context = getActivity(); +SharedPreferences sharedPref = context.getSharedPreferences( + getString(R.string.preference_file_key), Context.MODE_PRIVATE); +</pre> + +<p>Ao nomear seus arquivos de preferência compartilhada, você deve usar um nome que seja identificável exclusivamente +para o aplicativo, como {@code "com.example.myapp.PREFERENCE_FILE_KEY"}</p> + +<p>Em alternativa, caso precise apenas de um arquivo de preferência compartilhada para sua atividade, use o método +{@link android.app.Activity#getPreferences(int) getPreferences()}:</p> + +<pre> +SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE); +</pre> + +<p class="caution"><strong>Cuidado:</strong> se você criar um arquivo de preferência compartilhada +com {@link android.content.Context#MODE_WORLD_READABLE} ou {@link +android.content.Context#MODE_WORLD_WRITEABLE}, qualquer outro aplicativo que conhecer o identificador de arquivo +poderá acessar seus dados.</p> + + +<h2 id="WriteSharedPreference">Gravar em preferências compartilhadas</h2> + +<p>Para gravar em um arquivo de preferência compartilhada, crie um {@link +android.content.SharedPreferences.Editor} chamando {@link +android.content.SharedPreferences#edit} em seu {@link android.content.SharedPreferences}.</p> + +<p>Transmita as chaves e os valores que deseja gravar com métodos como {@link +android.content.SharedPreferences.Editor#putInt putInt()} e {@link +android.content.SharedPreferences.Editor#putString putString()}. Chame {@link +android.content.SharedPreferences.Editor#commit} para salvar as alterações. Por exemplo:</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">Ler de preferências compartilhadas</h2> + +<p>Para recuperar valores de um arquivo de preferência compartilhada, chame métodos como {@link +android.content.SharedPreferences#getInt getInt()} e {@link +android.content.SharedPreferences#getString getString()}, fornecendo a chave para o valor +desejado e opcionalmente um valor padrão para retornar caso a chave não esteja +presente. Por exemplo:</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/pt-br/training/basics/intents/filters.jd b/docs/html-intl/intl/pt-br/training/basics/intents/filters.jd new file mode 100644 index 0000000..73f0b84 --- /dev/null +++ b/docs/html-intl/intl/pt-br/training/basics/intents/filters.jd @@ -0,0 +1,236 @@ +page.title=Permitindo que outros aplicativos iniciem sua atividade +page.tags=intenções +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> + <div id="tb"> + +<h2>Esta lição ensina a</h2> +<ol> + <li><a href="#AddIntentFilter">Adicionar um filtro de intenções</a></li> + <li><a href="#HandleIntent">Responder à intenção em sua atividade</a></li> + <li><a href="#ReturnResult">Retornar um resultado</a></li> +</ol> + +<h2>Leia também</h2> +<ul> + <li><a href="{@docRoot}training/sharing/index.html">Compartilhando dados simples</a></li> + <li><a href="{@docRoot}training/secure-file-sharing/index.html">Compartilhando arquivos</a> +</ul> + </div> +</div> + +<p>As duas lições anteriores enfatizaram um aspecto: iniciar a atividade de outro aplicativo pelo +seu aplicativo. Mas se seu aplicativo puder executar uma ação que pode ser útil para outro aplicativo, +ele precisa estar preparado para responder às solicitações de ação de outros aplicativos. Por exemplo, se +construir um aplicativo social que compartilhe mensagens ou fotos com os amigos do usuário, é interessante +que ele possa responder à intenção {@link android.content.Intent#ACTION_SEND} para que os usuários consigam iniciar uma +ação de “compartilhar” por outro aplicativo e iniciar seu aplicativo para executar a ação.</p> + +<p>Para permitir que outros aplicativos iniciem sua atividade, adicione um elemento <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a> +em seu arquivo de manifesto para o elemento <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> correspondente.</p> + +<p>Quando seu aplicativo é instalado em um dispositivo, o sistema identifica seu filtro de +intenções e adiciona a informação a um catálogo interno de intenções suportado por todos os aplicativos instalados. +Quando um aplicativo chama {@link android.app.Activity#startActivity +startActivity()} ou {@link android.app.Activity#startActivityForResult startActivityForResult()}, +com uma intenção implícita, o sistema encontra qual atividade (ou atividades) pode responder à +intenção.</p> + + + +<h2 id="AddIntentFilter">Adicionar um filtro de intenções</h2> + +<p>Para definir adequadamente a quais intenções sua atividade responderá, cada filtro de intenções adicionado +deverá ser o mais específico possível em termos de tipo de ação e dados aceitos pela +atividade.</p> + +<p>O sistema pode enviar uma determinada {@link android.content.Intent} para uma atividade se ela tiver +um filtro de intenções que atenda aos seguintes critérios do objeto {@link android.content.Intent}:</p> + +<dl> + <dt>Ação</dt> + <dd>Uma cadeia de caracteres que dá nome a ação a ser executada. Geralmente, um dos valores definidos para a plataforma +como {@link android.content.Intent#ACTION_SEND} ou {@link android.content.Intent#ACTION_VIEW}. + <p>Especifique-o em seu filtro de intenções com o elemento <a href="{@docRoot}guide/topics/manifest/action-element.html">{@code <action>}</a>. +O valor especificado neste elemento deve ser o nome completo da cadeia de caracteres para a ação e não a +API constante (veja exemplos abaixo).</p></dd> + + <dt>Dados</dt> + <dd>A descrição dos dados associados à intenção. + <p>Especifique-a em seu filtro de intenções com o elemento <a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a>. Através de um +ou mais atributos neste elemento, você pode especificar apenas o tipo MIME, apenas um prefixo de URI, +apenas um esquema de URI ou uma combinação destes e outros elementos que indicam o tipo de dados +aceito.</p> + <p class="note"><strong>Observação:</strong> se não precisar especificar os dados +{@link android.net.Uri} (como quando sua atividade responde a outros tipos de dados “extra”, em vez +do URI), especifique apenas o atributo {@code android:mimeType} para declarar o tipo de +dado que sua atividade trata, como {@code text/plain} ou {@code image/jpeg}.</p> +</dd> + <dt>Categoria</dt> + <dd>Oferece uma outra forma de caracterizar a atividade que trata a intenção, geralmente relacionada +ao gesto do usuário ou localização onde foi iniciada. Há diferentes categorias +compatíveis com o sistema, mas a maioria raramente é utilizada. No entanto, todas as intenções implícitas são definidas com +{@link android.content.Intent#CATEGORY_DEFAULT} por padrão. + <p>Especifique-a em seu filtro de intenções com o elemento <a href="{@docRoot}guide/topics/manifest/category-element.html">{@code <category>}</a> +.</p></dd> +</dl> + +<p>Em seu filtro de intenções, declare quais critérios serão aceitos por sua atividade +informando cada um deles com elementos XML correspondentes aninhados no elemento <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a> +.</p> + +<p>Este é um exemplo de atividade com filtro de intenções que responde à intenção {@link +android.content.Intent#ACTION_SEND} quando o tipo de dado é texto ou imagem:</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>Cada intenção em entrada especifica apenas uma ação e um tipo de dado, mas pode-se declarar +múltiplas instâncias dos elementos <a href="{@docRoot}guide/topics/manifest/action-element.html">{@code +<action>}</a>, <a href="{@docRoot}guide/topics/manifest/category-element.html">{@code +<category>}</a> e <a href="{@docRoot}guide/topics/manifest/data-element.html">{@code +<data>}</a> em cada +<a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a>.</p> + +<p>Se dois pares de ação e dados fores mutuamente exclusivos em +seus comportamentos, crie filtros de intenções separados para especificar quais ações são aceitáveis +quando pareadas com qual tipo de dado.</p> + +<p>Imagine que sua atividade responda à ambos, texto e imagem, para intenções {@link +android.content.Intent#ACTION_SEND} e {@link +android.content.Intent#ACTION_SENDTO}. Nesse caso, defina dois +filtros de intenções separados para as duas ações porque uma intenção {@link +android.content.Intent#ACTION_SENDTO} deve usar os dados {@link android.net.Uri} para especificar +o endereço do destinatário utilizando o esquema do URI {@code send} ou {@code sendto}. Por exemplo:</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>Observação:</strong> para receber intenções implícitas, inclua a categoria +{@link android.content.Intent#CATEGORY_DEFAULT} no filtro de intenções. Os métodos {@link +android.app.Activity#startActivity startActivity()} e {@link +android.app.Activity#startActivityForResult startActivityForResult()} tratam todas as intenções como se elas tivessem +declarado a categoria {@link android.content.Intent#CATEGORY_DEFAULT}. Se você não a declarar +em seu filtro de intenções, nenhuma intenção implícita resolverá sua atividade.</p> + +<p>Para obter mais informações sobre o envio e recebimento de intenções {@link android.content.Intent#ACTION_SEND} +que realizam comportamentos de compartilhamento social, veja a lição <a href="{@docRoot}training/sharing/receive.html">Recebendo dados simples de outros aplicativos</a>.</p> + + +<h2 id="HandleIntent">Responder à intenção em sua atividade</h2> + +<p>Para decidir qual ação tomar em sua atividade, leia a {@link +android.content.Intent} utilizada para iniciá-la.</p> + +<p>Quando sua atividade iniciar, chame {@link android.app.Activity#getIntent()} para retomar a +{@link android.content.Intent} que iniciou a atividade. Pode-se fazer isso a qualquer momento durante +o ciclo de vida da atividade, mas recomenda-se fazê-lo no início do retorno de chamada como +{@link android.app.Activity#onCreate onCreate()} ou {@link android.app.Activity#onStart()}.</p> + +<p>Por exemplo:</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">Retornar um resultado</h2> + +<p>Se você deseja retornar um resultado para a atividade que invocou a sua, simplesmente chame {@link +android.app.Activity#setResult(int,Intent) setResult()} para especificar o código do resultado e o resultado {@link +android.content.Intent}. Quando sua operação estiver concluída e o usuário retornar à atividade +original, chame {@link android.app.Activity#finish()} para fechar (e destruir) a atividade. Por +exemplo:</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>Sempre especifique um código de resultado com o resultado. Geralmente é {@link +android.app.Activity#RESULT_OK} ou {@link android.app.Activity#RESULT_CANCELED}. Pode-se fornecer +dados adicionais com uma {@link android.content.Intent}, caso seja necessário.</p> + +<p class="note"><strong>Observação:</strong> o resultado é definido como {@link +android.app.Activity#RESULT_CANCELED} por padrão. Portanto, se o usuário pressionar o botão <em>Voltar</em> +antes de concluir a ação e definir o resultado, a atividade original recebe +o resultado “cancelado”.</p> + +<p>Se você apenas precisar retornar um número inteiro que indique uma das várias opções de resultado, defina +o código de resultados para qualquer valor maior que 0. Se você usar o código do resultado para fornecer um número inteiro e não +houver necessidade de incluir {@link android.content.Intent}, chame {@link +android.app.Activity#setResult(int) setResult()} e transmita apenas um código de resultado. Por exemplo:</p> + +<pre> +setResult(RESULT_COLOR_RED); +finish(); +</pre> + +<p>Nesse caso, talvez existam apenas alguns poucos resultados possíveis. Portanto, o código do resultado é um número inteiro definido +localmente (maior que 0). Isso funciona bem ao retornar um resultado de uma atividade +em seu próprio aplicativo porque a atividade que recebe o resultado pode fazer uma referência à constante pública +para determinar o valor do código de resultado.</p> + +<p class="note"><strong>Observação:</strong> não há necessidade de verificar se sua atividade foi iniciada +com {@link +android.app.Activity#startActivity startActivity()} ou {@link +android.app.Activity#startActivityForResult startActivityForResult()}. Simplesmente chame {@link +android.app.Activity#setResult(int,Intent) setResult()} se a intenção que iniciou sua atividade +estiver esperando um resultado. Se a atividade inicial tivesse chamado {@link +android.app.Activity#startActivityForResult startActivityForResult()}, o sistema o forneceria +o resultado para {@link android.app.Activity#setResult(int,Intent) setResult()}. Caso contrário, +o resultado é ignorado.</p> + + + + + + diff --git a/docs/html-intl/intl/pt-br/training/basics/intents/index.jd b/docs/html-intl/intl/pt-br/training/basics/intents/index.jd new file mode 100644 index 0000000..94108b8 --- /dev/null +++ b/docs/html-intl/intl/pt-br/training/basics/intents/index.jd @@ -0,0 +1,62 @@ +page.title=Interagindo com outros aplicativos +page.tags=intenções,atividade +helpoutsWidget=true + +trainingnavtop=true +startpage=true + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>Dependências e pré-requisitos</h2> +<ul> + <li>Conhecimento básico sobre o ciclo de vida da atividade (consulte <a href="{@docRoot}training/basics/activity-lifecycle/index.html">Como gerenciar o ciclo de vida +da atividade</a>)</li> +</ul> + + +<h2>Leia também</h2> +<ul> + <li><a href="{@docRoot}training/sharing/index.html">Compartilhando dados simples</a></li> + <li><a href="{@docRoot}training/secure-file-sharing/index.html">Compartilhando arquivos</a> + <li><a href="http://android-developers.blogspot.com/2009/11/integrating-application-with-intents.html"> +Integrando aplicativos e intenções (publicações do blog)</a></li> + <li><a href="{@docRoot}guide/components/intents-filters.html">Intenções e filtros de +intenções</a>.</li> +</ul> + +</div> +</div> + +<p>Geralmente, um aplicativo Android tem várias <a href="{@docRoot}guide/components/activities.html">atividades</a>. Cada atividade exibe uma +interface do usuário que permite ao usuário executar tarefas específicas (como visualizar um mapa ou tirar uma foto). +Para levar o usuário de uma atividade para outra, o aplicativo deve usar um {@link +android.content.Intent} para definir a “intenção” do aplicativo de fazer algo. Ao transmitir um +{@link android.content.Intent} para o sistema com um método como {@link +android.app.Activity#startActivity startActivity()}, o sistema usa {@link +android.content.Intent} para identificar e iniciar o componente adequado do aplicativo. Usar intenções +permite até mesmo que o aplicativo inicie uma atividade que esteja contida em outro aplicativo.</p> + +<p>Um {@link android.content.Intent} pode ser <em>explícito</em> para iniciar um componente específico +(uma instância {@link android.app.Activity} específica) ou <em>implícito</em> para iniciar qualquer +componente que possa responder à ação pretendida (como “capturar uma foto”).</p> + +<p>Esta aula mostra como utilizar um {@link android.content.Intent} para executar algumas interações +básicas com outros aplicativos, como iniciar outro aplicativo, receber seus resultados, e +disponibilizar seu aplicativo para responder às intenções de outros aplicativos.</p> + +<h2>Lições</h2> + +<dl> + <dt><b><a href="sending.html">Enviando o usuário para outro aplicativo</a></b></dt> + <dd>Mostra como criar intenções implícitas para iniciar outros aplicativos que podem executar uma +ação.</dd> + <dt><b><a href="result.html">Obtendo resultados de uma atividade</a></b></dt> + <dd>Como iniciar outra atividade e receber seu resultado.</dd> + <dt><b><a href="filters.html">Permitindo que outros aplicativos iniciem sua atividade</a></b></dt> + <dd>Como fazer com que atividades em seu aplicativo se abram para serem usadas por outros aplicativos através de definição +de filtros de intenções que declaram as intenções implícitas aceitas pelo aplicativo.</dd> +</dl> + diff --git a/docs/html-intl/intl/pt-br/training/basics/intents/result.jd b/docs/html-intl/intl/pt-br/training/basics/intents/result.jd new file mode 100644 index 0000000..ecb5a47 --- /dev/null +++ b/docs/html-intl/intl/pt-br/training/basics/intents/result.jd @@ -0,0 +1,178 @@ +page.title=Obtendo resultados de uma atividade +page.tags=intenções +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> + <div id="tb"> + +<h2>Esta lição ensina a</h2> +<ol> + <li><a href="#StartActivity">Iniciar a atividade</a></li> + <li><a href="#ReceiveResult">Receber o resultado</a></li> +</ol> + +<h2>Leia também</h2> +<ul> + <li><a href="{@docRoot}training/sharing/index.html">Compartilhando dados simples</a></li> + <li><a href="{@docRoot}training/secure-file-sharing/index.html">Compartilhando arquivos</a> +</ul> + + </div> +</div> + +<p>Iniciar outra atividade não precisa ser a única forma. Pode-se também iniciar outra atividade e +receber um resultado de volta. Para receber um resultado, chame {@link android.app.Activity#startActivityForResult +startActivityForResult()} (em vez de {@link android.app.Activity#startActivity +startActivity()}).</p> + +<p>Por exemplo, o aplicativo pode iniciar um aplicativo de câmera e receber a foto capturada como resultado. Ou, +ao iniciar o aplicativo Pessoas para que o usuário selecione um +contato, você receberá os detalhes do contato como resultado.</p> + +<p>Evidentemente a atividade que responde deve ser projetada para retornar um resultado. Quando isso acontecer, ela +enviará o resultado para outro objeto {@link android.content.Intent}. A atividade recebe o resultado no +retorno de chamada {@link android.app.Activity#onActivityResult onActivityResult()}.</p> + +<p class="note"><strong>Observação:</strong> pode-se usar intenções explícitas ou implícitas ao chamar +{@link android.app.Activity#startActivityForResult startActivityForResult()}. Ao iniciar uma de +suas próprias atividades que recebem um resultado, use uma intenção explícita para garantir que o +resultado esperado será recebido.</p> + + +<h2 id="StartActivity">Iniciar a atividade</h2> + +<p>Não há nada especial no objeto {@link android.content.Intent} utilizado para iniciar +uma atividade para um resultado, mas é preciso transmitir um argumento de número inteiro adicional ao método {@link +android.app.Activity#startActivityForResult startActivityForResult()}.</p> + +<p>O argumento de número inteiro é um “código de pedido” que identifica o pedido. Quando você recebe o +resultado {@link android.content.Intent}, o retorno de chamada fornece o mesmo código de pedido para que o +aplicativo possa identificar adequadamente o resultado e determinar como responder a ele.</p> + +<p>Essa é uma forma de iniciar uma atividade que permite ao usuário escolher um contato:</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">Receber o resultado</h2> + +<p>Quando o usuário termina a atividade subsequente e retorna, o sistema chama o método +{@link android.app.Activity#onActivityResult onActivityResult()} da atividade. Esse método inclui três +argumentos:</p> + +<ul> + <li>O código de pedido passado para {@link +android.app.Activity#startActivityForResult startActivityForResult()}.</li> + <li>Um código de resultado especificado pela segunda atividade. Se a operação for bem sucedida, será {@link +android.app.Activity#RESULT_OK} ou {@link +android.app.Activity#RESULT_CANCELED} se o usuário tiver desistido ou a operação tiver falhado por algum +motivo.</li> + <li>Um {@link android.content.Intent} que transporta os dados do resultado.</li> +</ul> + +<p>Esta é uma forma de responder ao resultado para a intenção “escolher um contato”:</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>Nesse exemplo, o resultado {@link android.content.Intent} retornado pelos +aplicativos Contatos ou Pessoas do Android fornece um conteúdo {@link android.net.Uri} que identifica o +contato escolhido pelo usuário.</p> + +<p>Para que o resultado seja tratado de forma adequada, é preciso saber o formato do resultado +{@link android.content.Intent}. Isso é fácil quando umas das suas atividades +retorna o resultado. Os aplicativos incluídos na plataforma Android oferecem suas próprias APIs que +podem ser usadas para dados de resultado específicos. Por exemplo, o aplicativo Pessoas (Contatos em algumas versões mais +antigas) sempre retorna um resultado com URI de conteúdo que identifica o contato escolhido e o aplicativo +Câmera retorna um {@link android.graphics.Bitmap} no {@code "data"} extra (consulte a lição +<a href="{@docRoot}training/camera/index.html">Capturando fotografias</a>).</p> + + +<h4>Bônus: Ler os dados do contato</h4> + +<p>O código acima que mostra como obter um resultado pelo aplicativo Pessoas não +traz muitos detalhes sobre como exatamente ler os dados do resultado porque exige uma discussão mais avançada +sobre <a href="{@docRoot}guide/topics/providers/content-providers.html">provedores +de conteúdo</a>. No entanto, se você desejar saber mais, aqui estão alguns códigos que mostram como consultar os +dados de resultado para obter o número de telefone do contato selecionado:</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>Observação:</strong> antes do Android 2.3 (API nível 9), executar uma consulta +no {@link android.provider.ContactsContract.Contacts Contacts Provider} (como mostrado +acima) exige que o aplicativo declare a permissão {@link +android.Manifest.permission#READ_CONTACTS} (consulte <a href="{@docRoot}guide/topics/security/security.html">Segurança e permissões</a>). Contudo, +iniciar com Android 2.3, o aplicativo Contatos/Pessoas dá ao aplicativo permissão +temporária para ler no Provedor de Contatos quando retornar um resultado. A permissão temporária +aplica-se apenas a pedidos de contato específicos, portanto, não é possível consultar um contato diferente daquele +especificado pelo {@link android.net.Uri} da intenção, a menos que você declare a permissão {@link +android.Manifest.permission#READ_CONTACTS}.</p> + + + + + + + + + + + + + + + diff --git a/docs/html-intl/intl/pt-br/training/basics/intents/sending.jd b/docs/html-intl/intl/pt-br/training/basics/intents/sending.jd new file mode 100644 index 0000000..526374d --- /dev/null +++ b/docs/html-intl/intl/pt-br/training/basics/intents/sending.jd @@ -0,0 +1,256 @@ +page.title=Enviando o usuário para outro aplicativo +page.tags=intenções +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + + +<div id="tb-wrapper"> + <div id="tb"> + +<h2>Esta lição ensina a</h2> +<ol> + <li><a href="#Build">Criar uma intenção implícita</a></li> + <li><a href="#Verify">Confirmar se há um aplicativo para receber a intenção</a></li> + <li><a href="#StartActivity">Iniciar uma atividade com uma intenção</a></li> + <li><a href="#AppChooser">Mostrar um selecionador de aplicativo</a></li> +</ol> + +<h2>Leia também</h2> +<ul> + <li><a href="{@docRoot}training/sharing/index.html">Compartilhando dados simples</a></li> +</ul> + + </div> +</div> + +<p>Uma das características mais importantes do Android é a habilidade do aplicativo enviar o usuário para outro aplicativo +com base em uma “ação” que gostaria de executar. Por exemplo, se +o aplicativo tiver o endereço de um negócio que você gostaria de mostrar em um mapa, não é necessário criar +uma atividade no aplicativo que mostre um mapa. Em vez disso, é possível criar uma solicitação para exibir o endereço +usando {@link android.content.Intent}. O sistema Android inicia um aplicativo que possa mostrar +o endereço em um mapa.</p> + +<p>Como explicado na primeira lição, <a href="{@docRoot}training/basics/firstapp/index.html">Criando +seu primeiro aplicativo</a>, use intenções para navegar entre atividades no aplicativo. Geralmente +isso é feito com <em>intenções explícitas</em>, que define o nome exato da classe do +componente que deseja iniciar. No entanto, quando desejar que outro aplicativo execute uma ação, como +“visualizar um mapa”, use uma <em>intenção implícita</em>.</p> + +<p>Esta lição mostra como criar uma intenção implícita para uma ação específica e como utilizá-la +para iniciar uma atividade que executa a ação em outro aplicativo.</p> + + + +<h2 id="Build">Criar uma intenção implícita</h2> + +<p>Intenções implícitas não declaram o nome da classe do componente a iniciar, mas declaram uma +ação a executar. A ação especifica o que deve ser feito, como <em>visualizar</em>, +<em>editar</em>, <em>enviar</em> ou <em>obter</em> algo. Geralmente, as intenções incluem dados associados +à ação, como o endereço que deseja visualizar ou a mensagem de email a ser enviada. +Dependendo da intenção que deseja criar, os dados podem ser {@link android.net.Uri}, +um dos outros tipos de dados ou a intenção pode não precisar de dado algum.</p> + +<p>Se seu dados forem um {@link android.net.Uri}, há um simples construtor {@link +android.content.Intent#Intent(String,Uri) Intent()} que pode ser usado para definir a ação e +os dados.</p> + +<p>Este é um exemplo de como criar uma intenção para iniciar uma chamada telefônica usando os dados {@link +android.net.Uri} para especificar o número de telefone:</p> + +<pre> +Uri number = Uri.parse("tel:5551234"); +Intent callIntent = new Intent(Intent.ACTION_DIAL, number); +</pre> + +<p>Quando o aplicativo invoca a intenção chamando {@link android.app.Activity#startActivity +startActivity()}, o aplicativo Telefone inicia uma chamada para o número especificado.</p> + +<p>Estas são algumas outras intenções e suas ações e pares de dados {@link android.net.Uri} +:</p> + +<ul> + <li>Visualizar um mapa: +<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>Visualizar uma página da Web: +<pre> +Uri webpage = Uri.parse("http://www.android.com"); +Intent webIntent = new Intent(Intent.ACTION_VIEW, webpage); +</pre> + </li> +</ul> + +<p>Outros tipos de intenções implícitas exigem dados “extras” que fornecem diferentes tipos de dados, +como uma cadeia de caracteres. É possível adicionar um ou mais dados extras utilizando vários métodos {@link +android.content.Intent#putExtra(String,String) putExtra()}.</p> + +<p>Por padrão, o sistema determina o tipo MIME adequado que uma intenção exige com base nos dados +{@link android.net.Uri} incluídos. Se você não incluir um {@link android.net.Uri} na +intenção, sempre utilize {@link android.content.Intent#setType setType()} para especificar o tipo +de dado associado à intenção. Definir o tipo de MIME especifica melhor que tipos de +atividades receberão a intenção.</p> + +<p>Estas são mais algumas intenções que adicionam dados extra para especificar a intenção desejada:</p> + +<ul> + <li>Enviar um email com um anexo: +<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>Criar um evento de calendário: +<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>Observação:</strong> esta intenção para evento de calendário é suportada apenas com nível de API +14 e superior.</p> + </li> +</ul> + +<p class="note"><strong>Observação:</strong> é importante definir seu {@link +android.content.Intent} para ser o mais específico possível. Por exemplo, se você deseja exibir uma imagem +usando a intenção {@link android.content.Intent#ACTION_VIEW}, especifique o tipo de MIME do +{@code image/*}. Isto evita que aplicativos que podem “exibir” outros tipos de dados (como um aplicativo de mapas) seja +acionado pela intenção.</p> + + + +<h2 id="Verify">Confirmar se há um aplicativo para receber a intenção</h2> + +<p>Embora a plataforma do Android garanta que determinadas intenções sejam resolvidas com um dos + aplicativos embutidos (como aplicativo de Telefone, Email ou Agenda), sempre inclua uma +etapa de confirmação antes de chamar uma intenção.</p> + +<p class="caution"><strong>Cuidado:</strong> se você invocar uma intenção e não houver aplicativo +disponível no dispositivo para tratar a intenção, o aplicativo falhará.</p> + +<p>Para confirmar se há atividade disponível para responder à intenção, chame {@link +android.content.pm.PackageManager#queryIntentActivities queryIntentActivities()} para obter uma lista +de atividades que podem tratar a {@link android.content.Intent}. Se o {@link +java.util.List} retornado não estiver vazio, a intenção poderá ser usada com segurança. Por exemplo:</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>Se <code>isIntentSafe</code> for <code>true</code>, pelo menos um aplicativo responderá à +intenção. Se for <code>false</code>, não há aplicativos disponíveis para responder à intenção.</p> + +<p class="note"><strong>Observação:</strong> faça essa verificação quando a atividade +iniciar, caso seja necessário desabilitar a funcionalidade que usa a intenção antes do usuário tentar utilizá- +la. Caso conheça um aplicativo específico que possa lidar com a intenção, forneça um link para que o +usuário baixe o aplicativo (veja como <a href="{@docRoot}distribute/tools/promote/linking.html">vincular para seus produtos no Google +Play</a>).</p> + + +<h2 id="StartActivity">Iniciar uma atividade com uma intenção</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>Figura 1.</strong> Exemplo de diálogo de seleção que aparece +quando mais de um aplicativo pode tratar uma intenção.</p> +</div> + +<p>Quando tiver criado sua {@link android.content.Intent} e definido as informações extras, chame {@link +android.app.Activity#startActivity startActivity()} para enviá-la ao sistema. Se o sistema +identificar mais de uma atividade que pode tratar a intenção, um diálogo exibirá para que o usuário +selecione qual aplicativo usar, como mostrado na figura 1. Se houver apenas uma atividade para tratar a +intenção, o sistema a iniciará imediatamente.</p> + +<pre> +startActivity(intent); +</pre> + +<p>Este é um exemplo completo que mostra como criar uma intenção para exibir um mapa, confirmar se há +um aplicativo para tratar a intenção e iniciar:</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">Mostrar um selecionador de aplicativo</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>Figura 2.</strong> Um diálogo seletor.</p> +</div> + +<p>Observe que ao iniciar uma atividade passando seu {@link android.content.Intent} para {@link +android.app.Activity#startActivity startActivity()} e quando há mais de um aplicativo que responde à +intenção, o usuário pode selecionar qual aplicativo usar por padrão (marcando uma caixa de seleção na parte inferior +do diálogo, veja a figura 1). Isto é bom quando se executa uma ação para qual o usuário +deseja usar o mesmo aplicativo todas as vezes, como quando abre uma página da Web (o usuário +geralmente usa apenas um navegador) ou para tirar fotos (o usuário prefere uma câmera).</p> + +<p>Contudo, se a ação a ser executada puder ser tratada por vários aplicativos e o usuário +preferir um aplicativo diferente a cada vez, como a ação “compartilhar” onde os usuários podem ter vários +aplicativos para compartilhar um item, você deve exibir explicitamente um diálogo seletor +conforme mostrado na figura 2. O diálogo seletor +força o usuário a selecionar qual aplicativo usar para a ação todas as vezes (o usuário não pode selecionar +um aplicativo padrão para a ação).</p> + +<p>Para exibir o seletor, crie uma {@link android.content.Intent} usando {@link +android.content.Intent#createChooser createChooser()} e transmita para {@link +android.app.Activity#startActivity startActivity()}. Por exemplo:</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>Isso exibe um diálogo com uma lista de aplicativos que respondem à intenção transmitida ao método {@link +android.content.Intent#createChooser createChooser()} e utiliza o texto fornecido como +título do diálogo.</p> + + + diff --git a/docs/html-intl/intl/ru/training/basics/activity-lifecycle/index.jd b/docs/html-intl/intl/ru/training/basics/activity-lifecycle/index.jd new file mode 100644 index 0000000..b8de11e --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/activity-lifecycle/index.jd @@ -0,0 +1,72 @@ +page.title=Управление жизненным циклом операций +page.tags=жизненный цикл операций +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/ru/training/basics/activity-lifecycle/pausing.jd b/docs/html-intl/intl/ru/training/basics/activity-lifecycle/pausing.jd new file mode 100644 index 0000000..c483780 --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/activity-lifecycle/pausing.jd @@ -0,0 +1,147 @@ +page.title=Приостановка и возобновление операции +page.tags=жизненный цикл операции +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#onPause onPause()} на {@link android.app.Activity}, что позволяет +остановить текущие действия, которые не должны продолжаться во время паузы (например воспроизведение видео), или сохранять +любую информацию, которая должна постоянно сохраняться в случае выхода пользователя из приложения. Если +пользователь возвращается к операции после паузы, система возобновляет ее и вызывает метод +{@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>Остановка анимации или других текущих действий, которые могут потреблять ресурсы процессора.</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>Обычно <strong>не</strong> следует использовать {@link android.app.Activity#onPause()} для сохранения +пользовательских изменений (например введенной в форму личной информации) в постоянном хранилище. Изменения +пользователя нужно сохранять в постоянном хранилище {@link android.app.Activity#onPause()} +только в тех случаях, когда пользователи ожидают автоматического сохранения изменений (например при составлении черновика электронного письма). +Однако в состоянии {@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/ru/training/basics/activity-lifecycle/recreating.jd b/docs/html-intl/intl/ru/training/basics/activity-lifecycle/recreating.jd new file mode 100644 index 0000000..acb89fa --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/activity-lifecycle/recreating.jd @@ -0,0 +1,178 @@ +page.title=Воссоздание операции +page.tags=жизненный цикл операции +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 могла восстановить состояние +представлений операции, <strong>каждое представление должно иметь уникальный идентификатор</strong>, предоставляемый атрибутом +<a href="{@docRoot}reference/android/view/View.html#attr_android:id">{@code +android:id}</a>.</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}. +Если операция будет уничтожена, +и системе нужно будет воссоздать тот же экземпляр, она передаст данные +состояния, определенные в (1), методам {@link android.app.Activity#onCreate onCreate()} +(2) и {@link android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} +(3).</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. В этом случае +система создает новый экземпляр операции +вместо восстановления ранее уничтоженного экземпляра.</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#onRestoreInstanceState onRestoreInstanceState()}, который система вызывает +после метода {@link android.app.Activity#onStart()}. Система вызывает {@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#onRestoreInstanceState onRestoreInstanceState()} следует вызывать во всех случаях, чтобы реализация +по умолчанию могла сохранить состояние новой иерархии.</p> + +<p>Более подробную информацию о воссоздании операции в связи +с перезапуском во время исполнения (например при повороте экрана) можно найти в разделе <a href="{@docRoot}guide/topics/resources/runtime-changes.html">Обработка изменений в режиме выполнения</a>.</p> + diff --git a/docs/html-intl/intl/ru/training/basics/activity-lifecycle/starting.jd b/docs/html-intl/intl/ru/training/basics/activity-lifecycle/starting.jd new file mode 100644 index 0000000..3a946e2 --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/activity-lifecycle/starting.jd @@ -0,0 +1,285 @@ +page.title=Запуск операции +page.tags=жизненный цикл операции +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>. Упрощенная иллюстрация жизненного цикла операции +в виде многоступенчатой пирамиды. На рисунке показано, что для каждого обратного вызова, +поднимающего операцию на одну ступень к состоянию возобновления на вершине пирамиды, +существует обратный вызов, опускающий операцию на одну ступень вниз. Возобновление операции также может производиться из состояний +паузы и остановки.</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>Возобновление</dt> + <dd>В этом состоянии операция выполняется в экранном режиме, и пользователь может с ней взаимодействовать. +Иногда это состояние также называется рабочим состоянием.</dd> + <dt>Приостановка</dt> + <dd>В этом состоянии операция частично закрывается другой операцией — в экранном режиме +эта другая операция или является полупрозрачной, или не закрывает экран целиком. Приостановленная операция +не получает команд пользователя и не может выполнять код. + <dt>Остановка</dt> + <dd>В этом состоянии операция полностью скрыта и невидима для пользователя. Она считается находящейся в +фоновом режиме. В состоянии остановки сохраняется экземпляр операции и вся его информация +о состоянии, например, переменных, однако операция не может выполнять код.</dd> +</dl> + +<p>Другие состояния (создание и запуск) являются переходными, и система быстро переходит от них +к следующим состояниям посредством вызова следующего метода обратного вызова в жизненном цикле. Таким образом, после вызова +{@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#onCreate onCreate()} для {@link android.app.Activity} в вашем приложении +в соответствии с тем, какую операцию вы задекларировали как операцию запуска (или основную операцию). Эта операция выступает +основной точкой входа в пользовательский интерфейс вашего приложения.</p> + +<p>Для определения основной операции вы можете использовать файл манифеста Android <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">{@code AndroidManifest.xml}</a>, +который находится в корневом каталоге вашего проекта.</p> + +<p>Основная операция приложения должна декларироваться в манифесте с помощью фильтра <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a>, включающего действие {@link +android.content.Intent#ACTION_MAIN MAIN} и категорию +{@link android.content.Intent#CATEGORY_LAUNCHER LAUNCHER}. Например:</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 с помощью инструментов +Android SDK файлы проекта по умолчанию включают класс {@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} посредством вызова его метода {@link +android.app.Activity#onCreate onCreate()}.</p> + +<p>Вы должны реализовать метод {@link android.app.Activity#onCreate onCreate()} для выполнения базовой +логики запуска приложения, которое должно производиться только один раз для всего срока существования операции. Например, +ваша реализация {@link android.app.Activity#onCreate onCreate()} должна содержать определение пользовательского +интерфейса и возможно создавать экземпляры некоторых переменных уровня класса.</p> + +<p>Например, в следующем примере метода {@link android.app.Activity#onCreate onCreate()} +показан код, выполняющий фундаментальную настройку операции, включая +декларирование пользовательского интерфейса (определен в файле макета XML), определение составных переменных +и частичную настройку пользовательского интерфейса.</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()} по +очереди. Операция никогда не остается в состоянии создания или запуска. Технически +операция становится видимой для пользователя при вызове {@link android.app.Activity#onStart()}, однако затем сразу же происходит +{@link android.app.Activity#onResume()} и операция остается в состоянии возобновления, +пока что-то не произойдет, например пока не поступит телефонный звонок, пользователь не переключится +на другую операцию или экран устройства не выключится.</p> + +<p>На последующих уроках вы увидите, как можно с пользой использовать другие методы запуска, {@link +android.app.Activity#onStart()} и {@link android.app.Activity#onResume()}, в жизненном цикле операции +при возобновлении работы после паузы или остановки.</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()}. После завершения этой серии +обратных вызовов операция переходит в состояние возобновления, где пользователи могут +взаимодействовать с операцией до тех пор, пока не переключатся на другую операцию.</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#onDestroy} +после вызова {@link android.app.Activity#onPause} и {@link +android.app.Activity#onStop} во всех случаях, кроме ситуации, когда вы вызываете {@link +android.app.Activity#finish()} из метода {@link android.app.Activity#onCreate onCreate()} +. В некоторых случаях, например когда ваша операция временно отвечает за принятие решения о запуске +другой операции, вы можете вызвать {@link android.app.Activity#finish()} из метода {@link +android.app.Activity#onCreate onCreate()} для уничтожения операции. В этом случае система +сразу же вызывает {@link android.app.Activity#onDestroy}, не вызывая другие методы жизненного +цикла.</p> diff --git a/docs/html-intl/intl/ru/training/basics/activity-lifecycle/stopping.jd b/docs/html-intl/intl/ru/training/basics/activity-lifecycle/stopping.jd new file mode 100644 index 0000000..27c771f --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/activity-lifecycle/stopping.jd @@ -0,0 +1,187 @@ +page.title=Остановка и перезапуск операции +page.tags=жизненный цикл операции +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()}, позволяющие явно +обрабатывать остановку и перезапуск операции. В отличие от состояния паузы, +означающем частичное уничтожение пользовательского интерфейса, в состоянии остановки пользовательский интерфейс больше не +отображается и пользователь переключается на отдельную операцию (или отдельное приложение).</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#onPause onPause()} перед вызовом {@link +android.app.Activity#onStop onStop()}.</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#onPause onPause()} вызывается до +{@link android.app.Activity#onStop()}, вам следует использовать {@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.view.View} (например, текста в {@link +android.widget.EditText}) в {@link android.os.Bundle} (наборе пар "ключ-значение") и восстановит +их, если пользователь вернется в тот же экземпляр операции (на <a href="recreating.html">следующем уроке</a> мы более подробно поговорим об использовании {@link android.os.Bundle} для сохранения +других данных состояния в случае уничтожения и воссоздания вашей операции).</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#onDestroy()} +для вашего {@link android.app.Activity}. Поскольку вы уже освобождаете большую часть +ресурсов с помощью {@link android.app.Activity#onStop()} к моменту вызова {@link +android.app.Activity#onDestroy()}, большинству приложений почти ничего не нужно делать. Этот метод представляет собой последний +шанс освободить ресурсы, могущие привести к утечке памяти, обеспечивая уверенность +в уничтожении дополнительных потоков и других долгосрочных действий, например, отслеживания +методов.</p> + diff --git a/docs/html-intl/intl/ru/training/basics/data-storage/databases.jd b/docs/html-intl/intl/ru/training/basics/data-storage/databases.jd new file mode 100644 index 0000000..418d288 --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/data-storage/databases.jd @@ -0,0 +1,317 @@ +page.title=Сохранение данных в базах данных SQL +page.tags=хранение данных +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, и он поможет вам начать работать с базами данных +SQLite на платформе Android. API-интерфейсы, необходимые для использования базы данных +на платформе Android, доступны в составе пакета {@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.database.sqlite.SQLiteOpenHelper#getWritableDatabase} или {@link +android.database.sqlite.SQLiteOpenHelper#getReadableDatabase} в фоновом потоке, +например с {@link android.os.AsyncTask} или {@link android.app.IntentService}.</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()} +представляет собой просто название таблицы. Второй аргумент указывает +имя столбца, в который инфраструктура вставляет значение NULL, если +{@link android.content.ContentValues} является пустым (если вместо этого указать {@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} get, например {@link android.database.Cursor#getString +getString()} или {@link android.database.Cursor#getLong getLong()}. Для каждого из методов get +вы должны передать указатель желаемого столбца, который может вызвать +{@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()} и синтаксиса {@code where} +для {@link android.database.sqlite.SQLiteDatabase#delete delete()}.</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/ru/training/basics/data-storage/files.jd b/docs/html-intl/intl/ru/training/basics/data-storage/files.jd new file mode 100644 index 0000000..2afecea --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/data-storage/files.jd @@ -0,0 +1,379 @@ +page.title=Сохранение файлов +page.tags=хранение данных +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 +и стандартными файловыми API-интерфейсами ввода/вывода в {@link java.io}.</p> + + +<h2 id="InternalVsExternalStorage">Выбор внутреннего или внешнего хранилища</h2> + +<p>Все устройства Android имеют две области хранения файлов: внутренняя память и внешние хранилища. Эти области +появились в первые годы существования Android, когда на большинстве устройств имелись встроенная память +(внутреннее хранилище) и карты памяти (например micro SD, внешнее хранилище). +Некоторые устройства делят встроенную память на внутренний и внешний разделы, +так что даже без съемных носителей в системе две области хранения файлов, +и API-интерфейс работает одинаково вне зависимости от типа внешнего хранилища. +Ниже подробно описаны обе области хранения файлов.</p> + +<div class="col-5" style="margin-left:0"> +<p><b>Внутренняя память</b></p> +<ul> +<li>Всегда доступна.</li> +<li>Сохраненные здесь файлы по умолчанию доступны только вашему приложению.</li> +<li>При удалении пользователем вашего приложения система Android удаляет из внутренней памяти +все файлы этого приложения.</li> +</ul> +<p>Внутренняя память лучше всего подходит для ситуаций, когда вы хотите быть уверены, что ни пользователь, ни другие приложения не смогут получить +доступ к вашим файлам.</p> +</div> + +<div class="col-7" style="margin-right:0"> +<p><b>Внешнее хранилище</b></p> +<ul> +<li>Доступно не всегда, потому что пользователь может в любое время подключать и отключать такие +хранилища, например, USB-накопители.</li> +<li>Такие хранилища доступны для чтения везде, +поэтому вы не контролируете чтение сохраненных в них данных.</li> +<li>При удалении пользователем вашего приложения система Android удаляет из внешних хранилищ +файлы этого приложения, только если они сохраняются в директории из {@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>Для записи во внешнее хранилище следует указать запрос +разрешения {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} в <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">файле манифеста</a>:</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}, соответствующий внутренней директории файлов временной +кэш-памяти приложения. Обязательно удаляйте каждый файл, когда он перестанет +быть нужным, и устанавливайте разумное ограничение размера памяти, используемой в каждый момент времени, +например, 1 МБ. Если системе будет не хватать места в хранилище, она может удалять файлы +из кэш-памяти без уведомления.</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. +Технически другое приложение может прочитать ваши файлы во внутренней памяти, если вы установите для файлов +режим Readable (доступно для чтения). Однако для этого другому приложению должны быть известны имя пакета вашего +приложения и имена файлов. Другие приложения не могут просматривать внутренние каталоги вашего приложения и не имеют разрешений на +чтение или запись, если вы специально не установите для своих файлов режим Readable (доступно для чтения) или Writable (доступно для записи). Следовательно, пока +вы будете использовать режим {@link android.content.Context#MODE_PRIVATE} для своих файлов во внутренней памяти, +они будут недоступны другим приложениям.</p> + + + + + +<h2 id="WriteExternalStorage">Сохранение файла во внешнем хранилище</h2> + +<p>Поскольку внешнее хранилище может быть недоступно— например, если пользователь установил +хранилище в гнездо на компьютере или извлек из устройства 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()} для собственных файлов приложения, вы должны использовать имена каталогов, предоставляемые +постоянными значениями API-интерфейсов, например, +{@link android.os.Environment#DIRECTORY_PICTURES}. Эти имена каталогов обеспечивают +правильную обработку файлов системой. Например, сохраненные в {@link +android.os.Environment#DIRECTORY_RINGTONES} файлы относятся медиа-сканером системы в категорию рингтонов, +а не музыки.</p> + + + + +<h2 id="GetFreeSpace">Запрос доступного пространства</h2> + +<p>Если вам заранее известен объем сохраняемых данных, вы можете +определить наличие достаточного пространства без исключения {@link +java.io.IOException}, вызвав метод {@link java.io.File#getFreeSpace} или {@link +java.io.File#getTotalSpace}. Эти методы позволяют узнать текущее доступное пространство и +общее пространство в хранилище. Эта информация также позволять +избежать переполнения объема хранилища сверх определенного уровня.</p> + +<p>Однако система не гарантирует возможность записи такого же количества байт, как указано +в {@link java.io.File#getFreeSpace}. Если выводимое число на +несколько мегабайт превышает размер данных, которые вы хотите сохранить, или если файловая система заполнена +менее, чем на 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/ru/training/basics/data-storage/index.jd b/docs/html-intl/intl/ru/training/basics/data-storage/index.jd new file mode 100644 index 0000000..064239b --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/data-storage/index.jd @@ -0,0 +1,57 @@ +page.title=Сохранение данных +page.tags=хранение данных,файлы,sql,база данных,настройки +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>Знакомство с коллекциями значений ключей Map</li> + <li>Знакомство с файловым API-интерфейсом ввода/вывода Java</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/ru/training/basics/data-storage/shared-preferences.jd b/docs/html-intl/intl/ru/training/basics/data-storage/shared-preferences.jd new file mode 100644 index 0000000..61a0037 --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/data-storage/shared-preferences.jd @@ -0,0 +1,120 @@ +page.title=Сохранение наборов "ключ-значение" +page.tags=хранение данных +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">Запись в общие настройки</a></li> + <li><a href="#ReadSharedPreference">Чтение общих настроек</a></li> +</ol> + +<h2>См. также:</h2> +<ul> + <li><a href="{@docRoot}guide/topics/data/data-storage.html#pref">Использование общих настроек</a></li> +</ul> + +</div> +</div> + + +<p>Если вы хотите сохранить относительно небольшой набор пар "ключ-значение", +используйте {@link android.content.SharedPreferences} API-интерфейсы. +Объект {@link android.content.SharedPreferences} указывает на файл, +содержащий пары "ключ-значение", и предоставляет простые методы для чтения и записи. Управление каждым +файлом {@link android.content.SharedPreferences} осуществляется +с помощью инфраструктуры и может быть частным или общим.</p> + +<p>На этом уроке вы узнаете, как использовать API-интерфейсы {@link android.content.SharedPreferences} для сохранения и +получения простых значений.</p> + +<p class="note"><strong>Примечание.</strong> API-интерфейсы {@link android.content.SharedPreferences} предназначены +только для чтения и записи пар "ключ-значение", и их не следует путать с API-интерфейсами +{@link android.preference.Preference}, которые помогают создать пользовательский интерфейс +для настроек приложения (хотя они используют {@link android.content.SharedPreferences} в качестве своей +реализации для сохранения настроек приложения). Информацию об использовании API-интерфейсов {@link +android.preference.Preference} см. в руководстве <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">Записать в общие настройки</h2> + +<p>Для записи в файл общих настроек создайте объект {@link +android.content.SharedPreferences.Editor} посредством вызова {@link +android.content.SharedPreferences#edit} в {@link android.content.SharedPreferences}.</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">Чтение общих настроек</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/ru/training/basics/intents/filters.jd b/docs/html-intl/intl/ru/training/basics/intents/filters.jd new file mode 100644 index 0000000..0f5bb31 --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/intents/filters.jd @@ -0,0 +1,236 @@ +page.title=Разрешение другим приложениям на запуск вашей операции +page.tags=намерения +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + +<div id="tb-wrapper"> + <div id="tb"> + +<h2>Содержание этого урока</h2> +<ol> + <li><a href="#AddIntentFilter">Добавление фильтра Intent</a></li> + <li><a href="#HandleIntent">Обработка объекта Intent в операции</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>Предыдущие два урока были посвящены одной теме – запуску операции вашего приложения +в другом приложении. Однако если ваше приложение может выполнять операции, полезные другим приложениям, +его нужно подготовить так, чтобы оно реагировало на запросы других приложений. Представьте, что вы разработали +приложение для социальных сетей, позволяющее отправлять сообщения и фотографии друзьям пользователя. В этом случае в ваших интересах +будет поддерживать объекты Intent (намерения) {@link android.content.Intent#ACTION_SEND}, чтобы пользователи могли +поделиться контентом из другого приложения и запускать ваше приложение для выполнения требуемого действия.</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>.</p> + +<p>Если ваше приложение установлено на устройстве, система идентифицирует ваши +фильтры Intent и добавляет информацию во внутренний каталог намерений, поддерживаемый всеми установленными приложениями. +Когда приложение осуществляет вызов {@link android.app.Activity#startActivity +startActivity()} или {@link android.app.Activity#startActivityForResult startActivityForResult()} +с неявными объектами Intent, система определяет, какая операция (или какие операции) может отреагировать на объект +Intent.</p> + + + +<h2 id="AddIntentFilter">Добавление фильтра Intent</h2> + +<p>Чтобы правильно определить, какие объекты Intent может обрабатывать ваша операция, каждый добавляемый вами фильтр Intent +должен быть максимально определенным с точки зрения действий и данных, принимаемых +операцией.</p> + +<p>Система может отправить указанный {@link android.content.Intent} в операцию, если у нее имеется +фильтр Intent, соответствующий следующим критериям объекта {@link android.content.Intent}:</p> + +<dl> + <dt>Действие</dt> + <dd>Строка, называющая действие, которое необходимо выполнить. Обычно это одно из определяемых платформой значений, +например, {@link android.content.Intent#ACTION_SEND} или {@link android.content.Intent#ACTION_VIEW}. + <p>Это следует указать в фильтре Intent с элементом <a href="{@docRoot}guide/topics/manifest/action-element.html">{@code <action>}</a>. +Указанное в этом элементе значение должно представлять собой полное имя строки действия, а не +постоянное значение API-интерфейса (см. примеры ниже).</p></dd> + + <dt>Данные</dt> + <dd>Описание данных, связанных с объектом Intent. + <p>Указывается в фильтре Intent с элементом <a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a>. Используя один +или несколько атрибутов в этом элементе, вы можете указать только тип MIME, только префикс URI, +только схему URI или из сочетание, а также другие индикаторы типа +принимаемых данных.</p> + <p class="note"><strong>Примечание.</strong> Если вам не нужно декларировать специфику данных +{@link android.net.Uri}(например, когда операция использует другие виды дополнительных данных вместо +URI), вы должны указать только атрибут {@code android:mimeType} для декларирования типа +данных, с которыми работает ваша операция, например, {@code text/plain} или {@code image/jpeg}.</p> +</dd> + <dt>Категория</dt> + <dd>Дополнительный способ описания характеристик операции, обрабатывающей объект Intent который обычно связан +с жестом пользователя или местом запуска. Система поддерживает несколько разных категорий, +но большинство из них используется редко. Однако по умолчанию все неявные объекты Intent определяются с +{@link android.content.Intent#CATEGORY_DEFAULT}. + <p>Это указывается в фильтре Intent с элементом <a href="{@docRoot}guide/topics/manifest/category-element.html">{@code <category>}</a> +.</p></dd> +</dl> + +<p>В своем фильтре Intent вы можете декларировать критерии, принимаемые вашей операцией. +Для этого нужно декларировать каждый из них с соответствующими элементами XML, вложенными в элемент <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a> +.</p> + +<p>Рассмотрим в качестве примера операцию с фильтром Intent, обрабатывающим объект {@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>Каждый входящий объект Intent указывает только одно действие и только один тип данных, однако допускается декларирование нескольких +экземпляров элементов <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> в каждом +<a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a>.</p> + +<p>Если поведение любых двух пар действий и данных является взаимоисключающим, +необходимо создать отдельные фильтры Intent с указанием допустимых действий +в сочетании с соответствующими типами данных.</p> + +<p>Допустим, ваша операция обрабатывает текст и изображения для объектов Intent {@link +android.content.Intent#ACTION_SEND} и {@link +android.content.Intent#ACTION_SENDTO}. В этом случае вам необходимо определить два отдельных фильтра Intent +для двух действий, потому что объект Intent {@link +android.content.Intent#ACTION_SENDTO} должен использовать данные {@link android.net.Uri} для указания +адреса получателя с использованием схемы URI {@code send} или {@code sendto}. Например:</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> Для получения неявных объектов Intent необходимо включить категорию +{@link android.content.Intent#CATEGORY_DEFAULT} в фильтр Intent. Методы {@link +android.app.Activity#startActivity startActivity()} и {@link +android.app.Activity#startActivityForResult startActivityForResult()} обрабатывают все объекты Intent, как если бы они +декларировали категорию {@link android.content.Intent#CATEGORY_DEFAULT}. Если вы не декларируете ее +в своем фильтре Intent, никакие неявные объекты Intent не будут разрешаться в вашу операцию.</p> + +<p>Более подробную информацию об отправке и получении объектов {@link android.content.Intent#ACTION_SEND} +Intent для социальных сетей и обмена данными см. в уроке <a href="{@docRoot}training/sharing/receive.html">Получение простых данных от других приложений</a>.</p> + + +<h2 id="HandleIntent">Обработка объекта Intent в операции</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()}, если запустивший вашу операцию объект Intent может +ожидать результат. Если исходная операция вызвала метод {@link +android.app.Activity#startActivityForResult startActivityForResult()}, система передаст +ей результат, который вы передаете {@link android.app.Activity#setResult(int,Intent) setResult()}. В противном случае +результат будет проигнорирован.</p> + + + + + + diff --git a/docs/html-intl/intl/ru/training/basics/intents/index.jd b/docs/html-intl/intl/ru/training/basics/intents/index.jd new file mode 100644 index 0000000..cfb606d --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/intents/index.jd @@ -0,0 +1,62 @@ +page.title=Взаимодействие с другими приложениями +page.tags=объекты Intent,операция +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"> +Интеграция приложений с объектами Intent (сообщение в блоге)</a></li> + <li><a href="{@docRoot}guide/components/intents-filters.html">Объекты Intent и фильтры +объектов Intent</a></li> +</ul> + +</div> +</div> + +<p>В приложениях Android обычно имеется несколько <a href="{@docRoot}guide/components/activities.html">операций</a>. Каждая операция отображает +пользовательский интерфейс, позволяющий пользователю выполнить определенную задачу (например, посмотреть карту или сделать снимок). +Чтобы обеспечить переход пользователя от одной операции к другой, приложение должно использовать объект {@link +android.content.Intent} для определения "намерения" приложения что-то сделать. При передаче системе +{@link android.content.Intent} с помощью такого метода как {@link +android.app.Activity#startActivity startActivity()} система использует {@link +android.content.Intent} для идентификации и запуска соответствующего компонента приложения. Использование объектов Intent даже +позволяет приложению запускать операции, содержащиеся в отдельном приложении.</p> + +<p>{@link android.content.Intent} может <em>явно</em> запускать определенный компонент +(определенный экземпляр {@link android.app.Activity}) или <em>косвенно</em> запускать любой +компонент, способный выполнить желаемую операцию (например, "сделать снимок").</p> + +<p>В этом учебном курсе рассказывается о том, как использовать {@link android.content.Intent} для простого +взаимодействия с другими приложениями, например для запуска другого приложения, получения результата от этого приложения и обеспечения +способности приложения реагировать на объекты Intent из других приложений.</p> + +<h2>Уроки</h2> + +<dl> + <dt><b><a href="sending.html">Направление пользователя в другое приложение</a></b></dt> + <dd>Вы узнаете, как создавать неявные объекты Intent для запуска других приложений, способных выполнить +операцию.</dd> + <dt><b><a href="result.html">Получение результата операции</a></b></dt> + <dd>В этом уроке демонстрируется, как запустить другую операцию и получить ее результат.</dd> + <dt><b><a href="filters.html">Разрешение другим приложениям на запуск операции</a></b></dt> + <dd>В этом уроке демонстрируется, как разрешить другим приложениям использовать операции вашего приложения за счет определения +фильтров объектов Intent, которые декларируют неявные намерения, принимаемые вашим приложением.</dd> +</dl> + diff --git a/docs/html-intl/intl/ru/training/basics/intents/result.jd b/docs/html-intl/intl/ru/training/basics/intents/result.jd new file mode 100644 index 0000000..8ab03d4 --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/intents/result.jd @@ -0,0 +1,178 @@ +page.title=Получение результата операции +page.tags=объекты Intent +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>В этом примере результаты, {@link android.content.Intent} возвращаемые приложениями +Android Контакты или Люди, предоставляют контент {@link android.net.Uri}, который идентифицирует +выбранный пользователем контакт.</p> + +<p>Для успешной обработки результатов необходимо понимать, каким будет формат этих результатов +{@link android.content.Intent}. Это просто, если результат возвращается одной из ваших +собственных операций. Приложения, входящие в состав платформы Android, имеют собственные прикладные интерфейсы, так что +вы можете рассчитывать на получение определенных результатов. Например, приложение "Люди" (приложение "Контакты" в старых +версиях) всегда возвращает результат с URI контента, идентифицирующий выбранный контакт, а приложение +"Камера" возвращает {@link android.graphics.Bitmap} в дополнительном {@code "data"} (см. урок +<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, приложение "Контакты/Люди" дает вашему приложению временное +разрешение на чтение данных Поставщика контактов при выводе результата. Это временное разрешение +действует только в отношении конкретного запрошенного контакта, так что нельзя запрашивать другой контакт, +кроме указанного объектом Intent {@link android.net.Uri}, если вы не хотите декларировать разрешение {@link +android.Manifest.permission#READ_CONTACTS}.</p> + + + + + + + + + + + + + + + diff --git a/docs/html-intl/intl/ru/training/basics/intents/sending.jd b/docs/html-intl/intl/ru/training/basics/intents/sending.jd new file mode 100644 index 0000000..da147ee --- /dev/null +++ b/docs/html-intl/intl/ru/training/basics/intents/sending.jd @@ -0,0 +1,256 @@ +page.title=Направление пользователя в другое приложение +page.tags=объекты Intent +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + + +<div id="tb-wrapper"> + <div id="tb"> + +<h2>Содержание этого урока</h2> +<ol> + <li><a href="#Build">Создание явного объекта Intent</a></li> + <li><a href="#Verify">Проверка наличия приложения для получения объекта Intent</a></li> + <li><a href="#StartActivity">Запуск операции с объектом Intent</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>, объекты Intent нужно использовать для навигации между операциями в собственном приложении. Обычно +для этого используются <em>явные объекты Intent</em>, определяющие точное имя класса +компонента, который вы хотите запустить. Однако если вы хотите, чтобы действие (например +просмотр карты) выполнялось отдельным приложением, следует использовать <em>неявный объект Intent</em>.</p> + +<p>На этом уроке вы узнаете, как создать неявный объект Intent для определенного действия и использовать его +для запуска операции, выполняющей действие в другом приложении.</p> + + + +<h2 id="Build">Создание неявного объекта Intent</h2> + +<p>Неявные объекты Intent не декларируют имя класса запускаемого компонента, а декларируют +выполняемое действие. Действие указывает задачу, которую вы хотите выполнить, например <em>просмотр</em>, +<em>правка</em>,<em> отправка</em> или <em>получение</em> чего-либо. Объекты Intent часто также содержат данные, ассоциируемые +с действием, например адрес для просмотра или электронное сообщение для отправки. +В зависимости от того, какой объект Intent вы хотите создать, данные могут относиться к типу {@link android.net.Uri}, +одному из нескольких других типов данных, либо объекту могут вообще не требоваться данные.</p> + +<p>Если ваши данные относятся к типу {@link android.net.Uri}, вы можете использовать простой конструктор {@link +android.content.Intent#Intent(String,Uri) Intent()} для определения действия и +данных.</p> + +<p>Приведем пример создания объекта Intent для запуска телефонного звонка, в котором данные {@link +android.net.Uri} используются для указания телефонного номера:</p> + +<pre> +Uri number = Uri.parse("tel:5551234"); +Intent callIntent = new Intent(Intent.ACTION_DIAL, number); +</pre> + +<p>Когда ваше приложение активирует этот объект Intent посредством вызова {@link android.app.Activity#startActivity +startActivity()}, приложение "Телефон" инициирует звонок на указанный номер телефона.</p> + +<p>Вот еще несколько объектов Intent в сочетании с действиями и {@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>Другие виды неявных объектов Intent требуют дополнительные данные, предоставляющие разные типы данных, +например строки. Вы можете добавить один или несколько элементов дополнительных данных с помощью разных методов {@link +android.content.Intent#putExtra(String,String) putExtra()}.</p> + +<p>Система по умолчанию определяет соответствующий тип MIME, который требует объект Intent на базе включенных данных +{@link android.net.Uri}. Если не включать {@link android.net.Uri} в объект +Intent, обычно нужно использовать {@link android.content.Intent#setType setType()} для указания типа +данных, связанного с объектом Intent. Установка типа MIME также определяет, какие виды +действий должен получать объект Intent.</p> + +<p>Вот некоторые объекты Intent, добавляющие дополнительные данные для указания желаемого действия:</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> Этот объект Intent для создания события в календаре поддерживается только +API-интерфейсами уровня 14 и выше.</p> + </li> +</ul> + +<p class="note"><strong>Примечание.</strong> Очень важно определять объекты {@link +android.content.Intent} как можно более конкретно. Например, если вы хотите вывести изображение +с помощью объекта Intent {@link android.content.Intent#ACTION_VIEW}, вам следует указать тип MIME +{@code image/*}. Это не даст объекту Intent запускать приложения для просмотра других типов +данных (например картографические приложения).</p> + + + +<h2 id="Verify">Проверка наличия приложения для получения объекта Intent</h2> + +<p>Хотя платформа Android гарантирует выполнение объектов Intent во +встроенных приложениях (таких как "Телефон", "Электронная почта" или "Календарь"), перед активацией объекта Intent всегда следует добавлять шаг +проверки.</p> + +<p class="caution"><strong>Внимание!</strong> Если вы активируете объект Intent, а на устройстве не +будет приложения, способного его обработать, ваше приложение закроется с ошибкой.</p> + +<p>Чтобы убедиться в наличии операции, реагирующей на объект Intent, вызовите метод {@link +android.content.pm.PackageManager#queryIntentActivities queryIntentActivities()} для получения списка +операций, способных обработать ваш {@link android.content.Intent}. Если полученный в результате список {@link +java.util.List} не пустой, вы можете безопасно использовать данный объект Intent. Например:</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>, то хотя бы одно приложение отреагирует на объект +Intent. Если же он имеет значение <code>false</code>, то на устройстве нет приложений для обработки данного объекта Intent.</p> + +<p class="note"><strong>Примечание.</strong> Такую проверку следует выполнять при первом +запуске операции на случай, если понадобится отключить функцию, обрабатывающую объект Intent, до того, как пользователь попытается использовать +ее. Если вам известно определенное приложение, которое может обработать данный объект Intent, вы можете указать ссылку, +по которой пользователь может загрузить приложение (посмотрите, как <a href="{@docRoot}distribute/tools/promote/linking.html">добавить ссылку на свой продукт в Google +Play</a>).</p> + + +<h2 id="StartActivity">Запуск операции с объектом Intent</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> Пример диалогового окна выбора, +которое отображается, если объект Intent могут обработать разные приложения.</p> +</div> + +<p>После создания {@link android.content.Intent} и установки дополнительной информации вызовите {@link +android.app.Activity#startActivity startActivity()} для отправки в систему. Если система +идентифицирует несколько операций, способных обработать объект Intent, она выводит для пользователя диалоговое окно +выбора приложения, как показано на рисунке 1. Если объект Intent может быть обработан +только одной операцией, система сразу же запускает ее.</p> + +<pre> +startActivity(intent); +</pre> + +<p>Вот полный пример, показывающий, как создать объект Intent для просмотра карты, убедиться в наличии +приложения для его обработки и запустить это приложение:</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()} и наличии нескольких приложений, реагирующих на +объект Intent, пользователь может выбрать, какое из этих приложений использовать по умолчанию (для этого нужно установить флажок в нижней +части диалогового окна; см. рисунок 1). Это удобно при выполнении действия, для которого +пользователь обычно хочет всегда использовать одно и то же приложение, например при открытии веб-страницы (пользователи +обычно используют один и тот же браузер) или создании снимка (пользователи обычно предпочитают одно и то же приложение камеры).</p> + +<p>Однако бывает так, что выполняемое действие может быть обработано несколькими приложениями, и пользователь +каждый раз может использовать разные приложения — например, действие "Поделиться", для которого пользователи могут использовать разные +приложения, — и в этом случае ваше приложение должно отображать диалоговое окно выбора приложения, +как показано на рисунке 2. В диалоговом окне +выбора приложения пользователь должен при каждом запуске выбирать, какое приложение использовать для действия (пользователь не +может выбрать приложение по умолчанию).</p> + +<p>Чтобы отобразить блок выбора приложения, создайте {@link android.content.Intent} с помощью {@link +android.content.Intent#createChooser createChooser()} и передайте его {@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>В результате отобразится диалоговое окно со списком приложений, которые могут отреагировать на объект Intent, переданный методу {@link +android.content.Intent#createChooser createChooser()} и используют указанный текст в качестве +заголовка диалога.</p> + + + diff --git a/docs/html-intl/intl/zh-cn/training/basics/activity-lifecycle/index.jd b/docs/html-intl/intl/zh-cn/training/basics/activity-lifecycle/index.jd new file mode 100644 index 0000000..90bf7a0 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/basics/activity-lifecycle/index.jd @@ -0,0 +1,72 @@ +page.title=管理活动生命周期 +page.tags=活动生命周期 +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/zh-cn/training/basics/activity-lifecycle/pausing.jd b/docs/html-intl/intl/zh-cn/training/basics/activity-lifecycle/pausing.jd new file mode 100644 index 0000000..23fc9f4 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/basics/activity-lifecycle/pausing.jd @@ -0,0 +1,147 @@ +page.title=暂停和继续活动 +page.tags=活动生命周期 +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>一般情况下,您<strong>不得</strong>使用 {@link android.app.Activity#onPause()} 永久性存储用户更改(比如输入表格的个人信息)。 +只有在您确定用户希望自动保存这些更改的情况(比如,电子邮件草稿)下,才能在 {@link android.app.Activity#onPause()}中永久性存储用户更改。但您应避免在 {@link +android.app.Activity#onPause()} 期间执行 CPU 密集型工作,比如向数据库写入信息,因为这会拖慢向下一活动过渡的过程(您应改为在 +{@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/zh-cn/training/basics/activity-lifecycle/recreating.jd b/docs/html-intl/intl/zh-cn/training/basics/activity-lifecycle/recreating.jd new file mode 100644 index 0000000..a91ed78 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/basics/activity-lifecycle/recreating.jd @@ -0,0 +1,178 @@ +page.title=重新创建活动 +page.tags=活动生命周期 +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.widget.EditText} 对象中的文本值)中有关每个 {@link android.view.View} 对象的信息。 + +这样,如果您的活动实例被销毁并重新创建,布局状态便恢复为其先前的状态,且您无需代码。 + +但是,您的活动可能具有您要恢复的更多状态信息,比如跟踪用户在活动中进度的成员变量。 + +</p> + +<p class="note"><strong>注意:</strong>为了 Android 系统恢复活动中视图的状态,<strong>每个视图必须具有</strong> +<a href="{@docRoot}reference/android/view/View.html#attr_android:id">{@code +android:id}</a> 属性提供的唯一 ID。 +</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} 实例必须重新创建时保存的额外状态数据。如果活动被销毁且必须重新创建相同的实例,系统将在 (1) 中定义的状态数据同时传递给 {@link android.app.Activity#onCreate onCreate()} 方法(2) 和 {@link android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} 方法(3)。 + + + + + +</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#onStart()} 方法之后调用的 {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} ,而不是在{@link android.app.Activity#onCreate onCreate()} 期间恢复状态。 + +系统只在存在要恢复的已保存状态时调用 {@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/zh-cn/training/basics/activity-lifecycle/starting.jd b/docs/html-intl/intl/zh-cn/training/basics/activity-lifecycle/starting.jd new file mode 100644 index 0000000..ab54a58 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/basics/activity-lifecycle/starting.jd @@ -0,0 +1,285 @@ +page.title=开始活动 +page.tags=活动生命周期 +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>简化的活动生命周期图示,以阶梯金字塔表示。 +此图示显示,对于用于将活动朝顶端的“继续”状态移动一阶的每个回调,有一种将活动下移一阶的回调方法。 + +活动还可以从“暂停”和“停止”状态回到继续状态。 +</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>继续</dt> + <dd>在这种状态下,活动处于前台,且用户可以与其交互。(有时也称为“运行”状态。) +</dd> + <dt>暂停</dt> + <dd>在这种状态下,活动被在前台中处于半透明状态或者未覆盖整个屏幕的另一个活动—部分阻挡。 +暂停的活动不会接收用户输入并且无法执行任何代码。 + + <dt>停止</dt> + <dd>在这种状态下,活动被完全隐藏并且对用户不可见;它被视为处于后台。 +停止时,活动实例及其诸如成员变量等所有状态信息将保留,但它无法执行任何代码。 +</dd> +</dl> + +<p>其他状态(“创建”和“开始”)是瞬态,系统会通过调用下一个生命周期回调方法从这些状态快速移到下一个状态。 +也就是说,在系统调用 +{@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>您的应用的主活动必须使用 <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a>(包括 {@link +android.content.Intent#ACTION_MAIN MAIN} 操作和 +{@link android.content.Intent#CATEGORY_LAUNCHER LAUNCHER} 类别)在宣示说明中声明。例如:</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工具创建新 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} +可防止旧版系统以这种方式仅在 Android 2.0 (API 级别5)和更高级别执行新 API 工作。 +较旧版本会遇到运行时异常。</p> + +<p>一旦 {@link android.app.Activity#onCreate onCreate()} 完成执行操作,系统会相继调用 {@link android.app.Activity#onStart()} 和 {@link android.app.Activity#onResume()} 方法。 + +您的活动从不会驻留在“已创建”或“已开始”状态。在技术上,活动会在 {@link android.app.Activity#onStart()} 被调用时变得可见,但紧接着是 +{@link android.app.Activity#onResume()},且活动保持“继续”状态,直到有事情发生使其发生变化,比如当接听来电时,用户导航至另一个活动,或设备屏幕关闭。 + + +</p> + +<p>在接下来的其他课程中,您将看到其他活动如何启动方法,当用于从“暂停”或“停止”状态继续活动时,{@link +android.app.Activity#onStart()} 和 {@link android.app.Activity#onResume()} 在您的活动生命周期中特别有用。 +</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()}。 +一旦这一系列回调完成,活动就进入“继续”状态,此时用户可与活动进行交互,直至用户切换到其他活动。 + +</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#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#onCreate onCreate()} 内调用 {@link android.app.Activity#finish()} 来销毁活动。 +在这种情况下,系统会立刻调用 {@link android.app.Activity#onDestroy},而不调用任何其他 生命周期方法。 + +</p> diff --git a/docs/html-intl/intl/zh-cn/training/basics/activity-lifecycle/stopping.jd b/docs/html-intl/intl/zh-cn/training/basics/activity-lifecycle/stopping.jd new file mode 100644 index 0000000..e10dff6 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/basics/activity-lifecycle/stopping.jd @@ -0,0 +1,187 @@ +page.title=停止并重新开始活动 +page.tags=活动生命周期 +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#onPause onPause()} 方法在 +{@link android.app.Activity#onStop()}之前调用,您应使用 {@link android.app.Activity#onStop onStop()} +执行更大、占用更多 CPU 的关闭操作,比如向数据库写入信息。 +</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}(键值对的二进制大对象)中的 {@link android.view.View} 对象(比如 {@link +android.widget.EditText} 中的文本),并在用户导航回活动的相同实例时恢复它们 (<a href="recreating.html">下一堂课</a> 讲述更多有关在您的活动被销毁且重新创建的情况下使用 {@link android.os.Bundle} 保存其他数据状态的知识)。 + + +</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#onStop()} 释放大多数您的资源,到您接收对 {@link +android.app.Activity#onDestroy()} 的调用时,大多数应用无需做太多操作。 +此方法是您清理可导致内存泄露的资源的最后一种方法,因此您应确保其他线程被销毁且其他长期运行的操作(比如方法跟踪)也会停止。 + + +</p> + diff --git a/docs/html-intl/intl/zh-cn/training/basics/data-storage/databases.jd b/docs/html-intl/intl/zh-cn/training/basics/data-storage/databases.jd new file mode 100644 index 0000000..a6c9193 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/basics/data-storage/databases.jd @@ -0,0 +1,317 @@ +page.title=在 SQL 数据库中保存数据 +page.tags=数据存储 +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 辅助工具创建数据库</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 辅助工具创建数据库</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>{@link +android.database.sqlite.SQLiteOpenHelper} 类中有一组有用的 API。当您使用此类获取对您数据库的引用时,系统将只在需要之时而不是 <em>应用启动</em>过程中执行可能长期运行的操作:创建和更新数据库。 + + + +您只需调用 +{@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} 或 +{@link android.database.sqlite.SQLiteOpenHelper#getReadableDatabase}。</p> + +<p class="note"><strong>注意:</strong>由于它们可能长期运行,因此请确保您在后台线程中调用 {@link +android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} 或 {@link +android.database.sqlite.SQLiteOpenHelper#getReadableDatabase} , +比如使用 {@link android.os.AsyncTask} 或 {@link android.app.IntentService}。 +</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} 获取方法之一读取列的值,比如 {@link android.database.Cursor#getString +getString()} 或 {@link android.database.Cursor#getLong getLong()}。对于每种获取方法,您必须传递所需列的索引位置,您可以通过调用 +{@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/zh-cn/training/basics/data-storage/files.jd b/docs/html-intl/intl/zh-cn/training/basics/data-storage/files.jd new file mode 100644 index 0000000..1442275 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/basics/data-storage/files.jd @@ -0,0 +1,379 @@ +page.title=保存文件 +page.tags=数据存储 +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>由于外部存储可能不可用—比如,当用户已将存储装载到电脑或已移除提供外部存储的 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/zh-cn/training/basics/data-storage/index.jd b/docs/html-intl/intl/zh-cn/training/basics/data-storage/index.jd new file mode 100644 index 0000000..6918227 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/basics/data-storage/index.jd @@ -0,0 +1,57 @@ +page.title=保存数据 +page.tags=数据存储、文件、SQL、数据库、首选项 +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 文件 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/zh-cn/training/basics/data-storage/shared-preferences.jd b/docs/html-intl/intl/zh-cn/training/basics/data-storage/shared-preferences.jd new file mode 100644 index 0000000..7a9d462 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/basics/data-storage/shared-preferences.jd @@ -0,0 +1,120 @@ +page.title=保存键值集 +page.tags=数据存储 +helpoutsWidget=true + +trainingnavtop=true + +@jd:body + + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>本课程将向您展示如何</h2> +<ol> + <li><a href="#GetSharedPreferences">获取共享首选项的句柄</a></li> + <li><a href="#WriteSharedPreference">写入共享首选项</a></li> + <li><a href="#ReadSharedPreference">从共享首选项读取信息</a></li> +</ol> + +<h2>您还应阅读</h2> +<ul> + <li><a href="{@docRoot}guide/topics/data/data-storage.html#pref">使用共享首选项</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.preference.Preference} API 混淆,后者帮助您为您的应用设置构建用户界面(尽管它们使用 {@link android.content.SharedPreferences} 作为其实现以保存应用设置)。 + + +有关使用 {@link +android.preference.Preference} API 的信息,请参阅<a href="{@docRoot}guide/topics/ui/settings.html">设置</a>指南。</p> + +<h2 id="GetSharedPreferences">获取共享首选项的句柄</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">写入共享首选项</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">从共享首选项读取信息</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/zh-cn/training/basics/intents/filters.jd b/docs/html-intl/intl/zh-cn/training/basics/intents/filters.jd new file mode 100644 index 0000000..45ce35a --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/basics/intents/filters.jd @@ -0,0 +1,236 @@ +page.title=允许其他应用开始您的活动 +page.tags=意向 +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/intent-filter-element.html">{@code <intent-filter>}</a> +在相应元素的宣示说明文件中添加一个 <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</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>如果您无需声明关于数据的具体信息 +{@link android.net.Uri}(比如,您的活动处理其他类型的“额外”数据而不是 URI 的时间),您应只指定 {@code android:mimeType} 属性声明您的活动处理的数据类型,比如 {@code text/plain} 或 {@code image/jpeg}。 + +</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/zh-cn/training/basics/intents/index.jd b/docs/html-intl/intl/zh-cn/training/basics/intents/index.jd new file mode 100644 index 0000000..c261939 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/basics/intents/index.jd @@ -0,0 +1,62 @@ +page.title=与其他应用交互 +page.tags=意向、活动 +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} 可以为 <em>显式</em> 以便启动特定组件(特定的 {@link android.app.Activity} 实例)或<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/zh-cn/training/basics/intents/result.jd b/docs/html-intl/intl/zh-cn/training/basics/intents/result.jd new file mode 100644 index 0000000..b340fea --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/basics/intents/result.jd @@ -0,0 +1,178 @@ +page.title=获取活动的结果 +page.tags=意向 +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} 的格式。当返回结果的活动是您自己的活动之一时,这便非常容易。 +Andriod 平台附带的应用提供它们自己的 API,您可用这些 API 获取特定结果数据。 +例如,“联系人” 应用(在一些较旧的版本中是 Contacts 应用)始终返回带内容 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/zh-cn/training/basics/intents/sending.jd b/docs/html-intl/intl/zh-cn/training/basics/intents/sending.jd new file mode 100644 index 0000000..22ebfce --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/basics/intents/sending.jd @@ -0,0 +1,256 @@ +page.title=向另一个应用发送用户 +page.tags=意向 +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()} 指定与意向关联的数据的类型。 + +设置 MIME 类型可进一步指定哪些类型的活动应接收意向。 +</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> + + + diff --git a/docs/html-intl/intl/zh-tw/training/basics/activity-lifecycle/index.jd b/docs/html-intl/intl/zh-tw/training/basics/activity-lifecycle/index.jd new file mode 100644 index 0000000..4225184 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/basics/activity-lifecycle/index.jd @@ -0,0 +1,72 @@ +page.title=管理應用行為顯示生命週期 +page.tags=應用行為顯示生命週期 +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/zh-tw/training/basics/activity-lifecycle/pausing.jd b/docs/html-intl/intl/zh-tw/training/basics/activity-lifecycle/pausing.jd new file mode 100644 index 0000000..8c0843d --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/basics/activity-lifecycle/pausing.jd @@ -0,0 +1,147 @@ +page.title=暫停並繼續應用行為顯示 +page.tags=應用行為顯示生命週期 +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>一般而言,您<strong>不</strong>應使用 {@link android.app.Activity#onPause()} 將使用者變更 (例如表單中輸入的個人資訊) 儲存至永久儲存空間。 +只有在您確信使用者希望自動儲存變更 (例如在起草電子郵件的情況下) 時,才應將使用者變更保留在 {@link android.app.Activity#onPause()} 中的永久儲存空間內。但是在 {@link +android.app.Activity#onPause()} 期間,您應避免執行耗用大量 CPU 資源的工作 (例如寫入至資料庫),因為這會降低向下一個應用行為顯示進行視覺轉換的速度 (您應改為在 {@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/zh-tw/training/basics/activity-lifecycle/recreating.jd b/docs/html-intl/intl/zh-tw/training/basics/activity-lifecycle/recreating.jd new file mode 100644 index 0000000..4b0efda --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/basics/activity-lifecycle/recreating.jd @@ -0,0 +1,178 @@ +page.title=重新建立應用行為顯示 +page.tags=應用行為顯示生命週期 +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 系統還原應用行為顯示中檢視的狀態, +<strong>每個檢視都必須具有唯一的 ID</strong> (由 <a href="{@docRoot}reference/android/view/View.html#attr_android:id">{@code +android:id}</a> 屬性提供)。 +</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} 執行個體時希望儲存的其他狀態資料。若已終結應用行為顯示,且必須重新建立該執行個體,系統會將 (1) 中定義的狀態資料傳遞至 {@link android.app.Activity#onCreate onCreate()} 方法 (2) 與 {@link android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} 方法 (3)。 + + + + + + +</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#onRestoreInstanceState onRestoreInstanceState()},系統會在呼叫 {@link android.app.Activity#onStart()} 方法後呼叫該項目。 + +只有存在要還原的已儲存狀態時,系統才會呼叫 {@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#onRestoreInstanceState onRestoreInstanceState()} 的超級類別實作,以便預設實作可還原檢視階層的狀態。 +</p> + +<p>若要詳細了解因執行階段中的重新啟動事件 (例如在旋轉螢幕時) 而重新建立應用行為顯示,請閱讀<a href="{@docRoot}guide/topics/resources/runtime-changes.html">處理執行階段變更</a>。 +</p> + diff --git a/docs/html-intl/intl/zh-tw/training/basics/activity-lifecycle/starting.jd b/docs/html-intl/intl/zh-tw/training/basics/activity-lifecycle/starting.jd new file mode 100644 index 0000000..3453ac5 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/basics/activity-lifecycle/starting.jd @@ -0,0 +1,285 @@ +page.title=啟動應用行為顯示 +page.tags=應用行為顯示的生命週期 +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>應用行為顯示生命週期的簡化圖例 (以階梯金字塔表示)。 +此圖例顯示,對於讓應用行為顯示向頂部的「已繼續」狀態邁上一個台階所用的每個回呼,都存在可讓應用行為顯示下移的回呼方法。 + +應用行為顯示也可以從「已暫停」及「已停止」狀態返回至「已繼續」狀態。 +</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>已繼續</dt> + <dd>若具備此狀態,應用行為顯示將位於前景中,使用者可以與應用行為顯示互動 (有時也稱為「執行中」狀態)。 +</dd> + <dt>已暫停</dt> + <dd>若具備此狀態,該應用行為顯示的一部分會遭其他應用行為顯示遮蓋,— +位於前景中的其他應用行為顯示處於半透明狀態,或未覆蓋整個螢幕。已暫停的應用行為顯示不會接收使用者輸入,也無法執行任何程式碼。 + + <dt>已停止</dt> + <dd>若具備此狀態,應用行為顯示將完全隱藏,使用者無法看到;會將該應用行為顯示視為位於背景中。 +停止後,會保留應用行為顯示執行個體及其所有狀態資訊 (例如成員變數),但其無法執行任何程式碼。 +</dd> +</dl> + +<p>其他狀態 (「已建立」與「已啟動」) 都是暫時狀態,系統透過呼叫下一個生命週期回呼方法,會從這些狀態快速移至下一個狀態。 +換言之,在系統呼叫 {@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>必須在宣示說明中使用 <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a> (包括 {@link +android.content.Intent#ACTION_MAIN MAIN} 行為與 {@link android.content.Intent#CATEGORY_LAUNCHER LAUNCHER} 類別) 宣告您應用程式的主要應用行為顯示。 +例如:</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 工具建立新 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>僅在 Android 2.0 (API 層級為 5) 及更高版本中,可以透過此方式使用 {@link android.os.Build.VERSION#SDK_INT} 防止舊版系統執行新 API。 + +在舊版系統中,將發生執行階段例外狀況。</p> + +<p>{@link android.app.Activity#onCreate onCreate()} 執行完成後,系統會連續快速呼叫 {@link android.app.Activity#onStart()} 與 {@link android.app.Activity#onResume()} 方法。 + +您的應用行為顯示永遠不會具備「已建立」或「已啟動」狀態。嚴格來說,在呼叫 {@link android.app.Activity#onStart()} 時,使用者可以看到應用行為顯示,但隨後會快速呼叫 {@link android.app.Activity#onResume()},應用行為顯示將保持「已繼續」狀態,直到發生某些狀況 (例如接聽電話、使用者導覽至其他應用行為顯示,或裝置螢幕關閉) 變更該狀態為止。 + + + +</p> + +<p>在後續的其他課程中,您將了解在應用行為顯示的生命週期期間,其他啟動方法 ({@link +android.app.Activity#onStart()} 與 {@link android.app.Activity#onResume()}) 在用於從「已暫停」或「已停止」狀態繼續執行應用行為顯示時如何提供助益。 +</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()}。 + +完成這一系列的回呼後,應用行為顯示將具備「已繼續」狀態,此時使用者可以與應用行為顯示互動,直到使用者切換至其他應用行為顯示為止。 + +</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#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#onCreate onCreate()} 中呼叫 {@link android.app.Activity#finish()} 以終結應用行為顯示。 +若是如此,系統會立即呼叫 {@link android.app.Activity#onDestroy},而不會呼叫其他任何生命週期方法。 + +</p> diff --git a/docs/html-intl/intl/zh-tw/training/basics/activity-lifecycle/stopping.jd b/docs/html-intl/intl/zh-tw/training/basics/activity-lifecycle/stopping.jd new file mode 100644 index 0000000..a2da5ca --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/basics/activity-lifecycle/stopping.jd @@ -0,0 +1,187 @@ +page.title=停止並重新啟動應用行為顯示 +page.tags=應用行為顯示生命週期 +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#onPause onPause()},然後呼叫 {@link +android.app.Activity#onStop onStop()}。 +</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#onPause onPause()} 方法,然後呼叫 {@link android.app.Activity#onStop()},但是您應使用 {@link android.app.Activity#onStop onStop()} 執行規模更大、耗用 CPU 資源更多的關閉作業,諸如將資訊寫入至資料庫。 + + +</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} (索引鍵值配對的二進位大型物件) 中保留 {@link android.view.View} 物件 (例如 {@link +android.widget.EditText} 中的文字) 的狀態,若使用者重新導覽至應用行為顯示的同一執行個體,會還原這些物件 (<a href="recreating.html">下一課</a>將更詳細地討論在終結並重新建立您的應用行為顯示時使用 {@link android.os.Bundle} 儲存其他狀態資料)。 + + +</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#onStop()} 釋放大多數資源,因此在收到對 {@link +android.app.Activity#onDestroy()} 的呼叫之前,大多數應用程式幾乎都不需要執行操作。 +此方法將為您提供對可能導致記憶體流失的資源執行清理的最後機會,因此您應確保已終結其他執行緒,並確保已停止長時間執行的其他行為 (例如方法追蹤)。 + + +</p> + diff --git a/docs/html-intl/intl/zh-tw/training/basics/data-storage/databases.jd b/docs/html-intl/intl/zh-tw/training/basics/data-storage/databases.jd new file mode 100644 index 0000000..f3706f1 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/basics/data-storage/databases.jd @@ -0,0 +1,317 @@ +page.title=在 SQL 資料庫中儲存資料 +page.tags=資料儲存空間 +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 資料庫。 + +{@link android.database.sqlite} 套件中提供在 Android 上使用資料庫所需的 API。 +</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 類別 (諸如 cursor adaptor) 希望其具備該欄位。 + +此操作並非必需的操作,但是可協助您的資料庫與 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>{@link +android.database.sqlite.SQLiteOpenHelper} 類別中提供一組有用的 API。若使用此類別取得資料庫的參考,只有在需要執行且<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} move 方法,您必須始終先呼叫該方法,然後再開始讀取值。 +一般而言,您應呼叫 {@link android.database.Cursor#moveToFirst} 來執行啟動,如此會將「讀取位置」置於結果中的第一個項目。 + +對於每列,您可以呼叫其中一種 {@link android.database.Cursor} get 方法 (例如 {@link android.database.Cursor#getString +getString()} 或 {@link android.database.Cursor#getLong getLong()}),以讀取欄的值。 +對於每種 get 方法,您必須傳遞所需欄的索引位置,可以呼叫 {@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/zh-tw/training/basics/data-storage/files.jd b/docs/html-intl/intl/zh-tw/training/basics/data-storage/files.jd new file mode 100644 index 0000000..8b8d0a7 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/basics/data-storage/files.jd @@ -0,0 +1,379 @@ +page.title=儲存檔案 +page.tags=資料儲存空間 +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 發展的初期,當時大多數裝置都提供內建靜態記憶體 (內部儲存空間),以及諸如 micro 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()} 用於您應用程式私用的檔案,請務必使用由 API 常數 (例如 {@link android.os.Environment#DIRECTORY_PICTURES}) 提供的目錄名稱。 + + +這些目錄名稱可確保系統正確處理檔案。 +例如,系統媒體掃描程式會將 {@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} 所示的位元組數量。 +若傳回的數值較您要儲存的資料量略大,或檔案系統的已使用空間不到 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#deleteFile deleteFile()},讓 {@link android.content.Context} 尋找並刪除檔案: +</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/zh-tw/training/basics/data-storage/index.jd b/docs/html-intl/intl/zh-tw/training/basics/data-storage/index.jd new file mode 100644 index 0000000..9ff1fc0 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/basics/data-storage/index.jd @@ -0,0 +1,57 @@ +page.title=儲存資料 +page.tags=資料儲存空間,檔案,sql,資料庫,偏好設定 +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 檔案 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>在 {@link android.app.Activity#onPause onPause()} 期間,大多數 Android 應用程式都需要儲存資料 (即使僅儲存應用程式狀態的有關資訊),因此使用者的進度不會遺失。 +大多數非一般的應用程式還需要儲存使用者設定,某些應用程式必須管理檔案與資料庫中的大量資訊。 + +本課程將為您介紹 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/zh-tw/training/basics/data-storage/shared-preferences.jd b/docs/html-intl/intl/zh-tw/training/basics/data-storage/shared-preferences.jd new file mode 100644 index 0000000..46f8f20 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/basics/data-storage/shared-preferences.jd @@ -0,0 +1,120 @@ +page.title=儲存索引鍵值組 +page.tags=資料儲存空間 +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">寫入至共用的偏好設定</a></li> + <li><a href="#ReadSharedPreference">從共用的偏好設定進行讀取</a></li> +</ol> + +<h2>您也應該閱讀</h2> +<ul> + <li><a href="{@docRoot}guide/topics/data/data-storage.html#pref">使用共用的偏好設定</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.preference.Preference} API 混淆,後者可協助您建置應用程式設定的使用者介面 (然而這些 API 使用 {@link android.content.SharedPreferences} 作為其儲存應用程式設定的實作方式)。 + + + +如需有關使用 {@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">寫入至共用的偏好設定</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">從共用的偏好設定進行讀取</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/zh-tw/training/basics/intents/filters.jd b/docs/html-intl/intl/zh-tw/training/basics/intents/filters.jd new file mode 100644 index 0000000..2088b27 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/basics/intents/filters.jd @@ -0,0 +1,236 @@ +page.title=允許其他應用程式啟動您的應用行為顯示 +page.tags=意圖 +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>若您不需要宣告 {@link android.net.Uri} 資料的有關詳情 (例如若您的應用行為顯示處理其他類型的「額外」資料,而非處理 URI),應僅指定 {@code android:mimeType} 屬性以宣告應用行為顯示處理的資料類型,例如 {@code text/plain} 或 {@code image/jpeg}。 + + +</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/zh-tw/training/basics/intents/index.jd b/docs/html-intl/intl/zh-tw/training/basics/intents/index.jd new file mode 100644 index 0000000..70492f4 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/basics/intents/index.jd @@ -0,0 +1,62 @@ +page.title=與其他應用程式互動 +page.tags=意圖,應用行為的顯示 +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} 可以是<em>明確的</em>,以啟動特定元件 +(特定 {@link android.app.Activity} 執行個體),也可以是<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/zh-tw/training/basics/intents/result.jd b/docs/html-intl/intl/zh-tw/training/basics/intents/result.jd new file mode 100644 index 0000000..9fabe91 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/basics/intents/result.jd @@ -0,0 +1,178 @@ +page.title=從應用行為顯示取得結果 +page.tags=意圖 +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.Manifest.permission#READ_CONTACTS} 權限,否則無法查詢並非由意圖的 {@link android.net.Uri} 指定的連絡人。 + +</p> + + + + + + + + + + + + + + + diff --git a/docs/html-intl/intl/zh-tw/training/basics/intents/sending.jd b/docs/html-intl/intl/zh-tw/training/basics/intents/sending.jd new file mode 100644 index 0000000..70e8316 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/basics/intents/sending.jd @@ -0,0 +1,256 @@ +page.title=將使用者傳送至其他應用程式 +page.tags=意圖 +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()} 指定意圖所關聯資料的類型。 + +設定 MIME 類型會進一步指定哪些類型的應用行為顯示應接收意圖。 +</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>針對行事曆事件的這一意圖僅支援至少為 14 的 API 級別。 +</p> + </li> +</ul> + +<p class="note"><strong>注意:</strong>請儘可能具體地定義 {@link +android.content.Intent},這一點非常重要。例如,若您希望使用 {@link android.content.Intent#ACTION_VIEW} 意圖顯示影像,應將 MIME 類型指定為 {@code image/*}。 + +如此可防止意圖觸發能「檢視」其他類型資料的應用程式 (例如地圖應用程式)。 +</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> + + + diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs index e1e6838..5279969 100644 --- a/docs/html/training/training_toc.cs +++ b/docs/html/training/training_toc.cs @@ -91,25 +91,60 @@ include the action bar on devices running Android 2.1 or higher." <li class="nav-section"> <div class="nav-section-header"> <a href="<?cs var:toroot ?>training/basics/activity-lifecycle/index.html" + es-lang="" + ja-lang="アクティビティのライフサイクル 管理" + ko-lang="액티비티 수명 주기 관리하기" + pt-br-lang="Como gerenciar o ciclo de vida da atividade" + ru-lang="Управление жизненным циклом операций" + zh-cn-lang="管理活动生命周期" + zh-tw-lang="管理應用行為顯示生命週期" description= "How Android activities live and die and how to create a seamless user experience by implementing lifecycle callback methods." >Managing the Activity Lifecycle</a> </div> <ul> - <li><a href="<?cs var:toroot ?>training/basics/activity-lifecycle/starting.html"> + <li><a href="<?cs var:toroot ?>training/basics/activity-lifecycle/starting.html" + es-lang="" + ja-lang="アクティビティを開始する" + ko-lang="액티비티 시작하기" + pt-br-lang="Iniciando uma atividade" + ru-lang="Запуск операции" + zh-cn-lang="开始活动" + zh-tw-lang="啟動應用行為顯示"> Starting an Activity </a> </li> - <li><a href="<?cs var:toroot ?>training/basics/activity-lifecycle/pausing.html"> + <li><a href="<?cs var:toroot ?>training/basics/activity-lifecycle/pausing.html" + es-lang="" + ja-lang="" + ko-lang="" + pt-br-lang="" + ru-lang="" + zh-cn-lang="" + zh-tw-lang=""> Pausing and Resuming an Activity </a> </li> - <li><a href="<?cs var:toroot ?>training/basics/activity-lifecycle/stopping.html"> + <li><a href="<?cs var:toroot ?>training/basics/activity-lifecycle/stopping.html" + es-lang="" + ja-lang="アクティビティの一時停止と再開" + ko-lang="액티비티 일시정지 및 재개하기" + pt-br-lang="Pausando e reiniciando uma atividade" + ru-lang="Приостановка и возобновление операции" + zh-cn-lang="暂停和继续活动" + zh-tw-lang="暫停並繼續應用行為顯示"> Stopping and Restarting an Activity </a> </li> - <li><a href="<?cs var:toroot ?>training/basics/activity-lifecycle/recreating.html"> + <li><a href="<?cs var:toroot ?>training/basics/activity-lifecycle/recreating.html" + es-lang="" + ja-lang="アクティビティを再作成する" + ko-lang="액티비티 재생성하기" + pt-br-lang="Recriando uma atividade" + ru-lang="Воссоздание операции" + zh-cn-lang="重新创建活动" + zh-tw-lang="重新建立應用行為顯示"> Recreating an Activity </a> </li> @@ -144,21 +179,49 @@ include the action bar on devices running Android 2.1 or higher." <li class="nav-section"> <div class="nav-section-header"><a href="<?cs var:toroot?>training/basics/data-storage/index.html" + es-lang="" + ja-lang="データの保存" + ko-lang="데이터 저장하기" + pt-br-lang="Salvando dados" + ru-lang="Сохранение данных" + zh-cn-lang="保存数据" + zh-tw-lang="儲存資料" description= "How to save data on the device, whether it's temporary files, downloaded app assets, user media, structured data, or something else." >Saving Data</a> </div> <ul> - <li><a href="<?cs var:toroot ?>training/basics/data-storage/shared-preferences.html"> + <li><a href="<?cs var:toroot ?>training/basics/data-storage/shared-preferences.html" + es-lang="" + ja-lang="キー値セットを保存する" + ko-lang="키-값 세트 저장하기" + pt-br-lang="Salvando conjuntos de valor-chave" + ru-lang="Сохранение наборов "\"ключ-значение\"" + zh-cn-lang="保存键值集" + zh-tw-lang="儲存索引鍵值組"> Saving Key-Value Sets </a> </li> - <li><a href="<?cs var:toroot ?>training/basics/data-storage/files.html"> + <li><a href="<?cs var:toroot ?>training/basics/data-storage/files.html" + es-lang="" + ja-lang="ファイルを保存する" + ko-lang="파일 저장하기" + pt-br-lang="Salvando arquivos" + ru-lang="Сохранение файлов" + zh-cn-lang="保存文件" + zh-tw-lang="儲存檔案"> Saving Files </a> </li> - <li><a href="<?cs var:toroot ?>training/basics/data-storage/databases.html"> + <li><a href="<?cs var:toroot ?>training/basics/data-storage/databases.html" + es-lang="" + ja-lang="SQL データベースにデータを保存する" + ko-lang="SQL 데이터베이스에 데이터 저장하기" + pt-br-lang="Salvando dados em bancos de dados do SQL" + ru-lang="Сохранение данных в базах данных SQL" + zh-cn-lang="在 SQL 数据库中保存数据" + zh-tw-lang="在 SQL 資料庫中儲存資料"> Saving Data in SQL Databases </a> </li> @@ -168,6 +231,13 @@ include the action bar on devices running Android 2.1 or higher." <li class="nav-section"> <div class="nav-section-header"> <a href="<?cs var:toroot ?>training/basics/intents/index.html" + es-lang="" + ja-lang="他のアプリとの相互操作" + ko-lang="액티비티 수명 주기 관리하기" + pt-br-lang="Interagindo com outros aplicativos" + ru-lang="Взаимодействие с другими приложениями" + zh-cn-lang="与其他应用交互" + zh-tw-lang="與其他應用程式互動" description= "How to build a user experience that leverages other apps available on the device to perform advanced user tasks, such as capture a photo or view @@ -175,15 +245,36 @@ include the action bar on devices running Android 2.1 or higher." >Interacting with Other Apps</a> </div> <ul> - <li><a href="<?cs var:toroot ?>training/basics/intents/sending.html"> + <li><a href="<?cs var:toroot ?>training/basics/intents/sending.html" + es-lang="" + ja-lang="別のアプリにユーザーを送る" + ko-lang="다른 앱으로 사용자 보내기" + pt-br-lang="Enviando o usuário para outro aplicativo" + ru-lang="Направление пользователя в другое приложение" + zh-cn-lang="向另一个应用发送用户" + zh-tw-lang="將使用者傳送至其他應用程式"> Sending the User to Another App </a> </li> - <li><a href="<?cs var:toroot ?>training/basics/intents/result.html"> + <li><a href="<?cs var:toroot ?>training/basics/intents/result.html" + es-lang="" + ja-lang="アクティビティから結果を取得する" + ko-lang="액티비티로부터 결과 가져오기" + pt-br-lang="Obtendo resultados de uma atividade" + ru-lang="Получение результата операции" + zh-cn-lang="获取活动的结果" + zh-tw-lang="從應用行為顯示取得結果"> Getting a Result from the Activity </a> </li> - <li><a href="<?cs var:toroot ?>training/basics/intents/filters.html"> + <li><a href="<?cs var:toroot ?>training/basics/intents/filters.html" + es-lang="" + ja-lang="他のアプリからのアクティビティの開始を許可する" + ko-lang="다른 앱이 자신의 액티비티를 시작하도록 허용하기" + pt-br-lang="Permitindo que outros aplicativos iniciem sua atividade" + ru-lang="Разрешение другим приложениям на запуск вашей операции" + zh-cn-lang="允许其他应用开始您的活动" + zh-tw-lang="允許其他應用程式啟動您的應用行為顯示"> Allowing Other Apps to Start Your Activity </a> </li> |