Activity学习
简介:
Activity是一个应用组件,用户可与其提供的屏幕进行交互,以执行拨打电话、拍摄照片、发送电子邮件等操作。每个Activity都会获得一个用于绘制其用户界面的窗口。窗口通常会充满屏幕,但也可小于屏幕并浮动子其他窗口之上。
一个应用通常由多个彼此松散联系的Activity组成。一般会指定应用中的某个Activity为“主”Activity,即应用首次启动时呈现给用户的那个Activity。而且每个Activity均可启动另外的Activity,以便执行不同的操作。每次新的Activity启动时,前一Activity便会停止,但系统会在堆栈(“返回栈”)中保留该Activity。当新的Activity启动时,系统会将其推送到返回栈中,并获得用户焦点。返回栈,遵循“后进先出”的机制,因此当用户完成当前Activity并按下返回按钮时,系统会从堆栈中将其弹出(并销毁),恢复前一Activity。
当一个Activity因某一个新的Activity启动而停止时,系统通过该Activity的声明周期回调方法通知其这一状态变化。Activity因状态变化-系统是创建Activity、停止Activity、恢复Activity还是销毁Activity-收到的回调方法可能有若干种,每一种回调方法都会为你提供执行与该状态变化相应的特定操作的机会。
创建 Activity
要创建 Activity,您必须创建Activity的子类(或使用其现有子类)。您需要在子类中实现 Activity 在其生命周期的各种状态之间转变时(例如创建 Activity、停止 Activity、恢复 Activity 或销毁 Activity 时)系统调用的回调方法。 两个最重要的回调方法是:
您必须实现此方法。系统会在创建您的 Activity 时调用此方法。您应该在实现内初始化 Activity 的必需组件。 最重要的是,您必须在此方法内调用setContentView(),以定义 Activity 用户界面的布局。
系统将此方法作为用户离开 Activity 的第一个信号(但并不总是意味着 Activity 会被销毁)进行调用。 您通常应该在此方法内确认在当前用户会话结束后仍然有效的任何更改(因为用户可能不会返回)。
您还应使用几种其他生命周期回调方法,以便提供流畅的 Activity 间用户体验,以及处理导致您的 Activity 停止甚至被销毁的意外中断。 后文的管理 Activity 生命周期部分对所有生命周期回调方法进行了阐述。
实现用户界面
Activity 的用户界面是由层级式视图—衍生自View类的对象—提供的。每个视图都控制 Activity 窗口内的特定矩形空间,可对用户交互作出响应。 例如,视图可以是在用户触摸时启动某项操作的按钮。
您可以利用 Android 提供的许多现成视图设计和组织您的布局。“小工具”是提供按钮、文本字段、复选框或仅仅是一幅图像等屏幕视觉(交互式)元素的视图。 “布局”是衍生自ViewGroup的视图,为其子视图提供唯一布局模型,例如线性布局、网格布局或相对布局。您还可以为View类和ViewGroup类创建子类(或使用其现有子类)来自行创建小工具和布局,然后将它们应用于您的 Activity 布局。
利用视图定义布局的最常见方法是借助保存在您的应用资源内的 XML 布局文件。这样一来,您就可以将用户界面的设计与定义 Activity 行为的源代码分开维护。 您可以通过setContentView()将布局设置为 Activity 的 UI,从而传递布局的资源 ID。不过,您也可以在 Activity 代码中创建新View,并通过将新View插入ViewGroup来创建视图层次,然后通过将根ViewGroup传递到setContentView()来使用该布局。
如需了解有关创建用户界面的信息,请参阅用户界面文档。
在清单文件中声明 Activity
您必须在清单文件中声明您的 Activity,这样系统才能访问它。 要声明您的 Activity,请打开您的清单文件,并将<activity>元素添加为<application>元素的子项。例如:
...
...
您还可以在此元素中加入几个其他特性,以定义 Activity 标签、Activity 图标或风格主题等用于设置 Activity UI 风格的属性。android:name特性是唯一的必需特性—它指定 Activity 的类名。应用一旦发布,即不应更改此类名,否则,可能会破坏诸如应用快捷方式等一些功能(请阅读博客文章Things That Cannot Change[不能更改的内容])。
在清单文件中声明 Activity
您必须在清单文件中声明您的 Activity,这样系统才能访问它。 要声明您的 Activity,请打开您的清单文件,并将<activity>元素添加为<application>元素的子项。例如:
...
...
使用 Intent 过滤器
<activity>元素还可指定各种 Intent 过滤器—使用<Intent-filter>元素—以声明其他应用组件激活它的方法。
当您使用 Android SDK 工具创建新应用时,系统自动为您创建的存根 Activity 包含一个 Intent 过滤器,其中声明了该 Activity 响应“主”操作且应置于“launcher”类别内。 Intent 过滤器的内容与以下所示类似:
<action>元素指定这是应用的“主”入口点。<category>元素指定此 Activity 应列入系统的应用启动器内(以便用户启动该 Activity)。
如果您打算让应用成为独立应用,不允许其他应用激活其 Activity,则您不需要任何其他 Intent 过滤器。 正如前例所示,只应有一个 Activity 具有“主”操作和“launcher”类别。 您不想提供给其他应用的 Activity 不应有任何 Intent 过滤器,您可以利用显式 Intent 自行启动它们
启动 Activity
您可以通过调用startActivity(),并将其传递给描述您想启动的 Activity 的Intent来启动另一个 Activity。Intent 对象会指定您想启动的具体 Activity 或描述您想执行的操作类型(系统会为您选择合适的 Activity,甚至是来自其他应用的 Activity)。 Intent 对象还可能携带少量供所启动 Activity 使用的数据。
在您的自有应用内工作时,您经常只需要启动某个已知 Activity。 您可以通过使用类名创建一个显式定义您想启动的 Activity 的 Intent 对象来实现此目的。 例如,可以通过以下代码让一个 Activity 启动另一个名为SignInActivity的 Activity:
Intentintent=newIntent(this,SignInActivity.class);
startActivity(intent);
不过,您的应用可能还需要利用您的 Activity 数据执行某项操作,例如发送电子邮件、短信或状态更新。 在这种情况下,您的应用自身可能不具有执行此类操作所需的 Activity,因此您可以改为利用设备上其他应用提供的 Activity 为您执行这些操作。 这便是 Intent 对象的真正价值所在—您可以创建一个 Intent 对象,对您想执行的操作进行描述,系统会从其他应用启动相应的 Activity。 如果有多个 Activity 可以处理 Intent,则用户可以选择要使用哪一个。 例如,如果您想允许用户发送电子邮件,可以创建以下 Intent 对象:
Intentintent=newIntent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL,recipientArray);
startActivity(intent);
添加到 Intent 中的EXTRA_EMAILextra 是一个字符串数组,其中包含应将电子邮件发送到的电子邮件地址。 当电子邮件应用响应此 Intent 时,它会读取 extra 中提供的字符串数组,并将它们放入电子邮件撰写窗体的“收件人”字段。 在这种情况下,电子邮件应用的 Activity 启动,并且当用户完成操作时,您的 Activity 会恢复执行。
结束 Activity
您可以通过调用 Activity 的finish()方法来结束该 Activity。您还可以通过调用finishActivity()结束您之前启动的另一个 Activity。
注:在大多数情况下,您不应使用这些方法显式结束 Activity。 正如下文有关 Activity 生命周期的部分所述,Android 系统会为您管理 Activity 的生命周期,因此您无需完成自己的 Activity。 调用这些方法可能对预期的用户体验产生不良影响,因此只应在您确实不想让用户返回此 Activity 实例时使用。
管理 Activity 生命周期
通过实现回调方法管理 Activity 的生命周期对开发强大而又灵活的应用至关重要。 Activity 的生命周期会直接受到 Activity 与其他 Activity、其任务及返回栈的关联性的影响。
Activity 基本上以三种状态存在:
已继续
此 Activity 位于屏幕前台并具有用户焦点。(有时也将此状态称作“运行中”。)
已暂停
另一个 Activity 位于屏幕前台并具有用户焦点,但此 Activity 仍可见。也就是说,另一个 Activity 显示在此 Activity 上方,并且该 Activity 部分透明或未覆盖整个屏幕。 已暂停的 Activity 处于完全 Activity 状态(Activity对象保留在内存中,它保留了所有状态和成员信息,并与窗口管理器保持连接),但在内存极度不足的情况下,可能会被系统终止。
已停止
该 Activity 被另一个 Activity 完全遮盖(该 Activity 目前位于“后台”)。 已停止的 Activity 同样仍处于 Activity 状态(Activity对象保留在内存中,它保留了所有状态和成员信息,但未与窗口管理器连接)。 不过,它对用户不再可见,在他处需要内存时可能会被系统终止。
如果 Activity 处于暂停或停止状态,系统可通过要求其结束(调用其finish()方法)或直接终止其进程,将其从内存中删除。(将其结束或终止后)再次打开 Activity 时,必须重建。
图 1 说明了这些循环以及 Activity 在状态转变期间可能经过的路径。矩形表示回调方法,当 Activity 在不同状态之间转变时,您可以实现这些方法来执行操作。
pic1 保存 Activity 状态管理 Activity 生命周期的引言部分简要提及,当 Activity 暂停或停止时,Activity 的状态会得到保留。 确实如此,因为当 Activity 暂停或停止时,Activity对象仍保留在内存中 — 有关其成员和当前状态的所有信息仍处于 Activity 状态。 因此,用户在 Activity 内所做的任何更改都会得到保留,这样一来,当 Activity 返回前台(当它“继续”)时,这些更改仍然存在。
不过,当系统为了恢复内存而销毁某项 Activity 时,Activity对象也会被销毁,因此系统在继续 Activity 时根本无法让其状态保持完好,而是必须在用户返回Activity时重建Activity对象。但用户并不知道系统销毁 Activity 后又对其进行了重建,因此他们很可能认为 Activity 状态毫无变化。 在这种情况下,您可以实现另一个回调方法对有关 Activity 状态的信息进行保存,以确保有关 Activity 状态的重要信息得到保留:onSaveInstanceState()。
系统会先调用onSaveInstanceState(),然后再使 Activity 变得易于销毁。系统会向该方法传递一个Bundle,您可以在其中使用putString()和putInt()等方法以名称-值对形式保存有关 Activity 状态的信息。然后,如果系统终止您的应用进程,并且用户返回您的 Activity,则系统会重建该 Activity,并将Bundle同时传递给onCreate()和onRestoreInstanceState()。您可以使用上述任一方法从Bundle提取您保存的状态并恢复该 Activity 状态。如果没有状态信息需要恢复,则传递给您的Bundle是空值(如果是首次创建该 Activity,就会出现这种情况)。
pic2图 2.在两种情况下,Activity 重获用户焦点时可保持状态完好:系统在销毁 Activity 后重建 Activity,Activity 必须恢复之前保存的状态;系统停止 Activity 后继续执行 Activity,并且 Activity 状态保持完好。
注:无法保证系统会在销毁您的 Activity 前调用onSaveInstanceState(),因为存在不需要保存状态的情况(例如用户使用“返回”按钮离开您的 Activity 时,因为用户的行为是在显式关闭 Activity)。 如果系统调用onSaveInstanceState(),它会在调用onStop()之前,并且可能会在调用onPause()之前进行调用。
不过,即使您什么都不做,也不实现onSaveInstanceState(),Activity类的onSaveInstanceState()默认实现也会恢复部分 Activity 状态。具体地讲,默认实现会为布局中的每个View调用相应的onSaveInstanceState()方法,让每个视图都能提供有关自身的应保存信息。Android 框架中几乎每个小工具都会根据需要实现此方法,以便在重建 Activity 时自动保存和恢复对 UI 所做的任何可见更改。例如,EditText小工具保存用户输入的任何文本,CheckBox小工具保存复选框的选中或未选中状态。您只需为想要保存其状态的每个小工具提供一个唯一的 ID(通过android:id属性)。如果小工具没有 ID,则系统无法保存其状态。