[译]Activity

Activities

Activity是一个应用组件(Application Component),它提供一个与用户交互的界面。比如拨打电话,拍照,发送Email,查看地图等。每个Activity都提供展示UI的Window。通常情况下这个Window都是全屏的,当然也可以是小于屏幕或者浮在其他Window上面的。

一个Application通常包含很多个Activity,这些Activity之间的关系彼此松散。典型的,一个Application中有一个称作为Main Activity的Activity,此Main Activity在用户首次启动这个Application时展示给用户。每个Activity都可以为了执行不同的Action启动另外一个Activity。每次当一个新的Activity启动时,前一个Activity会被stop,但是系统会保存这个Activity在一个栈(Back Stack)中。当一个新的Activity启动时,会被push到这个Stack的Top,并且获得用户焦点(User focus)。此Back Stack遵循“LIFO(先进后出)”的原则,所以,当用户完成此Activity的工作并点击“Back”键返回时,此Activity会从Back Stack中pop(并且destroy),前一个Activity会被resume。关于Back Stack的部分详细请参照Tasks and Back Stack文档。

当一个Activity是被另一个Activity的start所stop时,它会通过Activity’s Lifecycle来通知状态的变化。由于这个状态的变化(creating,stopping,resuming 或者destroying ),Activity会接收一系列的回调方法,这些回调方法提供给你一个机会去根据状态的变化来做一些工作。比如,当Stopped的时候,你的Activity应该释放所有大的Object,比如数据库或者网络连接。当Resume,你可以重新获取必要的资源和重新开始之前中断的工作。这些状态转换是所有Activity Lifecycle的一部分。

底下的文档会讨论一些基础知识,怎么去创建和使用Activity,Activity Lifecycle如何工作,帮助你妥善管理和处理这些状态之间的转换。

Creating an Activity

要创建一个Activity,你必须创建一个Activity.java的子类,(或是子类的子类)。在这个我们自己创建的Activity子类中, 需要去实现一些Activity Liftcycle的回调方法。

  • onCreate()
    你必须实现此方法,系统会在创建Activity时调用它。在你的实现里面,你应用初始化一些必要的组件。最重要的是,你必须调用setContentView()去定义这个Activity要展示给用户的UI。
  • onPause()
    当用户要离开此Activity的第一时间系统会调用此方法,(此处“离开”并不一定意味着这个Activity会被destroyed)。通常在这里你应该保存那些在此用户会话外且应该持久化的变化(因为用户可能不会回来)。

为了在Activity的切换或者在意外情况打断你的Activity stop甚至destroy的情况给用户流畅的体验,还需要了解更多其它的Lifecycle的回调方法。在Managing the Activity Liftcycle章节详细讨论。

Implementing a user interface

Activity通过一个层级(hierarchy)的View(全部继承自View.java)来展示UI。每一个View控制着这个Activity’s Window的一个特定的矩形区域,并且可以与用户交互。比如,这个View可以是一个Button,用户可以touch它来触发一些操作。

Android提供了大量现成的View,你可以用来设计和组织你的Layout。“Widget”作为View提供在屏幕上可视(可交互)的元素,比如Button,TextView,Checkbox等等;“Layout”作为继承自ViewGroup的View给它的子View提供一种独特的布局方式,比如LinearLayout,GridLayout,RelativeLayout等。你也可以自己继承View后ViewGroup来实现你自己特有的View或Layout显示在Activity中。

定义一个Layout的最通常的方式是在你的Application Resource中顶一个XML布局文件。这种方式可以保持你的UI与你的Activity行为代码分离。你可以通过setContentView()(传一个此Layout的resource id作为参数)来将此Layout设置成UI。当然,你也可以自己创建一个View来作为此方法的参数初始化UI。

更多UI方面的信息,参见User Interface文档

Declaring the activity in the manifest

为了让系统可以访问你的Activity,你必须在Application的Manifest文件中定义此Activity。格式如下:

