一个Activity是一个应用程序的组件,它提供了一个用户可以进行交互的屏幕,例如拨打电话,拍照,发送邮件,或者查看地图。每个activity都提供了一个window用来绘制用户接口。这个window通常填充屏幕,但是可能比屏幕小并且悬浮在其他的window上面。
一个应用程序通常由多个松弛结合的activity构成,通常,一个应用程序会指定一个activity作为“main”activity,用来在用户启动程序的时候首先被显示。每个activity可以启动其它的activity为了执行更多的操作,每次一个新的activity被启动,之前的那个activity就会被停止(stop),但是系统会将这些activity维护成一个栈(stack,“back stack”)。当一个新的activity启动,它被压入back stack的顶端,并获得用户的焦点。back stack遵守的是last in first out的堆栈原则,因此,当用户对当前的activity的操作完成了并按下了返回按钮,则当前的activity就离开了栈并销毁(destroy),上一个activity恢复(resume)。(返回栈 back stack 在Tasks and Back Stack 文档中有更多的讨论)
当一个activity因为一个新的activity启动而被停止,这些状态的改变会通过activity的生命周期回调函数得到通知。activity会收到一系列的回调方法,由于状态的改变,无论系统是否创建它,停止它,重新启动它或者销毁它,每个回调函数都为你提供了一个让你适应状态改变即机会,让你适应改变。例如,当activity停止了,你的activity应该释放大的对象,例如网络或者数据库连接。当activity被重新启动的时候,你可以重新获得必要的资源和执行之前被中断的动作。这些转变的状态就是activity的生命周期的全部部分。
本文的接下去讨论的是基本的关于任何建立和使用一个activity,包含了一个完整的关于activity生命周期任何工作的讨论,因此你可以在activity状态改变的时候进行正确适当的管理。
Creating an Activity
创建一个activity,你需要创建一个Activity的子类(或者继承自一个已经存在的Activity的子类)。在你的子类中,你需要实现activity的回调函数,以便当activity的生命周期状态改变的时候,这些回调函数能被系统所调用,例如,在activity被创建,停止,重新启动或者销毁的时候。两个最重要的回调函数是:
onCreate()
你必须实现这个方法。在创建activity的时候系统会调用这个方法。在你实现这个方法的时候,你需要在这里面为你的activity初始化必要的组件。最重要的是,在这个你必须要调用setContentView()这个方法设置布局文件,来为你的activity定义用户接口。
onPause()
当用户要离开这个activity的时候,系统会首先调用这个方法。(尽管这并不总是意味着这个activity会被销毁)。这里通常是你需要保存用户改变的地方。(因为用户可能不会再回到这个页面了)。
还有几个其他的你需要注意的生命周期方法,通过使用这些方法可以在activity切换和处理中断异常的时候为用户提供流畅的用户体验,因为你的activity会被停止甚至销毁。所有的这些生命周期回调方法接下去的Managing the Activity Lifecycle 章节被讨论。
Implementing a user interface
一个activity的用户接口是由view的继承树提供的--继承自View类的一系列对象。每个view控制着一个activity的window中的某一特定的矩形空间,并且可以响应用户交互。例如,一个view可能是一个按钮Button,在用户触摸它的时候可以进行响应。
Android提供了一系列的已经准备好的view,你可以使用它们来设计和组织你的布局。“Widgets ”就是提供给你的虚拟的,可以用来交互的view元素,例如一个Button,text field,checkbox或者是一个image,“Layouts ”是继承自ViewGroup的View元素,为你的子View提供一个唯一的布局模式,例如一个线性布局,一个表格布局,或者一个相对布局。你也可以继承自View和ViewGroup类(或者已经存在的子类)来创建你自己的widget和layouts,并将它们运用到你的activity的布局中去。
最常用的做法是使用view定义一个XML布局文件并保存在你的应用程序的资源文件中。通过这种做法你可以很好地将你的代码和用户接口区分开来。你可以通过传递布局文件的资源Id给setContentView()方法来为你的activity设置UI布局。然而,你也可以在activity中使用代码来创建一个view树,然后将你的根ViewGroup传递给setContentView()方法。
更多的关于创建用户接口的信息。参考User Interface 文档。
Declaring the activity in the manifest
为了让系统能够得到你的activity,你必须声明在manifest文件中声明你的activity。方法是,打开你的manifest文件,在<application>标签下添加一个<activity>子标签。例如:
<manifest ... >
<application ... >
<activity android:name=".ExampleActivity" />
...
</application ... >
...
</manifest >
在这个标签元素中你还可以加入其它的一些属性,比如,为你的activity加入label,一个icon,或者activity的UI样式。android:name 属性是唯一必须的属性,它指定了这个activity的类名。一旦你发布了你的应用程序,你就不能更改这个名字,因为你如果这样做了,你可能会影响一些功能,比如应用程序的快捷键图标(参考博客,Things That Cannot Change )
更多关于在manifest文件中声明你的activity的信息参考<activity> 元素。
Using intent filters
一个<activity> 标签也可以指定不同的intent filter,通过使用<intent-filter> 标签,可以告诉其他应用程序如何激活这个组件。
当你使用Android SDK工具创建一个新的应用程序的时候,为你自动创建的activity的子类中已经包含了一个intent filter,它相当于声明这个activity为程序的入口,就能够被放置到启动目录中。该intent filter是这样的:
<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>
<action>标签指定了这是应用程序的入口点。<category>标签指定了这个activity要被放到系统的应用程序启动目录中(让用户可以启动这个activity)。
如果你不打算让别的应用程序可以激活你的activities,你就不再需要别的任何intent filter。只有一个activity需要“main”action和“launcher”目录,正如上一个例子。如果你不想让你的activities被其它应用程序激活调用,那么就不应该有intent filters,你可以通过明确的intent自己启动它们(下面的章节将会讨论)
然而,如果你想让你的activity响应来自其它应用程序(或者你自己的)的不明确的intent,你必须为你的activity定义额外的intent filter。为你想要响应的每种类型的intent,你必须包含一个<intent-filter>标签,在这个标签中包含<action>标签,并且你可以随意选择<category>和/或<data>标签。这些标签指定你的activity能够响应的intent的类型。
更多的关于你的activities如何响应intents的信息,参考Intents and Intent Filter
Starting an Activity
你可以通过调用startActivity()来启动一个Activity,并且传递一个Intent来描述你想要启动的activity。intent指定的可以是一个确定的activity也可以是你想要执行的某一类型的action(系统为你选择合适的activity,这个activity可能来自不同的应用程序)。一个intent也可以携带少量的数据量,可以被要被启动的activity使用。
在你自己的应用程序内,你经常需要启动一个已知的activity。你可以通过使用类名来明确定义你想启动的activity。例如,下面是一个activity如何启动一个名为SignInActivity的activity:
Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);
然而,你的应用程序也可能想要执行一些action,例如,发送一份邮件,文本消息或者状态更新,使用来自你的activity的数据。这种情况下,你的应用程序可能没有这样的activity来执行这样的action,所以你可以利用设备中其它应用程序提供的能够执行这些action的activities。这就是intents真正有用的地方,你可以描述一个你想要执行的action是什么样的,系统会从其它的应用程序中为你启动一个合适的activity。通过有多个activity能够处理这个intent,用户可以选择使用哪个。例如,如果你想允许用户发送一封邮件,你可以创建下面的intent:
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);
EXTRA_EMAIL 是intent额外添加的字符串数组,代表的是发送email的地址。当一个邮件应用程序响应了这个intent,它读取extra提供的字符串数组,并把这个字符串放到email接受者的输入框中。这种情况下,email应用程序的activity启动,当用户发送邮件结束,你的activity会重新启动。
Starting an activity for a result
有时候,你可能想从你启动的activity中获得一个值。这种情况下,在你启动这个activity的时候调用startActivityForResult() (而不是startActivity() )。实现onActivityResult() 回调方法来从被启动的activity中获取结果。当被启动的activity完成的时候,它将结果放在一个Intent中返回给你的onActivityResult() 方法中。
例如,可能你想要用户从他们的联系人中选择一个联系人,这样你的activity就可以利用这个联系人中的信息做一些事情。下面展示的是你应该如何创建一个这样的intent并处理返回的结果:
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进行处理了(即data 参数)。
当一个ContentResolver 对一个content provider 执行一个查询的时候,返回的是一个可以对查询结果进行读取的Cursor 。更多的信息可以参考Content Provider 文档。
更多的关于使用intents的信息参考Intents and Intent Filter 文档。
Shutting Down an Activity
你可以通过调用activity的finish() 方法来关闭它。你也可以通过调用finishActivity() 关闭你之前打开的activity。
注意:在多数情况下,你不应该使用这些方法来关闭一个明确的activity。正如下面章节中所要讨论的关于activity的生命周期,Android系统会为你管理activity的生命,因此你不需要finish自己的activities。调用这些方法反而会造成不好用户体验,只有在你确实不想要让用户回到之前所创建的那个activity实例的情况下才去调用它。
Managing the Activity Lifecycle
通过实现回调方法来管理你的activities的生命周期对于开发出健壮和灵活的应用程序十分重要。一个activity的生命周期会与它相关的其他的activity有直接的影响。
从根本上来说,一个activity存在3种状态:
Resumed
activity到屏幕的最上层“foreground”并获得了用户的焦点。(这种状态有时也叫做运行状态“running”)。
Paused
另一个activity来到了屏幕的最上层并获得了用户的焦点,但是这个activity仍然可见。也就是这种情况:另一个activity
来到了屏幕的顶端,但是他是部分透明的,或者并没有完全覆盖整个屏幕。一个paused状态的activity是完全活着的(它保存在内存中,维持着所有的状态和成员信息,仍然附属于窗口管理器window manager),但是当系统的内存不够用的情况下,依然可能被杀死。
Stopped
activity被另外一个activity完全遮盖的时候(当前的activity现在到了后台“background”)。一个stopped状态的activity也是活着的(这个activity对象保存在内存中,它维持着所有的状态和成员信息,但是不附属于窗口管理器window manager)。然而,它对用户来说不可见并且在内存不够用的时候会被系统杀死。
如果一个activity是paused或者stopped状态,系统会把它从系统中移除,通过调用它的finish方法或者更简单地把它的process杀死。当这个activity被重新打开的时候(被finished或者杀死后重新打开),它必须被重新创建。
Implementing the lifecycle callbacks
当一个activity状态改变并且不在上面所描述的状态中,它被其它的回调方法或注意到了。所有这些回调方法都是钩子,你可以覆盖这些钩子并在activity状态改变的时候做一些适当的工作。下面是包含了activity生命周期方法框架的activity:
public class ExampleActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// The activity is being created.
}
@Override
protected void onStart() {
super.onStart();
// The activity is about to become visible.
}
@Override
protected void onResume() {
super.onResume();
// The activity has become visible (it is now "resumed").
}
@Override
protected void onPause() {
super.onPause();
// Another activity is taking focus (this activity is about to be "paused").
}
@Override
protected void onStop() {
super.onStop();
// The activity is no longer visible (it is now "stopped")
}
@Override
protected void onDestroy() {
super.onDestroy();
// The activity is about to be destroyed.
}
}
注意:在实现这些生命周期方法之前你必须总是调用超类的实现。如上面代码所示。
结合在一起看,这些方法定义了一个activity的整个生命周期。通过实现这些方法,你可以在activity的生命周期中监视3个内嵌的循环:
整个生命周期entire lifetime 发生在onCreate() 和onDestroy() 之间。你的activity应该在onCreate()中执行全局的设置(比如在onCreate()方法中定义布局文件),并且在onDestroy()方法中释放维持着的资源。例如,如果你的activity有一个运行在后台从网络上下载数据的线程,那么这个线程可能需要在onCreate()中被创建,在onDestroy()中被停止。
可见生命周期visible lifetime 发生在onStart() 和onStop() 之间。在这段时间里,用户可以在屏幕上看见他并且可以进行交互。例如,当另一个activity启动的时候这个activity变得不可见的时候,onStop()方法被调用。在这两个方法之间,你可以维持着展现给用户看的必须的资源。例如,你可以在onStart()中注册一个BroadcastReceiver来监视影响你的UI的变化,并在onStop()的时候取消监听,因为这时候用户已经看不见你所显示的东西了。在activity的整个生命周期中,随着你的activity对用户可见和不可见,系统可能多次调用onStart()和onStop()方法。
前台生命周期foreground lifetime 发生在onResume() 和onPause() 之间,在这段时间内,activity处于其它所有的activity之上,处于屏幕的顶端,并获得用户的焦点。一个activity可以频繁地进出前台,例如,当设备进入睡眠状态或者出现了一个对话框,onPause()方法都会被调用。因为这个状态频繁转变,在这两个方法内的代码应该适当轻量级,因为这样可以避免转变时间太长,让用户等待太久。
下图展示了activity可能处于的各种状态的循环和路径。矩形框代表的是当activity状态改变切换改变的时候,你可以用来执行操作的回调方法。
注:接下去有一个表格,列的是各种状态下activity是否会被杀死等,但是太麻烦,不好画,暂时不翻译,下面给出链接:
http://developer.android.com/guide/components/activities.html#Lifecycle
Saving activity state
上面的Managing the Activity Lifecycle简要地提到了当一个activity是paused或者stopped状态的时候,activity的状态仍然保持着。确实是这样,因为当activity处于paused或者stopped状态的时候,它仍然保存在内存中,关于这个activity的成员和状态信息都保持着。因此,当这个activity重新回到前台foreground(当他“resumes”),用户在activity中所做的任何改变都保持着。
然而,当系统为了回收内存销毁了一个activity,由于activity被销毁了,当用户重新回到这个activity的时候,系统就不能简单地重新启动它(resume),而是需要重新创建activity对象。然而用户却不知道系统销毁了这个activity并且重新创建了它,这样的话,可能就需要让这个activity回到它之前的样子。这种情况下,你可以利用额外的回调方法onSaveInstanceState()方法来帮你保存一些activity的信息,以便恢复成原来的样子。
onSaveInstanceState()方法在activity被销毁之前会被系统调用。系统传递一个Bundle给这个方法,通过使用例如putString(),putInt()这样的方法,你就可以以键值对的方式将这个activity的状态信息保存在里面。这样,如果系统杀死了你的应用程序的process,当用户回到这个activity的时候,系统重新创建这个activity并将Bundle对象传递给onCreate()和onRestoreInstanceState()。你可以使用这两个方法中的一个来获取Bundle中保存的内容,来恢复你的activity状态。如果没有状态信息要存储,传递给你的Bundle参数是null(这种情况可能是activity第一次被创建的时候)。
上图展示的是一个activity回到初始状态的两种方式:一种是一个activity被销毁了,在重新创建以后,这个activity必须恢复之前保存的状态,一种是这个activity停止了,但是没有销毁,重新启动后还是之前的状态。
注意:在你的activity被销毁的时候,onSaveInstanceState()方法不会保证被调用到,因为有些情况下它没有必要保存数据(例如用户使用返回按钮离开你的activity,这表明用户很确定要离开这个activity)。如果系统调用onSaveInstanceState(),那么它会在onStop()之前,也可能会在onPause()之前调用它。
然而,即时你什么都不做并且不实现onSaveInstanceState()方法,一些activity的状态会被Activity类对onSaveInstanceState()方法的默认实现所恢复。特别的是,为布局文件中的每个view调用相对应的onSaveInstanceState()方法,能够让每个view提供需要被保存的自己的信息。在Android framework层中几乎每个widget都适当地实现了这个方法,
这样的话,UI中每个可见的改变都自动被保存,并在你的activity重新创建的时候恢复。例如,EditText保存任何用户输入的文本,CheckBox保存用户是否选中。你唯一要做的是为每个你想要保存它状态的widget提供一个唯一的ID(通过android:id属性)。如果一个widget没有一个ID,那么系统是不能保存它的状态的。
这个地方存在一个疑问,难道可以默认保存状态?如何恢复呢?
尽管默认实现的onSaveInstanceState()方法为你的activity的UI保存有用的信息,你可能仍然需要覆写这个方法来保存一些额外的信息。例如,你可能需要保存在activity生命周期中改变的成员值(可能与你的UI中存储的值有关,这些成员值默认是不会被保存的)。
因为onSaveInstanceState()方法的默认实现帮助你保存了UI的状态,如果你覆盖了这个方法来保存额外的状态信息,在做任何操作之前,你应该总是需要调用onSaveInstanceState()方法的超类实现。同样地,如果你覆盖onRestoreInstanceState()方法,你也需要先调用它的超类实现,这样默认实现就可以恢复view的状态了。
注意:因为onSaveInstanceState()方法不能保证被调用到,你应该只能用他来存储activity的状态(UI的状态),你不能使用它来存储重要的,固定的数据。当用户离开activity的时候,你应该使用onPause()方法来存储数据(例如需要保存在数据库中的数据)。
一个好的方法来测试你的应用程序恢复它的状态的能力是简单地旋转设备,这样你的屏幕的方向就改变了。当屏幕的方向改变的时候,系统销毁并重新创建了activity以便运用可以适应新屏幕的可替换的资源文件。因为这个原因,当activity重新创建的时候让你的activity恢复到之前的状态就显得非常重要了,因为用户在使用应用程序的时候经常会旋转屏幕。
Handling configuration changes
一些设备的轮廓在程序运行的时候经常改变(例如,屏幕的方向的改变,键盘状态的改变)。当这些改变发生的时候,Android重新创建了运行时的activity(系统调用了onDestroy()方法后立刻又调用了onCreate()方法)。设计这种行为的目的是让你的应用程序能够自动重新加载你所提供的可替换的资源(例如,为不同屏幕方向和大小设计的布局)以适应这些改变。
如果你适当地设计你的应用程序来处理上面描述的,由于屏幕方向的改变,所引起的activity重新开始,那么你的应用程序在它的activity的生命周期中就会变得更加有适应性。
处理这样的重新启动引起的问题的最好方式是,使用上面所讨论的onSaveInstanceState()和onRestoreInstanceState()(或者onCreate()),来保存和恢复你的activity的状态。
更多的关于任何处理运行时配置改变的信息,参考指导Handling Runtime Changes 。
Coordinating activities
当一个activity启动另一个activity,他们都经历着生命周期的改变。第一个activity变为pauses和stops(当然,如果他在后台仍然可见的话,是不会stop的),当另一个activity被创建的时候,假设这时候这些activities共享存储介质或者其它地方的数据,你一定要明白,第一个activity在第二个activity被创建之前并没有完全stop的!相当于,启动第二个activity的process叠在了停止第一个activity的process上面了。
当两个activities在同一个process中,其中一个启动另一个的时候,生命周期回调函数的顺序被很好地定义了。下面是Activity A启动Activity B时候发生的操作顺序:
1.Activity A的onPause()方法被执行。
2.Activity B的onCreate(),onStart()和onResume()方法被依次执行。(Activity B现在获得了用户的焦点)
3.这时候,如果Activity A在屏幕上仍然不可见,那么它的onStop()方法被执行。
这个可预测的一系列生命周期函数允许你来管理从一个activity到另一个activity所带来的变化。例如,如果你在第一个activity停止的时候你要将数据写到数据库中,以便下一个activity可以读取,那么你就要在onPause()方法中来执行写入数据库的操作,而不是在onStop()方法中。