Android 教程 翻译 1 Activities 活动

by Tonyfield

update: 2012.04.06 (初步完成)

原文链接 : http://developer.android.com/guide/topics/fundamentals/activities.html

文中名词翻译参见词汇表,欢迎交流指正。

活动 Activities

     一个活动( Activity )是一个应用程序组件,它提供了一个可视界面供用户与之互动以完成某些事情,如拨打电话,拍照,发送电子邮件,或查看地图。每项活动都被提供一个窗口以在其中绘制用户界面。通常的窗口填满整个屏幕,也可能比其它窗口小或浮动在其他屏幕上方。

     应用程序通常包含多个相互松散绑定的活动。通常一个应用程序中的活动被指定为“主”活动(Main Activities),它在应用首次开启时被呈现在用户面前。每个活动(Activities)可以启动另一个活动以执行不同的动作。每次一个新的活动启动,前一个活动被停止,而系统会将前一个活动保留在后端堆栈内(back stack)。一个新的活动启动时,它被推到后端堆栈顶部并获得用户焦点。后端堆栈遵守基本的“后进先出”机制,因此,当用户完成当前活动并按下“后退”按钮,当前活动被弹出堆栈(并销毁),然后恢复前一个活动。 (后端堆栈将在“任务和后端堆栈“(Task and Back stack)中详细讨论)。

     当一个活动由于一个新的活动开始而被停止,通过活动的生命周期回调方法,它将被获知这种状态的变化。一个活动可能接受的回调方法有多种,具体使用何种回调方法要根据活动(Activities)状态的变化,看系统是否正创建之,停止之,恢复之,或销毁之,每个回调方法给你机会执行适合当前状态的特定工作。例如,活动被停止时,应该释放所有大型对象,如网络或数据库连接。当活动恢复时,可以重新获得必要的资源和恢复被中断的行动。这些状态转换是所有的活动周期的一部分。

     本文件的其余部分将讨论如何建立和使用活动的基础,包括一个完整的关于活动周期如何工作的讨论,从而你可以妥善管理各种活动状态之间的转换。

创建活动

  要创建一个活动,你必须创建一个活动(或已有活动子类)的子类。该子类需要实现多个回调方法,当活动在生命周期各种状态间转换时,系统将调用这些回调方法,例如正在创建,停止,恢复或销毁活动时。两个最重要的回调方法是:

  onCreate()

  你必须实现这个方法。创建活动时系统会调用它。在你的实现中,应该初始化活动的重要组件。最重要的是,你必须在这里调用setContentView()来定义活动的用户界面布局。

  onPause()

  系统调用这个方法是用户正在离开你的活动的第一个指标(尽管它并不表示活动被销毁)。你通常应该在这里提交所有超出当前用户会话外应当保存的修改(如游戏中的等级装备数据)(因为用户可能不会回来 (指可能不经OnStop,OnDestroy而系统关闭))。

  还有其他多个生命周期回调方法供你使用以提供不同活动之间流畅的用户体验,以及处理导致活动停止甚至销毁的突发中断。所有的生命周期回调方法将在“管理活动生命周期”(Managing the Activity Lifecycle)的部分讨论。

实现用户界面

  活动的用户界面由视图类派生的层级视图对象提供。每个视图(View)控制活动窗口内特定的矩形空间,它可以响应用户交互行为。例如,一个视图可以是一个按钮,当用户触碰按钮,则启动某个动作。

  Android提供了一些现成的视图,你可以用它来设计和组织布局。 “部件”(Widgets)提供一个屏幕可视(可交互)的元素,如按钮(button),字段框(text field),复选框(checkbox),或只是一个图像。“布局”(Layout)是视图组(ViewGroup)的派生,它为其子视图(child views)提供一个独特的布局模式,如线性布局,网格布局,或相对布局。你还可以继承ViewViewGroup类(或其已有子类)来创建自己的部件和布局,并将其应用到您的活动布局(activity layout)。

  使用视图来定义一个布局的最常见方式,是应用程序资源中的一个XML布局文件。通过这种方式,你可以分别维护你的用户界面设计和定义了活动行为的源代码。你可以使用setContentView()设置该布局为活动的用户界面,传递布局的资源ID。然而,你也可以在活动代码中创建新的的视图,并通过在ViewGroup插入新Views来建立一个视图层次结构中,然后传递根ViewGroup给setContentView()来使用这种布局。

  有关创建用户界面的信息,请参阅用户界面(User Interface)文档 。

在Manifest文件中声明活动

  活动必须在manifest文件中声明,这样系统才能访问到该活动。为此,需要打开manifest文件,并将<activity>作为<application>子元素添加其中。例如:

<manifest ... >
  <application ... >
      <activity android:name=".ExampleActivity" />
      ...
  </application ... >
  ...
</manifest >

  在这个元素中你可以包含其他多个属性,如标签,图标,或UI主题风格。android:name是唯一必需的属性,它指定活动的类名。一旦你发布应用程序,就不应该改变这个名字,否则,可能会破坏部分功能,如应用程序的快捷方式,(请参阅博客文章, Things That Cannot Change )。

  清单中的有关声明您的活动的更多信息,请参阅元素的引用。

使用intent过滤器(intent-filter)

  一个<activity>元素也可以使用<intent-filter>元素来指定多种intent-filter,以声明其他应用程序组件如何激活它。

  当你使用Android SDK工具创建一个新的应用,根活动(stub activity)将自动包含intent过滤器,它表明活动响应“main”行为,并应归于“launer”类。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 >元素指定这一活动列于系统应用程序启动项中(允许用户启动这项活动)。

  如果你的应用程序被设计为自给自足的(self-contained),不让其他应用程序激活,那么你就不需要任何其他的intent过滤器。只要有一个活动含有“main”行动和“launcher”类就好,如前面的例子。对于不需要对其他应用程序有效的活动(activity),不应含有任何intent过滤器,你可以使用明确的intent启动它(在下一节讨论)。

  但是,如果您希望您的活动以应对隐含的intent是从其他应用程序(和自己)交付,那么你必须为你的活动定义额外的intent-filter。对于每一个你要响应的intent,<intent-filter>必须包括一个<action>元素,和一个可选的<category>元素和/或<data>元素。这些元素指出你的活动响应能的那类intents。

  了解更多活动能响应的intents信息,请参考Intent and Intent-Filters

启动一个活动

  你可以通过调用startActivity()来启动另一个活动,传递一个参数Intent来描述你要启动的活动。这个intent或是指出你要启动的确切活动,或是描述要执行动作的类型(这时系统将为您选择合适的活动,甚至可以是另一个不同的应用程序)。intent也可以附带少量数据,作为启动活动时的参数。

  在自己的应用程序内,经常需要简单地运行一个已知的活动。你可以通过使用类名创建一个intent明确定义你要启动的活动。例如,这里展示怎样启动一个名为SignInActivity的活动:

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

  然而,您的应用程序可能还需要使用活动中的数据执行一些动作,例如发送电子邮件文字信息,或更新状态。在这种情况下,你的应用程序无法利用其自身的活动来执行这些行动,所以你可以转而利用设备上的其他应用程序提供的活动,它能帮你执行需要的任务。这是intent真正价值所在,你可以创建一个intent描述需要执行的动作,系统将从其他应用程序中选择适当活动。如果有多个活动可以处理该intent,那么用户可以选择使用哪一个。例如,如果你想让用户发送电子邮件,你可以创建以下intent:

Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);
  EXTRA_EMAIL向intent额外补充一个表示电子邮件发送地址的字符串数组。当应用程序响应此intent,它读取额外的字符串数组,并把它们放在电子邮件构成的“to”域。这种情况下,电子邮件应用程序的活动启动, 原有的活动在 用户完成(发送电子邮件)后才恢复。

启动一个带返回值的活动

  有时你可能需要从开始的活动获得结果。这种情况下,调用startActivityForResult()启动活动(而不是startActivity())。为了接受后续活动的结果,需要实现onActivityResult()回调方法。后续活动完成后将返回一个Intent结果给onActivityResult()方法。例如,也许你期望用户选择他们的联系人,这样,你的活动可以使用该联系人信息。这里展示怎样创建这样的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()方法的基本逻辑。第一个判决条件检查请求是否成功,如果成功,ResultCode 将为 RESULT_OK,同时判断结果是否响应已知的请求( PICK_CONTACT_REQUEST ),在这种情况下,requestCode 匹配 startActivityForResult()发送的第二个参数。从那里,代码通过查询Intent返回的数据(数据参数)处理活动结果。在上面代码中,由getContentResolver()返回的ContentResolver类执行对Content Provider的查询,它返回一个游标(Cursor),游标(Cursor)允许我们读取查询数据。需要了解更多信息,请参阅Content Providers的文档。

   需了解更多Intents信息,请参考Intent and Intent-Filters

关闭活动

  你可以通过调用它的finish()方法关闭活动自身。你也可以调用finishActivity()单独关闭你之前开始的活动。注:在大多数情况下,你不应该显式调用这些方法来结束活动。在下面一节有关活动生命周期的讨论中,Android系统活动帮你管理活动生命周期,所以你不需要调用这些函数来结束自己的活动。调用这些方法可能无法实现预期的用户体验,产生不利影响,它只应在你绝对不想让用户返回到此活动时被调用。