1
2
3
4
5
6
7
<manifest ... >
<application ... >
<activity android:name=".ExampleActivity" />
...
</application ... >
...
</manifest >

你可以定义为此元素定义一些属性,比如说activity的label,icon或者主题样式等。android:name是必须要有的属性,它指定此Activity元素所对应的class name。一旦你发布了你的Application,你就不应该更改这个name。

Using intent filters

元素还可以使用元素指定不同的intent filter,来声明其他的Activity怎么来激活它。

当你使用SDK创建一个Application时,会自动创建一个包含“main” action和“launcher”category的子Activity。如下:

1
2
3
4
5
6
<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>

元素指明此Activity作为Application的main入口。 元素指明此Activity会被列举在系统应用Launcher中(允许用户在Launcher界面启动它)。

如果你想你的Application的Activity不能被其他Application激活,你不需要任何其他的Intent filter。只有一个Activity应用有main action和launcher category。不想让其他Application可用的Activity可以不声明intent filter, 在你的Application内部你可以使用显式的intent方式启动它。

当然,如果你想你的Activity能过响应其他Application(或是本Application)发出的隐式的Intent,你就必须给你的Activity添加额外的Intent filter。在添加的元素中,必须包含一个元素,作为可选的,你还可以添加一些的元素。这些元素结合在一起决定了你的Activity可以响应的Intent 类型。

关于Intent的更多信息,参考Intents and Intent Filters文档。

Starting an Activity

你可以通过调用startActivity()方法来启动一个Activity,传一个Intent作为参数来指定你想启动的Activity。此Intent不仅可以指明确切的Activity,还可以描述你想要执行的Action type(系统会选择适当的Activity(可能是别的Application的)来执行)。此Intent还可以为你将要启动的Activity携带少量的数据。

在Application内部运作时,你通常只需要简单的指定一个可知的Activity。你可以指明你想要启动的Activity的class name去显示的启动它。比如:

1
2
Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);

你的Application还可能想要处理一些action,比如发送Email,SMS或者是状态更新等等。在这种情况下,你可以不需要在你的Application中处理这些action,你可以使用设备中其他的Application来处理这些action。这正是Intent的价值所在——-你可以创建一个描述你想干什么的Intent,然后通过系统来为你从其他Application中启动合适的Activity。如果有多个Activity可以处理这个Intent,用户可以选择其中的一个来使用。比如,你想发Email,你可以创建如下的Intent:

1
2
3
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);

此EXTRA_EMAIL包含了Email要发送的Email地址。当Email Application响应此Intent时,会读取出此数据,放在Email的to地址中。当用户处理完此Email后,会回到你的Activity。

Starting an activity for a result

有时,你可能需要从你启动的Activity中获取一个result。在这种情况下,我们使用startActivityForResult()来启动Activity。在我们接收result的Activity中需要实现onActivityResult()回调方法。当被调用Activity执行完会传送一个包含result的intent给我们的onActivityResult()回调。

例如,你想要用户去Contacts中pick一位联系人,以便你的Activity对此联系人进行一些处理。你可以类似如下这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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()中处理你得到的result。你需要先check这个请求是否成功了(resultCode为RESULT_OK),还要此次返回的result是不是你请求的(requestCode和startActivityForResult()的第二个参数一致)。如果是,你可以在传回来的Intent中获取你想要的result data。

此处实例中涉及的ContentResolver,Cursor等概念,详见ContentProvider文档。
Intent的更多信息,参见Intents and Intent Filters文档。

Shutting Down an Activity

你可以调用finish()来关闭一个Activity。也可以调用finishActivity()来关闭一个你之前启动的Activity。

注意:绝大多数情况下,你都不应该调用这些方法开显示的finish一个Activity。在地下的Activity生命周期章节我们会讨论到,Android系统会为我们管理Activity的Life,所以不需要我们自己去finish这些Activity。调用这些方法可能产生不利不可预期的用户体验,所以这些finish方法应该在只有你绝对确定不想用户再回到某一Activity的情况下使用。

