Activity详解
Activity是应用程序的组件,它提供了一个用户可与之互动做一些事情的屏幕,如拨打电话,拍照,发送电子邮件,查看地图。每个Activity 会提供一个窗口,在其中绘制它的用户界面。通常窗口会填满整个屏幕,但也有可能比屏幕小并且浮动在其他窗口之上。
应用程序通常是多个松散并相互绑定的Activity组成。一般,用户首次启动应用时,将启动一个被指定为“main”的Activity。每个 Activity都可以启动另一个Activity,以执行不同的动作。每次启动一个新的Activity,以前的Activity停止,但在系统堆栈保留Activity(“回栈”)。一个新的Activity启动时,它将Activity压到栈里面并取得用户的焦点。回堆栈遵守基本的“后进先出”的堆栈机制,这样,当用户在前Activity,按下返回按钮,则弹出堆栈(并销毁)和恢复以前的Activity。
Activity停止是因为一个新的Activity开始,通过活动的生命周期回调方法,通知这种状态发送变化。一个Activity可能会收到多个回调方法,当状态发生变化时,产生,停止,恢复,销毁,这些回调在适当的时机提供给您做相应工作的机会。例如,停止时,你的Activity应该释放大型对象,如网络或数据库连接。恢复Activity时,你可以重新获得必要的资源和恢复被中断的Activity。这些状态转换是所有的Activity 程序周期的一部分。
本文件的其余部分将讨论如何建立和使用Activity,包括讨论一个完整的Activity程序周期是如何工作,你能正确的管理各种Activity状态之间的转换。
Creating an Activity
要生成一个Activity,你必须生成一个Activity 子类(或已有的子类)。在你的子类中,创建,停止,恢复或销毁Acitivty时,你需要实现回调方法,系统调用时,用这些方法在Activity生命周期的各种状态之间的转换。两个最重要的回调方法是:
你必须实现这个方法。生成Activity时系统调用。在你实现中,你应该初始化您的Activity中的组件。最重要的是,这是你必须调用 setContentView()定义Activity的用户界面的布局。
当用户离开你的Activity时候(尽管它并不总是意味着被销毁Activity),系统调用此方法。通常如果你需要保持当前会话的话,你所有的改动都应在这提交(因为用户可能不返回)。
实现用户界面-Implementing a user interface
一个Activity的用户界面是由一组按层派生View的视图对象组成。每个视图控制Activity窗口的特定矩形空间,以响应用户交互。例如,一个视图可能是一个按钮,当用户触摸它启动一个操作。
Android提供了一些现成的视图,你可以用它来设计和组织布局。“Widgets”就是视图,他提供一个可视的可交互的屏幕元素,如按钮,文本字段,复选框,或只是一个图象。“布局”是从ViewGroup派生的,提供了对子视图特殊的布局模式,如线性布局,网格布局,或相对布局的视图组。还可以继承 View 和 ViewGroup (或现有的子类)来创建自己的Widgets和布局,并将其应用到您的Activity布局中。
最常见的方式来定义一个布局的意见,是一个XML布局文件保存您的应用程序资源。这种方式,你可以单独从源代码中维护你的用户界面设计以及定义Activity的行为。你可以通过setContenView()设置您的Activity的UI布局 ,传入布局的资源ID。或者,你也可以在Activity代码中生成新的View,新建立一个View层次结构插入到 ViewGroup,然后传入根ViewGroup 给setContentView() 进行布局 。
在清单文件中声明Activity - Declaring the activity in the manifest
<manifest ... > <application ... > <activity android:name=".ExampleActivity" /> ... </application ... > ... </manifest >
在这个元素可以包含其他一些属性,包括定义属性,如Activity的标签,图标的Activity,或Activity的主题UI风格。属性android:name是唯一需要的属性,它指定Activity的类名。一旦你发布你的应用程序,你不应该改变这个名字,因为如果这样做,你可能会破坏一些功能,如应用程序的快捷方式。
使用intent过滤器-Using intent filters
一个 元素也可使用intent-filter的元素指定的各种 intent过滤器,以说明其他应用程序组件可以如何激活它。
当你使用Android SDK工具为您创建一个新的应用,标签下自动包括用于声明Activity响应“main”的动作的intent过滤器和用于启动的“lancher”分类。intent 过滤器看起来像这样:
<activity android:name"@drawable/app_icon"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
元素指定应用程序“main”的入口。元素指定系统的activity列表上应用启动的Activity,(允许用户启动这个Activity)。
如果你打算让您的应用程序成为一个独立的应用,不允许其他应用程序启动应用中的Activity,那你就不需要其他的intent过滤器。如前面示例所示,只有一个Activity带有"main"动作和"launcher"类别。你不让其他Activity用于其他应用程序,那他们不应有任何intent过滤器,你必须显示的调用intent启动他们。
但是,如果您希望您的Activity响应其他应用程序(和自己)的隐式intent,那么你必须在您的Activity中定义额外的intent过滤器。为了响应你需要响应的每种intent,你必须包括,其中包括一个元素的和一个可选的元素和/或元素。这些元素指定你的Activity可以响应的intent类型。
启动Activity-Starting an Activity
你就可以通过调用 startActivity(android.content.Intent) 启动另一个Activity,它传递一个 Intent 描述你要启动的Activity。intent指定你要启动的Activity,或描述要执行的动作类型(系统为您选择合适的Activity,甚至可能是他应用程序的Activity)。intent也可携带少量数据被用于要启动的Activity。
在自己的应用程序中,你会经常需要简单地启动一个已知的Activity。你可以使用类名来创建一个intent,明确定义你要启动的Activity。例如,如下是一个Activity的启动另一个命名为 SignInActivity 的 Activity:
Intent intent = new Intent(this, SignInActivity.class); startActivity(intent);
另外,您的应用程序可能还需要执行一些动作,例如发送电子邮件,文字信息,或状态更新,使用 Activity中的数据。在这种情况下,你的应用程序可能无法有其自身的Activity,执行这些行动,所以你可以转而利用设备上其他应用程序提供的 Activity为您执行。这是intent真正有价值的地方——你可以创建一个描述了要执行的一个动作的intent,系统从另一个应用程序启动的适当 Activity。如果有多个Activity可以处理的intent,那么用户可以选择使用哪一个。例如,如果你想允许用户发送电子邮件,你可以创建以下intent:
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);
EXTRA_EMAIL EXTRA_EMAIL 为发送电子邮件的邮件地址字符串数组,其充当intent的额外数据。当一个电子邮件应用程序响应此intent,它读取额外提供的字符串数组,并把它们放在程序的“to”地址栏。在这种情况下,应用程序启动电子邮件Activity,当用户完成后,恢复到你的Activity。
启动Activity 的返回结果-Starting an activity for a result
有时,您可能要取得你启动的Activity的结果。在这种情况下,使用Activity的 startActivityForResult()(而不是startActivity())。然后在后续实现了 onActivityResult()回调方法的Activity中取得结果。后续Activity完成后,它会返回一个intent给您onActivityResult()方法。
例如,也许你希望用户选择他们的联系人之一,便于你的Activity可以做一些与该联系人的信息相关的事情。下面是怎样生成这样的intent并处理结果:
private void pickContact() { / /根据CONTENT PROVIDER URI定义,建议一个用于选择联系人得 INTENT 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) { / /如果请求正常(OK)并且请求是PICK_CONTACT_REQUEST if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) { / /查询 content provider 取得联系人的名字 Cursor cursor = getContentResolver().query(data.getData(), new String[] {Contacts.DISPLAY_NAME}, null, null, null); if (cursor.moveToFirst()) { // 游标不为空 int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME); String name = cursor.getString(columnIndex); // 对选中名字的人进行处理... } } }
这个例子说明了使用 onActivityResult() 方法处理Activity的结果的基本逻辑。首要条件是检查请求是否成功和这个结果是响应是否已知,如果是,那么ResultCode会是 Activity.RESULT_OK ,在这种情况下,requestCode匹配startActivityForResult()的第二个参数 。从那起,代码在 intent 中处理取得Activity查询数据的返回结果的(data 参数)。
关闭Activity-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是在“background”)。停止Activity也仍然存在( Activity对象保留在内存中,它保持状态和成员信息,但不和窗口管理器有关联)。然而,它已不再是对用户可见,其他地方需要内存时,它可以被系统终止。
如果一项Activity被暂停或停止,该系统可以从内存中删除它,要求它结束(调用它的 finish() 方法),或者干脆杀死它的进程。Activity再次打开时(在被结束或被杀死),它必须创造所有的一切。
实现生命周期回调函数-Implementing the lifecycle callbacks
当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生命周期的三个嵌套循环:
- Activity的整个存在周期发生在 OnCreate() 调用和 OnDestroy() 调用之间。Activity调用 OnCreate() 执行“全局”状态设置(如定义布局),并调用 OnDestroy()释放所有剩余资源 。例如,如果Activity有一个线程在后台运行,从网络上下载数据,它可能会调用 OnCreate()创建该线程 ,然后由 OnDestroy()停止线程的 。
- Activity的可见周期发生在 OnStart() 调用和 OnStop()调用之间。在这段时间内,用户可以看到屏幕上的Activity,并与它进行交互。例如,一个新的Activity时启动时,OnStop()被调用,这时Activity不再可见。这两种方法之间,你可以维持运行Activity所需要的资源,提供给用户。例如,你可以在调用'OnStart() 注册 BroadcastReceiver 监测影响你用户界面的变化,当用户可以不再看到你内容时,在OnStop()时注销。在整个存在周期的Activity,Activity在可见和不可见的变化中,系统可能会多次调用OnStart() 和 OnStop () 。
- Activity的前台周期发生在onResume()调用和 onPause()调用之间。在这段时间内,该Activity显示在屏幕上,在所有其他Activity之前,具有用户输入焦点。一个Activity经常在前台后台之间转换,例如,当设备进入睡眠状态,或弹出一个对话框是 onPause() 被称调用。因为这种状态转换,在这两种方法中的代码应该是相当轻量级的,以避免缓慢的转换,使用户等待。
- Activity周期的回调方法的总结。
Activity创建后,onPause()是在系统能kill进程之前保证能背调用到得方法——如果系统在紧急情况下必须恢复内存,之后onStop() 和 onDestroy()可能不会被调用,因此,你应该使用 onPause() 写入些持久性数据(如存储用户正在编辑的数据)。然而,对于要保存的数据,你应该有选择性 ,因为任何阻塞方法将阻塞系统转到下一个Activity,降低了用户体验。
“之后是否可Kill?”列中被标记为“否”的方法,表示此时系统会保护Activity的主进程不被kill。因此,Activity在从 onPause()返回到 onResume()被调用的这段时间是可被kill的。在下次 onPause() 被调用并返回之前,将不能被kill。
注:对于Activity,技术上来说并不是“可kill”,Activity仍然可以被系统"kill",但仅在系统资源匮乏的极端情况下才会发生。Activity何时能被kill,更多信息在 进程和线程 - processes and threads 文档中讨论。
保存Activity状态-Saving activity state
在管理Activity生命周期中提到当Activity暂停或者停止的时候,会保留Activity状态,这是成立的,因为当他暂停或者停止的时候,内存中仍然有 Activity 对象,并且包含成员信息和当前的状态,因此,在用户模式下的任何对Activity的操作,在Activity返回到前台的时候将被还原("恢复状态")。
然而,系统为了回收资源而销毁 Activity,这时系统不能简单地恢复到之前的完整状态。相反,如果用户返回到Activity,系统必须重新创建 Activity 对象。此时,用户并不关心系统销毁Activity,并重新创建它。用户此时更关心Activity是否能恢复到所期望的状态。在这种情况下,要保证有关Activity状态的重要信息是通过实现一个叫做 onSaveInstanceState() 辅助回调函数来完成的,它允许你保存Activity的状态信息。
在Activity变得不太稳定的前,系统调用 onSaveInstanceState() 。该系统传递给其一个 Bundle,您可以在其中以名称-值对的形式保存为有关Activity的状态信息,如使用方法 putString() 和 putInt() 。然后,如果系统kill你的应用进程,当用户返回Activity,系统重建Activity时,系统会同时传给 onCreate()和 onRestoreInstanceState() Bundle 。使用这两种方法,你可以从 Bundle 提取到保存的状态信息来恢复Activity。如果没有恢复的状态信息,Bundle 传递的是空(首次创建Activity的情况)。
按照状态的完整性,Activity有两种方式返回到用户的焦点:要么Activity被销毁,然后重新创建Activity,必须要恢复以前保存的状态。或停止Activity,然后恢复,这时Activity保持状态的完整。
注:Activity被销毁之前不保证 onSaveInstanceState() 会调用,因为有情况下,它不需要保存状态(例如,当用户使用“后退“ 按钮离开Activity的时候,因为用户已明确关闭的Activity)。如果是系统调用 onSaveInstanceState() ,那么他会在 onStop()前也可能在 onPause() 前调用 。
然而,即使你什么也不做,也不实现 onSaveInstanceState() ,某些Activity状态恢复会使用默认的 onSaveInstanceState() 实现 。具体来说,默认实现对于布局中的每个view调用相应的onSaveInstanceState()方法,它允许每个视图提供保存自身的相应信息。几乎在Android框架每一个部件都会适当的实现此方法,这样,当重新创建Activity时候任何UI的变化将自动保存和恢复 。例如, EditText 控件保存由用户输入和任何文字, CheckBox控件保存它是否检查。对于你要保存其状态每一个部件,你所需要做的唯一的工作是提供一个唯一的ID(使用android:id 属性)。一个组件,如果没有一个ID,系统无法保存其状态。
您还可以显式阻止布局视图保存其状态,通过设置 android:saveEnabled属性为“false”或调用setSaveEnabled() 方法。通常情况下,你不应该禁他,但如果您想使你的Activity恢复状态时有不同UI效果就启用它。
虽然默认onSaveInstanceState() 实现保存有关Activity UI的有用信息,你仍可能需要重写它,以保存更多的信息。例如,在Activity中你可能需要在成员变量值发生改变时保存他们。(可能在UI恢复时需要关联这些值,但默认情况下,拥有UI值的的成员都不会被还原)。
由于onSaveInstanceState()的默认实施能保存UI的状态,如果要为保存额外的状态信息而重写此方法,那么在做任何工作之前,你一定要在实现onSaveInstanceState()之前调用其超类方法。同样,如果你重写它,你也应该调用 onRestoreInstanceState()实现的超类方法,以此默认实现恢复视图状态。
注:因为 onSaveInstanceState()不能保证被调用,所以当用户离开Activity,你应该只使用它记录Activity的瞬时状态(UI的状态),你不应该用它来存储持久数据。相反,你应该使用 onPause() 存储持久数据(如保存到数据库中的数据)。
测试您的应用程序恢复其状态的能力的一个好方法是简单地旋转装置,使屏幕的方向变化。当屏幕方向的变化,系统的销毁并重新创建Activity,对于新的屏幕配置替换资源。因此,当被重建时,您的Activity完全恢复其状态时就非常重要了,因为用户经常在使用应用程序时旋转屏幕。
协调Activity-Coordinating activities
当一个Activity启动另外一个Activity时,他们都经历生命周期的转换。当创建其他Activity时,第一个Activity暂停和停止(不过,如果它仍然在后台可见,它不会停止。)。这些Activity共享保存在磁盘上的数据或其他内容,重要的是要了解在第二个Activity没有被建立之前,第一项Activity是不会完全停止的。而不是启动第二个Activity进程覆盖第一个Activity进程。
生命周期很好的定义了回调顺序,特别是在同一进程中两个Activity中,一个启动另外一个的时候。如下是当Activity A 启动Acivity B是产生的操作顺序:
1.ActivityA执行 onPause()方法。
2.ActivityB 按照 onCreate() , OnStart() , onResume() 的顺序执行方法。(ActivityB现在取得用户的焦点。)
3.然后,如果ActivityA 已不再是显示在屏幕上,它执行方法 onStop() 。
这个可预测的生命周期回调顺序,可让您管理的从一个Activity到另一个Activity的转换信息。例如,如果第一个Activity停止时你必须写数据库 , 让之后的Activity 读取数据库,那么你应该在 onPause()期间写数据库而不是onStop() 。