管理活动的生命周期

   通过实现回调方法来管理活动生命周期,对于开发强大和灵活的应用程序至关重要。直接影响一个活动生命周期的因素有:其关联的其他活动它要执行的任务后端堆栈(back stack)。一个活动可以基本上存在三种状态:

恢复的 (Resumed)

  这项活动显示为屏幕前景,拥有用户焦点。 (该状态有时也被称为“运行中” ( running )。)

暂停的(Paused)

  另一个活动处于前台并拥有焦点,而本活动仍然可见。也就是说,另一个活动对于本活动是顶部可见的,本活动是部分透明的或不覆盖整个屏幕。暂停的活动是完全alive的(活动对象保留在内存中,它维护所有状态和成员信息,并保持和窗口管理器的关联),但在极低内存的情况下,会被系统完结。

停止的(Stopped)

  本活动被另一个活动完全遮住了(本活动处于“后台”)。停止的活动仍是alive的(活动对象保留在内存中,它保持状态和成员信息,但没有关联窗口管理器)。然而它不再是用户可见的,当其他地方需要内存时,它会被系统杀死(killed)。

  如果一个活动暂停或停止,系统可以通过从内存中删除它,要求它结束(调用它的finish()方法),或者干脆杀死(kill)其进程。当活动(在完结或死亡后)再次打开,它必须被从头至尾重新创建。

实现生命周期回调

  当活动在如上所述的不同状态间转换时,它可通过多种回调方法被通知状态的变化。所有的回调方法都是钩子(函数),当活动状态变化,你可以覆盖它们来完成适当的工作。以下活动的框架包括每一个基本的生命周期方法:
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.
	}
}

  注意如上面的例子所示,做任何工作之前,这些生命周期方法的实现必须首先调用父类的实现。

综上所述,这些方法定义一个活动的整个生命周期。通过实现这些方法,你可以监视活动生命周期的三个嵌套循环:

  • The foreground lifetime of an activity happens between the call to onResume() and the call to onPause(). During this time, the activity is in front of all other activities on screen and hasuser input focus. An activity can frequently transition in and out of the foreground—forexample, onPause() is called when the device goes to sleep orwhen a dialog appears. Because this state can transition often, the code in these two methods shouldbe fairly lightweight in order to avoid slow transitions that make the user wait.

  • 活动的整个存在期发生在对onCreate到OnDestroy()的调用之间。你的活动应在onCreate()进行“全局”状态设置(如定义布局),并在OnDestroy()中释放所有剩余资源。例如,如果你的活动有一个线程在后台运行,这个线程从网络上下载数据,它可以在onCreate()中创建该线程,然后在OnDestroy()中停止该线程。
  • 活动的可见存在期发生在对OnStart()到onStop()的调用之间。在此期间,用户在屏幕上可以看到活动并与其进行交互。例如,当一个新的活动启动,onStop()被调用,原活动不再可见了。这两种方法之间,你能维护资源以向用户显示该活动。例如,你能在OnStart()中注册一个BroadcastReceiver()以监察在用户界面发生的变化,当用户不再看到界面时,在onStop()中注销它。在活动的整个生命周期,随着活动对用户时而可见,时而不可见,系统可能多次调用onStart()onStop()
  • 活动的前景存在期发生在对onResume()到onPause()的调用之间。在此期间,该活动在屏幕上位于所有其他活动之前,拥有用户输入焦点。活动可以经常转换出入前景,例如,设备进入休眠状态或出现一个对话框时,onPause()被调用。因为这个状态时常被切换到,所以这两种方法中的代码应该设计得相当轻量级,以避免过渡缓慢而使用户等待。
   图1说明了活动可能在 状态之间行进的循环和路径。矩形代表了回调方法,当活动在状态之间过渡时,它们能实现你要完成的操作。


图1 活动的生命周期

    参考相同的生命周期回调方法都列于表1,介绍更多的细节和定位每一个活动的整个生命周期内的每一个回调方法,包括系统是否能杀死活动完成后的回调方法。表1。活动周期的回调方法的总结。方法说明后,可杀?未来OnCreate()中调用时,第一次被创建活动。这是你应该做的所有您正常的一组静态高达 - 创建视图,数据绑定到列表,依此类推。这种方法是通过一个Bundle对象包含活动的以前的状态,如果该状态被获取(见节约活动状态,后)。

表 1.  活动生命周期回调方法一览.

