activity是一个应用程序组件,它提供了一个屏幕,用户可以通过该屏幕进行交互以执行某些操作,例如拨打电话,拍照,发送电子邮件或查看地图。每个activity都有一个窗口,用于绘制其用户界面。窗口通常填满屏幕,但也可能比屏幕小,或者漂浮在其他窗口的顶部。
应用程序通常由多个彼此独立的activity组成。通常,应用程序中的一个activity被指定为“main”activity,该activity在首次启动应用程序时呈现给用户。然后,每个activity可以启动另一个activity以执行不同的操作。每次新活动start时,前一个活动都会stopped,但系统会将前一个activity保留在堆栈中(“回退栈”)。当一个新的活动start时,它会被push到回退栈顶,并取得用户的焦点。回退栈遵循基本的“后进先出”堆栈机制,因此,当用户完成当前活动并按下后退按钮时,它将从堆栈中弹出(并销毁)并恢复先前的活动。 (后台堆栈将在“任务和后台堆栈”文档中进行更多讨论。)
当activity因新activity启动而停止时,会通过activity的生命周期回调方法通知此状态更改。由于状态的变化,activity可能会收到多种回调方法 - 系统是否正在创建,停止,恢复或销毁它 - 每次回调都为您提供执行适合的特定工作的机会。例如,当activity停止时,您的activity应释放任何大型对象,例如网络或数据库连接。当activity恢复后,您可以重新获取必要的资源并恢复已中断的操作。这些状态转换都是activity生命周期的一部分。
本文档的其余部分讨论了如何构建和使用activity的基础知识,包括完整讨论activity生命周期的工作方式,以便您可以正确管理各种activity状态之间的转换。
要创建活动,您必须创建Activity的子类(或它的现有子类)。在子类中,您需要实现当活动在其生命周期的各个状态之间转换时系统调用的回调方法,例如在创建,停止,恢复或销毁活动时。两个最重要的回调方法是:
onCreate()
您必须实现此方法。系统在创建activity时调用此方法。在您创建时,您应该初始化activity的基本组成部分。最重要的是,您必须调用setContentView()来定义activity用户界面的布局。
onPause()
当用户焦点从您的activity离开时(并不意味着你的activity被销毁),系统第一时间会调用此方法。通常你需要在此方法中保存用户会话的状态(因为用户可能不在返回)。
您还应该使用其他几种生命周期回调方法,以便在activity之间提供流畅的用户体验,并处理导致您的activity被停止甚至销毁的意外中断。所有生命周期回调方法将在后面的“管理活动生命周期”一节中讨论。
activity的用户界面由从View类派生的视图对象层次结构提供。每个视图控制activity窗口中的特定矩形空间,并可响应用户交互。例如,视图可以是在用户触摸动作时启动动作的按钮。
Android提供了许多现成的视图,您可以使用它们来设计和组织您的布局。“Widgets”是为屏幕提供可视(和交互)元素的视图,例如按钮,文本字段,复选框或图像。“布局”是从ViewGroup派生的视图,为其子视图提供独特的布局模型,例如线性布局,网格布局或相对布局。您还可以将View和ViewGroup类(或现有子类)子类化,以创建自己的Widgets和布局,并将它们应用于您的activity布局。
使用视图定义布局的最常用方法是使用保存在应用程序资源中的XML布局文件。这样,您可以将用户界面的设计与定义activity行为的源代码分开维护。您可以使用setContentView()将布局设置为activity的UI,通过传递布局的资源ID。但是,您还可以在activity代码中创建新视图,并通过将新视图插入ViewGroup来构建视图层次结构,然后通过将根ViewGroup传递给setContentView()来使用该布局。
有关创建用户界面的信息,请参阅用户界面文档。
您必须在清单文件中声明您的activity,以便系统可以访问它。要声明您的activity,请打开您的清单文件并添加
...
...
您可以在此元素中包含其他几个属性,以定义属性例如activity的标签,activity的图标或用于设置activityUI样式的主题。android:name属性是唯一必需的属性 - 它指定activity的类名。发布应用程序后,不应更改此名称,因为如果这样做,您可能会破坏某些功能,例如应用程序快捷方式(阅读博客文章,无法更改的内容)。
请参阅
使用意图过滤器
如果您希望自己的应用程序是封闭的,并且不允许其他应用程序激活其activity,那么您不需要任何其他意图过滤器。只有一个活动应具有“Main”action和“启动器”类别,如上例所示。您不想其他应用程序访问的activity不应该设置intent过滤器,您可以使用显式意图自行启动它们(如下一节中所述)。
但是,如果您希望activity响应从其他应用程序(以及您自己的应用程序)提供的隐式意图,则必须为您的activity定义其他意图过滤器。对于您要响应的每种类型的意图,您必须包含
有关活动如何响应意图的更多信息,请参阅意图和意图过滤器文档。
您可以通过调用startActivity()来启动另一个activity,并向其传递一个描述您要启动的activity的Intent。intent指定要启动的确切activity或描述您要执行的操作类型(系统为您选择适当的activity,甚至可以来自不同的应用程序)。意图还可以携带少量数据以供启动的activity使用。
在您自己的应用程序中工作时,您通常需要简单地启动已知activity。您可以通过使用类名创建一个明确定义要启动的活动的intent来完成此操作。例如,以下是一个activity如何启动另一个名为SignInActivity的activity:
Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);
但是,您的应用程序可能还希望使用activity中的数据执行某些操作,例如发送电子邮件,短信或状态更新。在这种情况下,您的应用程序可能没有自己的activity来执行此类操作,因此您可以利用设备上其他应用程序提供的activity,这些activity可以为您执行操作。这是意图非常有价值的地方 - 您可以创建描述您要执行的操作的意图,并且系统从另一个应用程序启动相应的activity。如果有多个activity可以处理意图,那么用户可以选择使用哪个activity。例如,如果要允许用户发送电子邮件,可以创建以下意图:
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);
添加到intent中的EXTRA_EMAIL extra发送到的电子邮件地址的字符串数组。当电子邮件应用程序响应此意图时,它会读取extra中提供的字符串数组,并将它们放在电子邮件撰写表单的“to”字段中。在这种情况下,电子邮件应用程序的activity开始,当用户操作完成后,您的activity将被恢复。
有时,您可能希望从您启动的activity中接收结果。在这种情况下,通过调用startActivityForResult()(而不是startActivity())来启动活动。然后,要从后续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(data
参数)中返回的数据来处理activity返回的结果。
接下来,ContentResolver对content provider执行查询,该查询返回用于读取查询数据的Cursor。有关更多信息,请参阅content provider文档。
有关使用意图的更多信息,请参阅意图和意图过滤器文档。
您可以通过调用finish()方法来关闭activity。您还可以通过调用finishActivity()来关闭先前启动的某个activity。
注意:在大多数情况下,您不应使用这些方法显式结束activity。如以下有关activity生命周期的部分所述,Android系统会为您管理activity的生命周期,因此您无需结束自己的activity。
通过实现回调方法来管理activity的生命周期对于开发强大而灵活的应用程序至关重要。activity的生命周期直接受其与其他activity,其任务和后台堆栈的关联的影响。
活动可以基本上存在于三种状态:
Resumed
活动位于屏幕的前景并具有用户焦点。 (这种状态有时也被称为“running”。)
Paused
另一个activity是在前台并具有焦点,但这一个activity仍然可见。也就是说,另一个活动在这个activity的顶部,该activity部分透明或不覆盖整个屏幕。暂停的activity完全处于活动状态(Activity对象保留在内存中,它保留所有状态和成员信息,并保持附加到窗口管理器),但可以在极低内存情况下被系统杀死。
Stopped
该activity完全被另一个activity遮挡(activity现在位于“background”中)。已停止的activity仍处于活动状态(Activity对象保留在内存中,它维护所有状态和成员信息,但未附加到窗口管理器)。但是,它不再对用户可见,并且当其他地方需要内存时,它可能被系统杀死。
如果activity暂停或停止,系统可以通过请求结束它(调用其finish()方法)或简单地终止其进程来从内存中删除它。当活动再次打开时(在完成或杀死之后),必须全部创建它。
当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的整个生命周期。通过实现这些方法,您可以在活动生命周期中监视三个嵌套的循环:
图1说明了这些循环以及activity在状态之间可能采用的路径。矩形表示当activity在状态之间转换时可以实现的回调方法。
表1中列出了相同的生命周期回调方法,它们更详细地描述了每个回调方法,并在activity的整个生命周期中定位每个回调方法,包括系统是否可以在回调方法完成后结束activity。
表1.activity生命周期的回调方法的摘要。
标题为“Killable after?”的列表示系统是否可以在方法返回后随时终止托管activity的进程,而不会执行activity的其他代码。三种方法标记为“是”:( onPause(),onStop()和onDestroy())。因为onPause()是三者中的第一个,所以一旦创建了activity,如果系统必须在紧急情况下恢复内存,onPause()是在进程被杀死之前保证被调用的最后一个方法 ,然后可能不会调用onStop()和onDestroy()。因此,您应该使用onPause()将关键的持久数据(例如用户编辑)写入存储。但是,您应该选择在onPause()期间只保存必要信息,因为此方法中的任何缓慢过程都会影响过渡到下一个activity从而降低用户体验。
在Killable列中标记为“No”的方法可以保护托管activity的进程免于被杀死。因此,从onPause()返回到调用onResume()的时间,activity是可被杀死的。在再一次调用onPause()并返回之前,它不会再次被杀。
注意:表1中这个定义在技术上不“可杀”的activity可能仍然被系统杀死 - 但这只会在资源极端情况下发生。当一个活动是否可能被杀死时,将在“进程和线程”文档中进一步讨论。
管理activity生命周期的简介简要提到,当activity暂停或停止时,activity的状态将保留。因为Activity对象在暂停或停止时仍保留在内存中 - 有关其成员和当前状态的所有信息仍然存在。因此,保留用户在activity中所做的任何更改,以便当activity返回到前台时(当它“恢复”时),那些更改仍然存在。
但是,当系统销毁activity以恢复内存时,Activity对象将被销毁,因此系统不能简单地恢复它的状态。相反,如果用户导航回到它,系统必须重新创建Activity对象。然而,用户并不知道系统破坏了活动并重新创建了activity, thus,probably expects the activity to be exactly as it was.。在这种情况下,您可以通过实现一个额外的回调方法来确保保留有关activity状态的重要信息,该方法允许您保存有关activity状态的信息:onSaveInstanceState()。
在activity可能被销毁之前,系统会调用onSaveInstanceState(),系统将Bundle方法传递给该方法,在该方法中,您可以使用putString()和putInt()等方法将有关activity的状态信息以键值对形式保存。然后,如果系统终止您的应用程序进程后用户导航回您的activity,系统将重新创建activity并将Bundle传递给onCreate()和onRestoreInstanceState()。使用这些方法之一,您可以从Bundle中提取已保存的状态并恢复activity状态。如果没有要恢复的状态信息,则传递给您的Bundle为null(这是第一次创建活动时的情况)。
注意:无法保证在销毁activity之前一定调用onSaveInstanceState(),因为在某些情况下不需要保存状态(例如当用户使用“返回”按钮离开activity时,因为用户是明确关闭活动)。如果系统调用onSaveInstanceState(),它会在onStop()之前调用,也可能在onPause()之前执行。
但是,即使您不执行任何操作并且未实现onSaveInstanceState(),Activity类的默认实现onSaveInstanceState()也会恢复某些activity状态。具体来说,默认实现为布局中的每个View调用相应的onSaveInstanceState()方法,这允许每个视图提供有关保存的自身的信息。几乎Android框架中的每个控件都会适当地实现此方法,以便在重新创建活动时自动保存和恢复对UI的任何可见更改。例如,EditText控件保存用户输入的任何文本,CheckBox控件保存是否已选中。您需要的唯一工作是为要保存其状态的每个小部件提供唯一的ID(带有android:id属性)。如果控件没有ID,则系统无法保存其状态。
因为onSaveInstanceState()的默认实现有助于保存UI的状态,所以如果重写方法以保存其他状态信息,则应该在执行任何工作之前必须调用onSaveInstanceState()的超类实现。同样,如果覆盖它,也应该调用onRestoreInstanceState()的超类实现,因此默认实现可以恢复视图状态。
注意:因为无法保证调用onSaveInstanceState(),所以您应该仅使用它来记录activity的瞬态(UI的状态) - 您永远不应该使用它来存储持久数据。相反,您应该使用onPause()在用户离开activity时存储持久数据(例如应保存到数据库的数据)。
测试应用程序恢复其状态的能力的一种好方法是简单地旋转设备以使屏幕方向发生变化。当屏幕方向改变时,系统会销毁并重新创建activity,以便应用可能适用于新屏幕配置的备用资源。仅仅因为这个原因,您的activity在重新创建时完全恢复其状态非常重要,因为用户在使用应用程序时会定期旋转屏幕。
某些设备配置可能会在运行时更改(例如屏幕方向,键盘可用性和语言)。当发生这样的更改时,Android会重新创建正在运行的activity(系统调用onDestroy(),然后立即调用onCreate())。此行为旨在通过使用您提供的备用资源自动重新加载应用程序(例如,针对不同屏幕方向和大小的不同布局),帮助您的应用程序适应新配置。
如果您正确的设计activity以处理由于屏幕方向更改而导致的重新启动并按上述方式恢复activity状态,则您的应用程序将更具适应性对于activity生命周期中的其他意外事件。
处理此类重新启动的最佳方法是使用onSaveInstanceState()和onRestoreInstanceState()(或onCreate())保存和恢复activity状态,如上一节中所述。
有关在运行时发生的配置更改以及如何处理它们的更多信息,请阅读处理运行时更改指南。
当一个activity开始另一个activity时,它们都会经历生命周期变化。第一个activity暂停和停止(但是,如果它仍然在后台可见,它将不会停止),同时创建另一个activity。如果这些activity共享保存到光盘或其他地方的数据,则必须了解第一个活动在创建第二个活动之前并未完全停止。而是,启动第二个的过程与停止第一个过程同时发生。
生命周期回调的顺序是明确定义的,特别是当两个activity在同一个进程中而另一个正在启动另一个时。以下是activity A启动activity B时发生的操作顺序:
这种可预测的生命周期回调顺序可以让你您管理从一个activity 到另一个activity 的信息传递。例如,如果您必须在第一个activity 停止时写入数据库以便以下activity 可以读取它,那么您应该在onPause()期间而不是onStop()期间写入数据库。