Managing the Activity Lifecycle

通过实现这些回调方法来管理我们的Activity Lifecycle是开发一个强健和灵活的Application的关键。一个Activity的生命周期直接影响与它关联了其他Activity以及它坐在的Task和Back Stack。

一个Activity基本上有三种状态:

  • Resumed
    Activity处在屏幕前台(foreground)且获得用户focus。通常也称之为running状态。

  • Paused
    另外一个Activity处于foreground且获得focus,但是此Activity还是可见的。也就是说,有另外一个可见的Activity处在当前Activity的上方,但是另外的这个Activity是部分透明或没有覆盖整个屏幕的。一个Paused状态的Activity完全是alive(这个Activity的对象还保留在内存中,它维持着所有状态和成员信息,且保持与Window Manager的连接)的。但是在系统极端低内存的情况下此种状态的Activity也是可以被kill掉的。

  • Stopped
    此Activity完全被另一个Activity遮盖,(此Activity已经处于background)。Stopped状态的Activity依然是alive(与Paused不同的是,它不再与Window Manager保持连接)的。它不再是对用户可见的,系统会在别处需要内存时将它kill掉。

如果一个Activity处于Paused或Stopped状态,不管系统是通过调用finish()还是简单的kill掉它所在的process来把它从内存中拿掉。当这个Activity重新被打开时,它必须重新create。

Implementing the lifecycle callbacks

当一个Activity在上述状态中不断转换时,它会通知如下一系列的回调方法。你可以override这些回调来在状态变化时做些适当的工作。以下列举了基本的生命周期回调:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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的整个Lifecycle。实现这些方法,你可以监测Activity lifecycle中的三个嵌套循环:

  • Entire Lifetime
    onCreate()和onDestroy()之间。你的Activity应该在onCreate()初始化全局状态,比如定义Layout。在onDestroy()中释放所有持有的资源。例如。如果你的Activity中有一个线程在后台下载网络数据,这个线程应该在onCreate()创建而在onDestroy()中停止。

  • Visible Lifetime
    onStart()和onStop()之间。这段时间,用户可以在屏幕中看到此Activity且可以与之交互。例如,onStop()会在另一个Activity启动而这个不可见时被调用。在这两个方法之间,可以维持Activity所需要的资源展示给用户。例如,可以在onStart()中register一个BroadcastReceiver去监测一些变化来改变你的UI, 在onStop()中unRegister它,因为用户已经看不到你展示了界面了。我们的Activity在不断对用户可见于不可见的轮换中,onStart()和onStop()在Entire Lifetime可能被多次调用。

  • Foreground Lifttime
    onResume()和onPause()之间。这个阶段,Activity显示在屏幕上(其他所有Activity之上),且拥有用户的输入焦点(input focus)。Activity可以经常切换到(in/out)foreground。例如,onPause()会在屏幕休眠或弹出Dialog时调用。正因为这个状态会经常性的切换,这两个方法的代码要尽量保持轻量级以避免在状态切换时迫使用户等待。

下图展示了这些循环和状态之间互相可能的路径。矩形区域表示我们可以实现的回调方法。
17-35-52

另外,下表也展示了同样的lifecycle,包含一些方法的描述以及系统是否可以再方法调用之后kill它。