方法 描述 之后活动是否是可杀死的 下一状态
onCreate() 该方法在活动首次创建时被调用。你应该在此完成所有静态设置 — 创建视图,将数据绑定到列表,等等 。该方法被传给Bundle对象,当活动前一状态被捕获,则它包含活动前一状态 (参考后面保存活动状态 章节)。

 onStart()总会随后被调用

onStart()
onRestart() 该方法在活动中止后并又再次启动前被调用。

onStart()总会随后被调用。

onStart()
onStart() 活动对用户可见之前被调用。

如果活动出现在前台,onResume()随后被调用,如果活动变为隐藏的则onStop()随后被调用。

onResume()
onStop()
onResume() 在活动刚开始和用户交互之前被调用。这时活动在活动堆栈顶部,用户输入指向该活动。

 onPause()总会随后被调用

onPause()
onPause() 在系统即将开始重启另一个活动时被调用。该方法一般被使用于将未保存的改动提交为永久数据,停止动画以及其他可能消耗CPU的事情,诸如此类。无论执行什么任务,它都应该尽可能快速,因为下一个活动黄在这个方法返回后才被恢复。

当该活动回到前台,onResume()随后被调用,如果它对用户不可见了,onStop()随后被调用。

onResume()
onStop()
onStop() 当该活动对用户不再可见了,该方法被调用。这可能是因为它被销毁了,或是另一个活动 (现存的或新的活动) 已被恢复并覆盖了它(译注:使该活动不可见)。

当该活动重新和用户交互,onRestart()随后被调用;如果该活动消失onDestroy()随后被调用。

onRestart()

onDestroy()
onDestroy() 该方法在活动被销毁前被调用。这是活动收到的最后的调用方法。它的调用要么是因为活动正在结束中 (某人在活动内部调用了finish()),或是因为系统正临时销毁该活动的实例以节省空间。你能用isFinishing() 方法分辨这两种情况。

    标记为 "之后活动是否是可杀死的(killable)" 的列指出在方法返回后,系统是否能在任意时间杀死(kill)活动的宿主进程,而不会执行又一行活动代码。这一列有三个方法被标记为“": (onPause()onStop(),和onDestroy())。因为onPause() 是其中第一个,所以,活动建立以后,onPause() 是在进程被杀死(killed)前能保证被运行的最后一个方法—如果系统在紧急情况下必须恢复内存,则onStop()onDestroy() 可能不会被调用到。所以你应该使用onPause() 来存储关键永久数据 (如用户编辑内容) 。但是,对于何种数据必须在onPause()中被保留应该是有选择的,因为在此方法中的任何程序阻塞会阻塞向下一活动的转换,这样会使用户体验速度变慢 。

   在“之后活动是否是可杀死的(killable)"”列标记为 "" 的状态会保护活动的宿主进程,从他们被调用时刻开始免于杀死(killed)。由此,一个活动在onPause() 返回后到onResume()调用前都是可杀死的(killable)。onResume()开始之后它将不再是可杀死的(killable)直到onPause() 被再次调用及返回。

注意: 活动在技术层面上并不是像表一所定义的那样在某些时刻是"不可被杀死的(killable)",它仍可能被系统杀死(killed)— 但这只会发生在缺乏其他资源的极端环境下。进程和线程 文档会详细讨论何时活动可能被杀死(killed)

保存活动状态

  对 管理活动生命周期 的介绍简单提及当活动被暂停或中止时活动状态被保留。确实,当活动暂停或中止,其对象仍保留在内存中 — 其成员和当前状态的全部信息依然是alive的。于是,活动中用户做出的任何改变都被保留下来,以便当活动返回前景时(当其“恢复”),那些变化依然在那儿。

  然而,当系统为了恢复内存而销毁一个活动,该活动 对象随之销毁,所以系统无法简单地将其恢复如初。相反,如果用户回到这个活动,系统必须重建该活动 对象。用户还不知道系统已经销毁并重建了这个活动,这样的话,用户可能期望活动和之前状态一致。在这种情况下,通过实现一个附加的回调方法,你能确保有关活动状态的重要信息被保存,这个回调方法就是onSaveInstanceState()

  在活动变得容易受破坏之前,系统要调用 onSaveInstanceState()。系统传递给它一个Bundle 参数,其中,你能使用像putString()putInt()这样的函数,按名称-取值组合(name-value pairs)保存活动的状态信息。于是,如果系统杀死你的应用程序进程,而用户返回到你的活动时,系统重建活动并将Bundle 传递给onCreate()onRestoreInstanceState()。使用这些方法的任何一个,你能从Bundle提取你保存的状态并恢复该活动状态。如果没有保存的活动信息,那么,传递给你的Bundle 是 null 值 (那是活动首次被创建时的情况).

