活动(Activity)
一个Activity
是一个应用程序组件,它提供了一个屏幕,与用户交互,用户可以通过它做一些事情。如拨打电话,拍照,发送电子邮件或查看地图。每个活动都有一个窗口,在其中绘制它的用户界面。窗口通常充满屏幕,也可以是小于屏幕和浮于其他窗口之上。
一个应用程序通常包括多个彼此松散结合在一起的Activity。通常情况下,在应用程序中一个Activity被指定为“主”的Activity,这在首次启动应用程序时会呈现给用户。然后每个Activity都可以启动另一个Activity,这是为了执行不同的actions。每当一个新的Activity启动时,先前的Activity就会停止,但系统保留了preserves 的栈(the "back stack")。当一个新的Activity启动时,它被压入栈,并获得用户关注的焦点。栈遵守基本的“后进先出”的堆栈机制,所以,当用户完成当前的Activity,并按下返回按钮,它从堆栈中弹出(并销毁)和之前的Activity恢复。(堆栈更多讨论在任务和返回堆栈文档中。)
当Activity停止,因为一个新的Activity启动,它将被告知这种状态的变化,通过Activity的生命周期回调方法。一个Activity可能会收到几种回调方法,由于它的状态变化—系统是否正在创造它,停止它,恢复它,或者销毁它—并且每个回调方法为你提供的机会,以执行特定的工作,适应这种状态的变化。例如,在停止时,你的Activity应当释放任何大的对象,如网络或数据库连接。当Activity恢复,则可以重新获取必要的资源,并恢复了被打断的动作。这些状态转换是活动的生命周期的所有部分。
本文的其余部分讨论了如何建立和使用Activity,包括关于Activity生命周期是如何工作的完整讨论,这样你就可以适当地管理各种Activity状态之间的转换。
创建活动(Create an Activity)
要创建一个Activity,你必须创建一个 Activity的
子类(或它的一个现有的子类)。在你的子类中,你需要实现回调方法,系统调用会调用它们,当Activity在生命周期不同状态下转换时,如停止,恢复或销毁。两个最重要的回调方法是:
-
您必须实现此方法。创建Activity时,系统会调用这个。在你实现的回调方法中,你应该初始化Activity的重要组成部分。最重要的是,在理你必须调用
setContentView(),
为了定义Activity的用户界面的布局。 - 系统回调用该函数作为用户离开Activity的第一个预兆, 在当前用户会话结束之前,通常要在这里提交任何应该持久化的变化(因为用户可能不再返回)。
-
- 你应该使用其他一些生命周期回调方法来提供流畅的用户体验,因为Activity之间,处理意外中断,都会导致你的Activity停止,甚至销毁。所有的生命周期回调方法将在后面讨论,在 管理Activity的生命周期章节 。
onCreate()
onPause()
实现用户界面(Implementing a user interface)
一个Activity的用户界面通过视图对象的层次提供,这个视图对象来自于 View类
。每个视图控制Activity窗口中一个特定的矩形空间,并可以响应用户交互行为。例如,一个视图可以是一个按钮,当用户触摸它时,发起一个动作。
Android提供了一些你可以用它来 设计和组织布局现成的视图(view)。“部件”是视图,为屏幕提供可视(和交互)元素,诸如按钮,文本字段,复选框,或者仅仅一个图片。“布局”是衍生自ViewGroup,
提供了对子视图特殊的布局模式,诸如一个线性布局,网格布局,或相对布局。你也可以继承 View
和
ViewGroup
类(或现有的子类)来创建自己的部件和布局,并将它们应用到你的Activity布局。
使用view定义一个布局最常用方法是在你的应用程序的资源文件中定义XML布局。通过这种方式,可以从定义Activity行为的源代码中分离出来单独维护你的用户界面的设计。为你的Activity·,你可以用setContentView()设置布局作为你Activity的UI,同时要把资源文件的ID传给setContentView()。但是,您也可以创建新的view,
在你
的Activity代码中,并且通过插入新的view到ViewGroup中来构建一个视图层次,然后通过传递根视图组(ViewGroup)给setContentView()来使用这个布局。
有关创建用户界面的信息,请参阅用户界面(User Interface )文档。
在清单文件中声明Activity(Declaring the activity in the manifest)
你必须声明在你的manifest文件中声明Activity,以便它可以被系统访问到。要声明你的Activity,打开你的清单文件,并添加一个
元素,作为一个 <activity>
<application>元素的子
元素。例如:
<manifest ... >
<application ... >
<activity android:name = ".ExampleActivity" />
...
</application ... >
...
</manifest >
还有其他几个属性,你可以包含这个元素中,包括定义属性,Activity的标签,Activity的图标,或者Activity的主题样式。android:name
属性是唯一必需的属性,它指定Activity的类名。一旦你发布你的应用程序,你不能更改这个名称,因为如果你这样做,你可能会破坏一些功能,如应用程序快捷方式(阅读博客文章,Things That Cannot Change)。
有关在manifest文件声明Activity的更多信息请查阅 <activity>
元素
使用意图过滤器(Using intent filters)
一个
元素还可以指定不同的意图过滤器—使用<activity>
元素,以告知其他应用程序组件如何激活它。<intent-filter>
当您使用Android SDK工具创建新的应用程序时,<activity>标签下自动包括用于声明Activity响应“main”的动作的intent过滤器和用于启动的“lancher”分类。intent 过滤器看起来像这样:
<activity android:name = ".ExampleActivity" android:icon = "@drawable/app_icon" >
<intent-filter>
<action android:name = "android.intent.action.MAIN" />
<category android:name = "android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
元素指定,这是应用程序“main”入口点。<action>
元素指定,这个activity将被列入系统的应用程序启动器(允许用户启动此activity)。<category>
如果您打算让您的应用程序是自包含的,而不是允许其他应用程序来激活它的activity,那么你不需要任何意图过滤器。只要一个activity应该具有“main”动作和“启动器”类别,如前面的例子中。你不想让其他应用程序激活它activity,它应该没有意图过滤器,您可以使用显式意图自 己启动它们(如下面的章节中讨论)。
但是,如果你想你的activity响应来自于其他应用程序(和你自己)隐式意图,那么你必须为你的activity定义额外的意图过滤器。对于每种您要响应的意图类型,你必须包括
,其中包括一个 <intent-filter>
<action>
元素和一个可选的
<category>
的元素和/或
元素。这些元素指定你的Activity能响应的意图的类型。<data>
有关您的活动如何能够响应意图的更多信息,请参阅Intents and Intent Filters的文档。
启动一个Activity(Starting an Activity)
你可以通过调用startActivity()启动另一个activity,给它传递一个描述你想要启动这个activity的意图
。这个意图要么明确的指定你要启动的activity,要么描述你要执行的动作的类型(系统会为你选择合适的activity,它可能来自于其他不同的应用程序)。activity被启动时,intent 也可以携带一些有用的数据。
当在自己的应用程序中工作时,你会经常需要简单地启动一个已知的activity。你可以这佯作,通过创建一个意图,使用类名显示的定义你想要启动的activity。例如,这里是一个activity如何启动另一个名为SignInActivity
的activity
:
Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);
然而,你的应用程序也可能想要执行一些操作,比如发送电子邮件,短信或状态更新,使用你的activity的数据。在这种情况下,你的应用程序可能没有自己的activity来执行这样的操作,所以你可以转而利用设备上其他应用程序提供的Activity为您执行。这是意图是真正有价值的,你可以创建一个意图,描述要执行的操作,并且系统从另一个应用程序启动相应的Activity。如果有多个Activity,可处理这个意图,则用户可以选择要使用哪一个。例如,如果你想让用户发送电子邮件,您可以创建以下意图:
Intent intent = new Intent ( Intent . ACTION_SEND );
intent . putExtra ( Intent . EXTRA_EMAIL , recipientArray );
startActivity ( intent );
被
添加到意图中的EXTRA_EMAIL是邮件收件人的字符串数组。当电子邮件应用程序响应这个意图它读取额外提供的字符串数组,并把它们放在程序的“to”地址栏。在这种情况下,应用程序启动电子邮件Activity,当用户完成后,恢复到你的Activity。
启动一个Activity并返回一个结果(Starting an activity for a result)
有时候,你可能需要从你启动的activity上获得的结果。在这种情况下,启动通过调用startActivityForResult()
(而不是startActivity()
)启动activity。为了从后续activity得到结果,实现onActivityResult()
回调方法。当后续activity完成后,它返回一个结果 Intent
给你的onActivityResult()
方法。
例如,也许你希望用户选择他们的联系人之一,所以你的activity可以做一些与该联系人的信息有关系的事情。下面是你如何创建这样一个意图并处理返回的结果:
private void pickContact() {
// Create an intent to "pick" a contact, as defined by the content provider URI
Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
startActivityForResult(intent, PICK_CONTACT_REQUEST);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// If the request went well (OK) and the request was PICK_CONTACT_REQUEST
if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) {
// Perform a query to the contact's content provider for the contact's name
Cursor cursor = getContentResolver().query(data.getData(),
new String[] {Contacts.DISPLAY_NAME}, null, null, null);
if (cursor.moveToFirst()) { // True if the cursor is not empty
int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);
String name = cursor.getString(columnIndex);
// Do something with the selected contact's name...
}
}
}
这个例子说明了使用onActivityResult()方法处理Activity的结果的基本逻辑。首要条件是检查请求是否成功和这个结果是响应是否已知,如果是,那么ResultCode会是RESULT_OK,在这种情况下,requestCode匹配 startActivityForResult()的第二个参数 。从那起,代码在 Intent
中处理取得Activity查询数据的返回结果的(data 参数)。
ContentResolver
针对一个content provider 执行了查询,它返回一个Cursor(游标),允许查询的数据被读取。欲了解更多信息,请参阅 Content Providers文档。
有关使用意图的更多信息,请参阅Intents and Intent Filters 的文档。
关闭一个活动(Shutting Down an Activity)
你可以通过调用 finish()
方法关闭activity。你也可以通过调用 finishActivity()
.关闭之前您启动的一个独立的Activity
注:在大多数情况下,你不应该使用这些方法明确的结束activity。在下面一节讨论有关的Activity生命周期,Android系统为你管理Activity, 所以你不需要来自己结束Activity。调用这些方法可对用户体验产生影响,只有你确定不想让用户返回此Activity实例时使用。
管理Activity生命周期(Managing the Activity Lifecycle)
通过实现回调方法管理你的活动的生命周期,对于你发展强大和灵活的应用是至关重要的。个与其关联的其他Activity直接影响其Activity的生命周期,任务和返回堆栈。
一个activity可以存在于基本上三种状态:
-
恢复
- Activity是在屏幕的前景中,并具有用户焦点。(该状态也有时被称为“运行”)。 暂停
-
另一个Activity显示在最前和具有焦点,但是这个Activity仍是可见的。即,其他的Activity显示在这个Activity的上面,并且它上面的Activity是部分透明或不覆盖整个屏幕。一个暂停的activity是完全存在的(该
Activity
对象保留在内存中,它保留所有的状态和成员信息,并且仍然在窗口管理器),但在系统内存及其低的情况下回被杀死。
停止
- Activity完全被另一个Activity遮住了(现在Activity是在“background”)。停止Activity也仍然存在( Activity 对象保留在内存中,它保持状态和成员信息,但不和窗口管理器有关联)。然而,它已不再是对用户可见,其他地方需要内存时,它可以被系统终止。
如果一项Activity被暂停或停止,该系统可以从内存中删除它,要求它结束(调用它的finish()方法),或者干脆杀死它的进程。Activity再次打开时(在被结束或被杀死),它必须创造所有的一切。
实现生命周期回调函数(Implementing the lifecycle callbacks)
当一个Activity按照上面所描述的不同状态如中进行转换时,它是通过各种回调方法通知。所有的回调方法是钩子,当你的活动状态改变时,你可以重写这些回调方法做相应的事情。下面的Activity,包括每一个生命周期的基本方法:
public class ExampleActivity extends Activity {
@Override
public voidonCreate
(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// The activity is being created.
}
@Override
protected voidonStart()
{
super.onStart();
// The activity is about to become visible.
}
@Override
protected voidonResume()
{
super.onResume();
// The activity has become visible (it is now "resumed").
}
@Override
protected voidonPause()
{
super.onPause();
// Another activity is taking focus (this activity is about to be "paused").
}
@Override
protected voidonStop()
{
super.onStop();
// The activity is no longer visible (it is now "stopped")
}
@Override
protected voidonDestroy()
{
super.onDestroy();
// The activity is about to be destroyed.
}
}
注意:您实现这些生命周期方法必须始终调用父类的实现方法,在做任何工作之前前,如上面的例子。
综合来看,这些方法定义一个Activity的整个生命周期。通过实施这些方法,您可以监视Activity周期三个嵌套循环:
- Activity整个生命周期存在于调用
onCreate
和调用的onDestroy()之间
。你的Activity应该执行“”全局状态的设置(如定义布局),在onCreate()函数中
,并在onDestroy()中释放所有剩余资源。例如,如果你的Activity有个在后台从网络下载数据运行的线程,它可能会在onCreate()创建那个线程
,然后在onDestroy()中停止该线程中。 -
Activity可见的声明周期存在于调用
onStart()
和onStop()之间
。在这段时间中,用户可以看到在屏幕上的Activity,并与其进行交互。例如,onStop()被调用,
当一个新的活动启动的时候,并且这个Activity不再可见。这两种方法之间,可以保留用来给用户显示Activity所需要的资源。例如,你可以在onStart()方法中注册一个BroadcastReceiver
,以监测影响您的UI的变化,并在onStop()中注销它,
当用户不再能看到你正在显示什么。系统在Activity的整个生命周期中可能会多次调用onStart()
和的onStop()
,因为Activity在对用户可见和隐藏之间交替。 -
Activity前台生命周期存在于调用
onResume
和onPause()之间
。在此期间,此Activity是在屏幕上的所有其他Activity之上,并具有用户输入的焦点。一个activity可以频繁的在前后台转换 - 例如,当设备进入睡眠或者对话显示的时候,onPause()被调用。因为这个状态可以经常转换,这两个方法中的代码应该适当的轻量话来避免让用户等待缓慢的转换。
图1说明activity可能在状态间发生的这些循环和路径。矩形区域描述当activity在状态间转换的时候你可以实现的回调函数来执行操作。
图1。activity生命周期。
同样的生命周期回调方法列在表1中,其中描述了各自的回调方法更详细信息和确定每一个在activity总的生命周期的位置,包括系统能否在回调函数完成之后杀死activity。
表1activity生命周期的回调方法的总结。
方法 | 描述 | 是否被杀死 | 下一个 | ||
---|---|---|---|---|---|
| 第一次创建Activity时调用。你应该在这做所有的你普通的静态设置 - 建立view,绑定list数据等等,这个方法传递一个包含此activity之前状态(如果这个状态被捕获了)的Bundle。 总是跟着 | 没有 | ONSTART() | ||
|
| 在Activity已停止后调用,之前它被重新启动。 后面总是跟着 | 没有 | ONSTART() | |
| 仅在Activity变的对用户可见之前被调用。 其次是 | 没有 | onResume() 或 的onStop() | ||
|
| 在Activity开始与用户交互之前调用。在这一点上的Activity是在Activity堆栈的顶部,伴随着用户的输入进入它。 后面总是跟随 | 没有 | 的onPause() | |
| 当系统即将启动恢复另一个活动时调用。此方法通常用于提交未保存的更改给持久性数据,停止动画和会消耗CPU等的其他东西。不管怎样它应该迅速的执行完毕,因为下一个activity要等到它返回后才会重新使用。如果activity返回到前面,后面则跟着onResume(),否则它对用户不可见,后面跟着onStop()。 | 是的 | onResume() 或 的onStop() | ||
| 当Activity不再对用户可见时调用,这可能会发生,因为它将要被销毁时或者另一个Activity(可以是一个现有的,也可以是一个新的Activity)恢复并正在覆盖它。如果返回去与用户交互,后面跟着onRestart(),否则如果activity正在消失,后面跟着onDestroy()。 | 是的 | onRestart() 或 的onDestroy() | ||
| Activity在被销毁之前调用。这是Activity获得最后的调用。它的调用可能是因为Activity正在关闭(有人在此调用了finish()),或者系统暂时销毁这个Activity实例来节省空间 你可以使用isFinishing()方法来区别这两种情况 | 是的 | 没什么 |
列标有“之后是否被杀死?” 表明系统是否能够在方法返回后的任何时间杀承载Activity的进程,而不执行Activity的另一行代码。三种方法都标记为“是”(onPause()
,onStop()
,和onDestroy()
)。因为onPause()
是三者中的第一个,一旦Activity被创建,onPause()是最后一个能保证进程被杀死之前能被调用的函数 - 如果系统必须紧急恢复内存的话,那么onStop()和onDestroy())也许不会被调用。
因此,你应该使用onPause()存储关键的持久性数据(如用户编辑)。然而,在onPause()期间,对于哪些信息是必须保存的你应该有选择性,这个方法中因为任何阻塞的程序都会阻塞转换到下一个activity并且放慢用户体验。
在Killable这列标记“No”的方法从被调用的时刻起保护这activity的进程不被杀死。因此,一个activity从onPause()返回到onResume()是可以被杀死的。直到onPause()被调用和返回,他才会再次成为可杀死的。
注意:对于Activity,表1中的定义,技术上来说并不是“可kill”,Activity仍然可以被系统"kill",但仅在系统资源匮乏的极端情况下才会发生。Activity何时能被kill,更多信息在Processes and Threading 文档中讨论。
保存Activity状态(Saving activity state)
管理Activity声明周期(Managing the Activity Lifecycle)的介绍简略的提到Activity暂停或者是停止时,Activity的状态仍保留着。这是事实,因为Activity对象在内存中所持有的所有关于其成员和当前状态的信息当它被暂停或停止时仍然活着。由此,Activity范围内用户所做的任何修改都会保留,使得当活动返回到前景(当它“恢复”)时,这些更改仍然存在。
然而,当系统销毁Activity以回收内存时,Activity
对象会被销毁,所以系统不能用它所保存的状态简单地恢复它。相反,如果用户按返回键返回到它,系统必须重新创建Activity
对象。然而,用户没有注意到系统销毁了activity然后又重建了它,因此也许期待这个activity会和之前一模一样这种情况下,通过实现运行你保存你的activity状态的额外的回调函数: onSaveInstanceState()
,你能保证activity状态最重要的信息被保存下来。
在使的Activity容易受到破坏之前,系统调用的onSaveInstanceState()
。该系统传递给其一个 Bundle
,您可以在其中以名称-值对的形式保存为有关Activity的状态信息,如使用方法 putString()
和putInt() 。那时,如果系统杀死你的应用进程,用户通过返回键返回Activity,系统重建Activity,系统会同时将 Bundle
传给onCreate()
和 onRestoreInstanceState()。使用这两种方法,你可以从
Bundle
提取到保存的状态信息用来恢复Activity。如果没有恢复的状态信息, Bundle
传递的是空(首次创建Activity的情况)。
图2,两种方式activity带着其完整的状态返回到用户焦点:要么activity被销毁了,然后重新建立并且activity必须恢复之前保存的状态,要么activity停止了,然后重新开始,activity状态完整的保存。
注意:不保证 onSaveInstanceState()
会在你的Activity销毁之前被调用,因为有许多情况是没有必要保存状态的(例如当用户使用back键离开你的Activity,因为用户就是想关闭这个Activity)。如果系统调用 onSaveInstanceState()
,它会在 onStop()
之前,并且可能在onPause()之前。
但是,即使你什么也不做,不实现的onSaveInstanceState()
,一些Activity状态通过Activity类默认的方法onSaveInstanceState()来恢复
。默认实现为每一个布局中的View
调用允许每一个view提供它自己应该保存的信息的相应的 onSaveInstanceState()
方法。几乎每一个在Android framework中的widget都恰当的实现了这个方法,这样任何UI上可见的变化都会自动的保存并且当你的activity重新建立的时候被恢复。例如, EditText
部件保存用户输入的任何文本, CheckBox
保存它是否被选中。唯一要求你完成的工作是为每一个你想保存状态的部件提供一个唯一ID(使用 android:id
属性)。
虽然onSaveInstanceState()的默认实现保存了你的activity的UI有用的信息,你仍然也许需要覆盖它来保存更多的信息例如,你也许需要保存成员在activity的生命期间改变的值(与UI中恢复的值相关,但是默认的,成员变量保存的这些UI值没有恢复)。
通过设置android:saveEnabled属性为“false“或者调用setSaveEnabled()方法,你也可以精确的阻止一个你布局中的view保存它的状态。通常,你不应该让它失效,除非如果你想别具一格的恢复activity的UI状态。
因为onSaveInstanceState()的默认实现为保存UI状态提供帮助,如果你因要保存额外的状态信息覆盖这个方法,在做任何工作之前,你应该总是调用父类onSaveInstanceState()的实现。同样的,如果你重写onRestoreInstanceState(),你也应该调用父类的实现,因为默认实现可以恢复view状态。
注:因为onSaveInstanceState()并不能保证被调用,只有记录暂时性的activity状态(UI的状态)你才应该使用 - 你不应该使用它来保存持久化数据。相反,当用户离开activity时,你应该使用 onPause()
来保存持久化数据(比如应该存入数据库的数据)。
测试你应用恢复状态的能力的一种好的办法是旋转设备来改变屏幕方向。当屏幕方向改变时,系统销毁并且重新建立activity来应用可能对新屏幕方向有效的可采用的资源。仅仅出于这个原因,当activity重新建立时,你的activity完全的恢复其状态是非常重要的。因为用户使用应用期间会经常的旋转屏幕。
处理配置更改(Handling configuration changes)
一些设备的配置在运行期间可能改变(如屏幕方向,键盘可用性和语言),当这样的变化发生时,Android会重建正在 运行的Activity(系统调用onDestroy(),然后,立即调用onCreate())。这种行为的设计有助于你的应用通过使用你提供的有效的资源(比如为不同屏幕方向和尺寸不同的布局)重新加载你应用来自动适应新配置。
如果你正确地设计自己的Activity来处理由于屏幕方向变化和恢复如上所述的Activity状态的重新启动,你的应用将更具弹性,以应付在Activity生命周期中的其他突发事件。
处理这种重新启动最好的方法是使用onSaveInstanceState()和onRestoreInstanceState()
(or onCreate()
)方法保存和恢复你的Activity的状态,如上一节讨论的。
关于运行时配置改变和如何处理这些,请阅读Handling Runtime Changes。
协调活动(Coordinating activities)
当一个Activity启动另一个时,它们都经历生命周期的交替。第一个Activity暂停,停止(尽管它不会停止,如果它仍然在后台可见),在其他Activity被创建期间。一旦这些Activity共享保存到磁盘或其它地方的数据,理解第一个Activity不会在第二个Activity创建之前完全被停止,是尤为重要的。相反,启动第二个Activity的进程与停止第一个Activity的进程有重叠(注:应该是指时间上的重叠)。
生命周期回调函数的顺序是明确定义的,特别是当这两个Activity是在同一进程中,一个启动另一个。以下是ActivityA启动ActivityB的顺序:
- Activity A's
onPause()
方法被执行. - Activity B's
onCreate()
,onStart()
, andonResume()
方法一次执行。 (Activity B 现在没有用户焦点.) - 然后,如果Activity A在屏幕上不再可见,它的
onStop()
方法被执行。
这可预知的声明周期回调的顺序可以让你管理从一个Activity到另一个Activity的信息的交替。例如,如果你必须对一个数据库进行写入,当第一个Activity停止以便接下来的Activity能读取它时,那时,你应该在 onPause()期间写入数据库,而不是在onStop()期间。