Method Description Killable after? Next
onCreate() Activity第一次创建时会调用。在这里可以做一些静态的初始化——常见View、为List绑定数据等等。这个方法有一个包含之前Activity状态的Bundle对象作为其参数 (参见Saving Activity State, later)。 No 之后总是onStart().
onRestart() 在Activity被Stopped之后,再次启动时调用。 No onStart()
onStart() 当Activity变得对用户可见时调用。如果Activity切换到foreground会调用onResume(). 如果被隐藏不可见会调用onStop()。 No onResume() or onStop()
onResume() 当Activity开始可以与用户交互时调用。此时,Activity处于Stack的最顶端,用户可以与其交互(touch,focus,input)。之后总是onPause(). No onPause()
onPause() 当系统开始resume另外一个Activity时调用。这个方法通常被用来保存持久数据,停止动画以及其他消费CPU的操作等等。它应该尽可能快的执行,因为下一个Activity要等它执行完后才能resumed。如果Activity重新回到前台会执行onResume()。当变得对用户不可见时会执行onStop()。 Yes onResume() or onStop()
onStop() 当Activity对用户不再可见时调用。这种情况可能发生在Activity正在被destroy,或是另外一个Activity resumed且覆盖了它。如果Activity重新回到前台与用户交互会调用onRestart()。当Activity是要被销毁时会调用onDestroy()。 Yes onRestart() or onDestroy()
onDestroy() 在Activity被销毁之前调用。这是这个Activity最后接收到的回调方法。情况发生可能是因为这个Activity正在finish(调用了finish()方法),或是系统要暂时销毁这个Activity的实例一边节省空间。你可以使用isFinishing()方法来区分这两种情况。 Yes nothing

“Killable after”这列指明系统是否可以在这个方法执行返回后kill掉它所在process(不执行后续的代码)。onPause()、onStop()、onDestroy()被标记为“Yes”。

因为onPause方法是在Activity创建后,process可以被kill之前最后一个肯定会被调用的方法(如果系统要紧急恢复memory,onStop和onDestroy都可能不被执行)。所以,我们应该在onPause中保存重要的持久数据到storage。然而,你也应该考虑在onPause里选择性的保存必须保存的信息,因为此方法中的任何阻塞将会阻塞下一个Activity的状态转换,从而带来很慢的用户体验。

标记为“No”的方法系统会保证在这些方法调用时Activity所在的process不会被kill。所以当一个Activity从onPause返回到onResume,在它的onPause方法再次被调用之前不会被kill。

在表上表明的理论上不可能被kill的Activity,在极端(没有任何其他资源可用)的情况下仍然是可以被kill的。关于此部分更多内容参见Processes and Threading文档。

Saving activity state

在Managing the Activity Lifecycle中我们简略提到了当Activity Paused或Stopped时,Activity保存状态的一些知识。当Activity Paused或Stopped的时候,因为Activity对象还存在于Memory中,所以所有信息(成员变量和当前状态)都是存在的。因此,所有用户在此Activity内的改变都被保存下来,当此Activity再回到前台(resume foreground)时,这些改变依然存在。

然而,当系统为了回收memory而destroy一个Activity时,这个Activity对象会被销毁。系统将不能以完整的状态resume到这个Activity,当用户再次回到这Activity时,系统必须重新创建一个Activity对象,然而对于用户来说,他不知道系统销毁并重新创建了Activity,所以他们期待Activity正确的工作。在这种情况下,你要确保Activity的一些重要信息被保存下来,通过实现另外一个回调方法(onSaveInstanceState())我们可以保存这些状态信息。

系统会在Activity变得“容易”(见附录一)遭受毁坏之前调用onSaveInstanceState方法。系统会传一个Bundle的参数给这个方法,你可以使用putString或putInt方法以name-value的方式保存Activity的状态到这个Bundle中。这样,当系统kill你的Application process而用户重新启动这个Activity时,系统会在重新创建这个Activity的同时把这个Bundle传给onCreate和onRestoreInstanceState。使用其中任一个,你都可以从Bundle中解析出你保存的状态数据来恢复你的Activity。如果没有任何信息要被恢复,此Bundle为null,此时Activity就和第一次create一样被创建起来。