图 2. 活动原封不动返回到用户焦点的两种方法:其一活动被销毁并重建,活动必须恢复之前保存的状态,或者活动被中止并恢复,活动状态保持原样。

注意: 无法保证 onSaveInstanceState() 在你的活动被销毁前一定会被调用到,因为存在某些情况下不必要保存状态(比如当用户按返回按钮明确离开并关闭活动)。系统调用onSaveInstanceState()的时间会在onStop() 之前,也有可能在onPause()之前。

  但是,即使你什么都不做,也不实现onSaveInstanceState(),某些活动状态也会被活动 类的缺省onSaveInstanceState()实现恢复。特别是,对于布局中的每个视图,缺省实现会调用对应的onSaveInstanceState() 方法,这使每个视图提供应被保存的自身信息。几乎Android框架中的每个部件都恰当地实现了这个方法,以至于任何对UI可见的改变都被自动保存,并在活动重建时被恢复。例如,EditText 部件保存任何用户输入文本,CheckBox 部件保存其是否被选中。你要做的工作仅仅是为每个需要保存状态的部件提供唯一的ID (使用android:id 属性)。如果一个部件没有 ID,那么系统无法保存其状态

  虽然 onSaveInstanceState() 的缺省实现保存关于活动UI有用的信息,你还是可能需要覆盖它来保存附加信息。例如你可能需要保存在活动存在期改变的成员变量值 (它可能和UI恢复的值相关,但缺省情况下保持那些UI值的成员没有被恢复)。

  因为 onSaveInstanceState() 的缺省实现有助于保存UI状态,如果你为了保存附加状态信息覆盖onSaveInstanceState()方法,在任何工作前,应该始终先调用父类的实现。类似地,在覆盖onRestoreInstanceState() 时也应该始终调用其父类实现,这样缺省实现会恢复各视图状态。

注意: 因为 onSaveInstanceState() 无法保证被调用到,你应该仅在记录活动的瞬间状态时使用它(UI状态) — 不应该用它保存持久性数据。相反,当用户离开活动,你应该使用onPause() 来保存持久性数据 (例如应该保存在数据库的数据) 。

  测试应用程序状态恢复能力有一个好的方法,就是简单旋转设备使屏幕方向变化。当屏幕方向变化时,为了转换资源以应对新的屏幕配置,系统会销毁和重建活动。单单因为这个原因,你的活动在重建时能完全恢复其状态就很重要,因为用户在使用应用程序时常会旋转屏幕。

配置改变处理

  一些设备配置会在运行时改变 (例如屏幕旋转,键盘有效性和语言)。当那样的改变发生时,Android重建和运行活动 (系统调用onDestroy(),并立即调用onCreate())。通过自动用不同资源重载你的应用程序,这种行为被设计来帮助你的应用程序适应新的配置 (例如对应于不同屏幕方向和尺寸的不同布局)。

  如果你正确设计你的活动来处理一次屏幕方向变化带来的重启,并如上所述恢复活动状态,在面对其他活动生命周期中无法预见的事件时,你的应用程序将更具适应力。

  如前面一节讨论的,处理那样的重启最好的方法是使用onSaveInstanceState()onRestoreInstanceState() (或是onCreate())保存和恢复活动状态。

  更多关于运行时配置变化及如何处理的信息,请参阅 处理运行时变化。

协调活动

 当一个活动启动另一个活动,他们都经历生命周期事务。当另一个活动被创建,前一个活动就暂停和停止( 虽然如果它在后台仍可见它将不会停止)。如果这些活动共享保存在磁盘或其他介质上的数据,那么在第二个活动被创建之前,前一个活动不会完全被终止,理解这一点很重要。Rather, the process of starting the second one overlaps with the process of stopping the firstone.

  生命周期回调方法的顺序有明确的定义,特别当两个活动在同一进程中,其中一个启动另一个。这儿给出当活动A启动活动B时的操作顺序:

  1. 活动 A的 onPause() 方法执行。
  2. 活动 B的 onCreate(),onStart()和onResume()方法顺序执行。 (活动B现在获得用户焦点。)
  3. 于是,如果活动 A 不再是屏幕可见,其 onStop() 方法执行。

   明确的生命周期回调序列允许我们管理一个活动到另一个活动的信息转换。例如,如果你必须在第一个活动停止时写数据库,那么下一个活动将读取这些数据,那么你应该在onPause() 中写数据库,而不是onStop()


email: [email protected]



你可能感兴趣的:(数据库,android,UI,活动,application,transition)