diff options
Diffstat (limited to 'docs/html-intl/intl/zh-cn/training/basics')
13 files changed, 2474 insertions, 0 deletions
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> + + + |