注意:在你的Activity被destroyed之前onSaveInstanceState不是一定会被执行的,因为有一些情况下没有必要保存状态(比如用户直接按Back键退出Activity,因为用户明确关闭Activity而不会保存状态)。如果系统调用了onSaveInstanceState,它可能是在onStop甚至onPause之前。

需要强调的是,就算你没有实现onSaveInstanceState或是在此方法中没有做什么保存状态的工作,默认实现的onSaveInstanceState也还是可以恢复Activity的一些状态。默认onSaveInstanceState方法会调用Layout中每个View相应的onSaveInstanceState方法,它允许每个View自己保存一些需要保存的信息。Android框架中几乎每个widget都适当实现了onSaveInstanceState方法。比如,当你的Activity被销毁而重新create时,UI的可视变化会自动保存并恢复。EditText会保存用户输入的text,CheckBox会保存它是否被checked的状态。当然你必须给这个widget赋予唯一的ID(使用android:id属性)。如果widget没有ID,系统将不会保存它的状态。

虽然默认的onSaveInstanceState方法为你的Activity UI保存了一些有用的信息,你还是需要override它来保存更多的信息。比如,你可能需要保存一些Activity的变化的成员变量值(与UI恢复相关但不会被默认保存的值)。

因为默认的onSaveInstanceState方法帮助我们保存了UI的状态,如果你override这个方法来保存更多信息,你应用总是在做你的工作之前调用父类的onSaveInstanceState。同样的,在你恢复状态之前,你也应该先调用父类的onRestoreInstanceState。

注意:因为onSaveInstanceState是不保证一定会执行的,所以你应该只是用来保存一些Activity的瞬时状态数据(UI状态)。你绝对不应该用它来保存持久型数据,而应该使用onPause来在用户离开Activity时保存持久数据(比如一些应该写入数据库的数据)。

一个好而简单的测试你的Application恢复状态的能力的方式是转动你的屏幕,使屏幕的orientation改变。当屏幕orientation改变时,系统会为新的屏幕configuration选择资源而销毁和重新创建Activity。仅仅出于这个原因,你的Activity在重新create时完整恢复状态都是相当重要的,因为用户在使用你的Application时可能经常的rotate屏幕。

Handling configuration changes

一些设备配置(device configuration)可以在运行时改变(比如屏幕orientation,键盘可用性,语言等)。当此情况发生时,Android会重新创建当前正在运行的Activity(系统调用onDestroy,然后直接调用onCreate)。这种设计方式是为了帮助你的Application通过自动重新载入资源(比如不同的Layout或屏幕orientation、大小等)来适应新的configuration。

当你正确的设计你的Activity来处理由于屏幕orientation变化导致的重新启动,你的Application在面对生命周期中的突发事件时会更有弹性。

处理此类重启的最好方法是在你的Activity中使用onSaveInstanceState和onRestoreInstanceState/onCreate来保存和恢复状态。参见前一章节。

其他更多关于运行时Configuration变化的处理,参见Handling Runtime Changes章节。

Coordinating activities

当一个Activity启动另外一个Activity时,它们都经历了lifecycle的过渡。当第二个Activity被创建时第一个Activity Pause, Stop(不过,如果它还在后台可见,则不会Stop)。一些情况下这些Activity会共享保存在磁盘或其他地方的数据,特别需要注意的是当第二个Activity被Created之前第一个Activity并不完全Stopped。相反的,Stop第一个和Start第二个的过程是重叠的。

Lifecycle的回调顺序要明确定义,尤其是处于同一个process的Activity,一个启动另外一个时。如下是Activity A启动Activity B的操作流程:

  1. A的onPause方法执行。
  2. B的onCreate,onStart和onResume顺序执行。(Activity B取得用户focus)。
  3. 如果A不再可见,则执行A的onStop方法。

这种可预见的Lifecycle回调允许你管理AB的跳转过程。比如,当你必须在第一个Activity Stop时写数据库一边第二个Activity可以读到数据,你应该在onPause中写入数据库而不是onStop中。