Activity为Android应用程序的一个关键组成部分,它通常提供一个用户界面用来和用户交互以完成某个功能,比如拨号,拍照,发送电子邮件或者是浏览地图, 在移动设备上,Activity通常占据整个屏幕,但Android也支持部分屏幕或是浮动窗口。
一个Android应用通常由多个Activity构成,不同Activity之间采用低耦合度设计,其中某个Activity可以称为应用的“主Activity”,作为在用户单击应用图标时显示的初始界面。然后每个Activity都可以触发其它的Activity以往常某种功能。每当一个新Activity启动后,之前的Activity将处于“停止”状态,但是Android系统会继续保留之前的Activity的状态,这样就形成一个“Activity”栈结构(称为“Back Stack”)。新Activity启动后被Android系统推放到“Activity”栈的最前面并且获取用户焦点(响应按键,触摸事件等),这个“Activity”栈采用“后进先出”的栈机制,因此当用户完成当前Activity功能后,单击“回退”键,当前Activity从“Activity”栈退栈并被“销毁”,之前的Activity变为当前Activity并且恢复之前的状态。
当一个Activity由于有新的Activity启动转变到“停止”状态时,Android系统将通过Activity的生命周期回调函数来通知该Activity。根据Activity当前状态的不同,系统将触发Activity多个不同的生命周期回调函数—创建,停止,恢复,销毁等。通过回调函数可以为Activity的不同状态添加不同的处理方法,比如当Activity停止时,可以释放某些系统资源,比如网络,数据库连接等,而当恢复某个Activity时可以重新获取这些系统资源。
创建Activity
为了创建一个Activity,你必须从Activity或是Activity的某个子类派生一个新的Activity子类,在这个子类中,你必须实现Activity生命周期中的几个回调函数,比如Activity创建,恢复,停止及销毁时的回调方法。其中两个最重要的回调方法如下:
onCreate()Activity必须实现这个方法,Android系统在创建Activity时将调用该回调函数,在你的实现方法,你应该为Activity中使用到的关键部件做初始化,最重要的时,此时你可以调用setContentView()为Activity设置用户界面布局。
onPause()Android系统在用户将要离开你的Activity之前调用(尽管这不总是意味着该Activity将被销毁)。通常此时你需要完成保存数据的工作以便用户后面回到你的Activity以恢复之前的状态。
实现用户界面
Android用户界面是由View子类构造的层次结构来实现的,每个View控件代表Activity用户界面中某个矩形空间,通常可以响应用户事件,比如一个View控件可以为一个可以响应用户点击的按钮。
Android提供了很多内置的View控件,你可以直接用在UI布局中。“Widget(小组件)”为具有可视部分且通常支持用户交互的View控件,比如按钮,文本框,多选框或是图像。“Layout(布局)”为ViewGroup的子类,用来管理包含在其中的其它View控件的大小和位置,比如有线性布局,网格布局等,你也可以通过派生View或ViewGroup来创建自定义的小组件或是布局。
最常用的定义用户界面的方法是通过XML来描述,使用这种方法定义用户界面可以很好的实现UI和应用逻辑的分离。你可以通过setContentView()来为Activity设置UI布局。
除此之外,你也可以通过代码来创建用户界面。
在Manifest文件中申明Activity
你必须在Android应用的清单文件(AndroidManifest.xml)中申明Activity才可以在应用中使用该Activity。你可以通过打开Android应用的清单文件,然后添加一个<activity>元素作为<application>的子节点。比如:
<manifest ... >
<application ... >
<activity android:name=".ExampleActivity" />
...
</application ... >
...
</manifest >
除android:name 属性之外,你还可以为Activity添加其它一些属性定义,比如图标,显示主题等,android:name是唯一必须设置的属性,它定义了Activity的类名称,一旦定义最好不要修改,否则有可能破坏其它某些功能,比如应用的快捷方式等。
使用IntentFilter
在Android清单中也可以为Activity定义各种Intent Filter,这是通过<intent-filter>元素来定义的,定义Intent Filter的目的是为了描述应用的其它组件如何来启动该Activity。
当你使用Android SDK 开发环境创建新应用时,自动创建的Activity在Android清单文件中的定义自动包含了一个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> 的值为android.intent.action.MAIN 表示该Activity为应用的“主”入口点。而<category>的值为android.intent.category.LAUNCHER表示这个Activity 会显示在Android系统的应用程序列表中(用户可以通过单击应用图标启动该Activity)。
如果你不想让其它应用来启动你应用的某个Activity,则不要为该Activity定义任何Intent Filter,你可以在应用通过显式调用来启动不含任何IntentFilter的Activity,而其它应用则无法触发该Activity。
启动Activity
你可以调用startActivity()通过传入描述需启动的Activity的Intent参数来启动一个Activity。这个Intent可以明确指明需要启动的Activity或者说明你需要哪种功能的Activity(然后系统根据要求自动选择合适的Activity),Intent也可以传入少量数据给被启动的Activity。
在你自己的应用中,常见的是简单的启动一个已知的Activity,这可以通过构建一个明确定义需要启动Activity的Intent来实现。比如下面代码明确启动名为SignInActivity的Activity。
Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);
然后有时你的应用需要完成某种功能,比如发送Email,短消息或者是使用你Activity的数据来更新系统状态,在这种情况下,你的应用中可能并没有定义具有上述功能的Activity,那么你可以利用设备中其它应用中提供的Activity来完成这些功能。此时,Intent就显得非常有用,你可以创建一个Intent,在这个Intent中描述你想要完成的功能,然后让Android系统来决定启动合适的Activity。如果系统中有多个可以往常同样功能的Activity,系统可以让用户来选择使用那个Activity来完成该项任务。比如,你想让用户发送Email,你可以使用如下定义的Intent:
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);
由EXTRA_EMAIL 附加参数指定的为需要发送的Email的地址。当被启动的Email应用响应这个Intent时会读取这个Email地址,然后填到收件人部分。在这种情况下,当用户推出启动的Email应用后,你原先的应用将重新恢复运行。
启动Activity并等待Activity返回结果
有时当你启动另外一个Activity后,希望从启动的Activity获得返回结果,此时可以通过调用startActivityForResult()而不是前面的startActivity()来实现。为了获得由Activity返回的结果,你需要实现onActivityResult()回调函数。当启动的Activity完成后,将使用onActivityResult()回调函数经由Intent返回结果。
比如,你可能希望用户从通信录中选择一个联系人,然后你的应用可以对这个联系人的信息做些处理,下面代码示例可以帮助你完成所需功能:
这个例子显示了使用onActivityResult()处理Activity返回值的基本逻辑。条件语句中第一个判断是检查请求是否成功,成功的话,resultCode的值为RESULT_OK,第二个判断请求是否为已知的请求,本例使用和startActivityResult同样的参数值来和requestCode做比较。如果两个条件都满足,你就可以处理由Activity返回的数据了。本例使用ContentResolver 查询指定的联系人信息,ContentResolver用法可能参见后面的介绍。
终止一个Activity
你可以调用Activity的finish()方法结束该Activity。你也可以通过调用finishActivity () 来终止先前启动的Activity。
要注意的是,在大部分情况下,你不需要使用上面的方法来强制终止一个Activity的运行,而是由Android系统来管理这些Activity的生命周期。因此你无需调用finish来结束你的Activity,使用这些方法反而可能会影响最终的用户体验。
管理Activity的生命周期
通过实现Activity的生命周期的回调函数来管理Activity的生命周期是开发健壮,灵活的应用的关键。Activity的生命周期直接影响到与之关联的其它Activity,它所对应的任务以及“Activity”栈。
一个Activity可以存在下面三种关键的状态:
· Resumed该Activity处于前台并获取用户焦点(本状态通常称为“运行”)
· Paused此时有其它Activity处于前台并有用户焦点。但本Activity依然可见,也就是说,有其它可见的Activity覆盖在本Activity之上,而且上面的Activity为半透明或者没有占据整个屏幕。处于“Paused”状态的Activity依然是活动的(该Activity依然占据内存并保持其所有状态数据,并且仍由WindowsManager管理),在系统空闲内存极低时还是有可能被Android系统清除的。
· Stopped该Activity完全被其它Activity覆盖,此时该Activity在后台运行,处于“Stopped”状态的Activity也是活动的(该Activity依然占据内存并保持其所有状态数据但和WindowsManager分离开),本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生命周期的三个嵌套的循环。
· Actvity整个生命周期,它发生在onCreate()和onDestroy之间,在你的Activity的onCreate()方法中你应该一些“全局”状态的设置,并且在onDestroy()释放所有资源。例如,你的Activity中有一个在后台下载数据的线程,这个线程可以在onCreate()创建,而在onDestroy()中终止该线程。
· Activity可见生命周期,它发生在onStart()和onStop()之间。在这个过程中,用户可以看到这个Activity的用户界面并可以和它交互。比如,在新Activity启动后,当前Activity的onStop()被调用并且隐藏到后台。在这两个方法之间,你可以保持一些需要显示给用户的资源,比如,你可以在onStart()中注册一个BroadcastReceiver来监视一些可能影响到UI的变化(比如网络状态),而在onStop()中注销这个BroadcastReceiver。随着Activity由显示到隐藏的交替变化,系统可能会重复多次调用Activity的onStart()和onStop()方法。
· Activity的前台生命周期,它发生在onResume()和onPause()之间,在这段时间内,Activit处在所有其它Activity的前面并具有用户焦点。一个Activity可以多次在前后台之间切换。比如,在设备进入睡眠状态时或是显示对话框会调用Activity的onStop()方法。由于这个状态交替频繁,因此要避免在这两个方法中添加过多的操作以免影响不同Activity之间切换的速度而需要用户等待。
下图显示了这些循环和Activity的可能的状态变化图,矩形代表了可以重载的回调函数。
图1. Activity生命周期
下面的表格列出了上面所说的所有生命周期方法并给出较为详细的说明,并且给出当每个回调函数结束后系统是否可以终止这个Activity。
表2Activity 生命中周期回调函数汇总
回调方法 | 说明 | 可否终止 | 下个状态 | ||
创建Activity时调用,在这个方法中通常你做些静态配置—创建View,绑定List数据源等等。这个函数的传入参数包含了Activity之前的状态(如果之前保存的状态),这个方法后面总是跟着onStart()方法。 | 否 | onStart() | |||
| 在Activity停止之后再重新启动之前调用,总是紧跟着onStart()方法。 | 否 | onStart() | ||
在Activity刚要变为可见前调用。如果Activity转到前台,后面的方法为onResume(),如果隐藏到后台,则后面调用onStop() | 否 | onResume() | |||
| 在Activity就要可以和用户发生交互前调用。此时该Activity处于“Activity栈”的顶端,可以响应用户输入。后面总是跟着onPause()方法。 | 否 | onPause() | ||
在系统就要启动其它Activity之前调用。在这个方法典型的用法是保存一些尚未保存的数据,停止一下可能占用CPU资源比如动画等操作。这些方法必须很快完成,因为只有这些操作完成后后面的Activity才会启动。如果Activity转到前台,后面的方法为onResume(),如果隐藏到后台,则后面调用onStop() | 是 | onResume() | |||
在Activity不再可见时调用,这可能发生在Activity被销毁或是有其它Activity启动覆盖了整个屏幕时。如果之后Activity恢复到可见则调用onRestart(),如果Activity结束则调用onDestroy() | 是 | onRestart() | |||
在Activity被销毁之前调用,这是Activity最后一个可能接受到的通知。在Activity调用finish()或者系统需要内存临时销毁Activity会调用,你可以通过方法isFinishing()来区分这两种情况。 | 是 | 无 |
上表中列“可否终止”代表Android系统在该回调函数结束后是否而无需再执行Activity的任何代码就可以杀死运行该Activity的进程。三个方法后面标记为“是”(onPause(),onStop()和onDestroy())。因为onPause()排在三个方法的第一位,一旦创建Activity之后,在系统内存不够的紧急情况下可以杀死该进程,onPause()在Activity终止前肯定会被调用。而onStop()和onDestroy()却不一会被调用。因此你应该在onPause()中保存一些关键数据。然而你也应该在onPause()中有选择性的保存数据,这些因为耗时的操作会影响后续Activity的启动,从而影响用户体验。
保存Activity状态
前面介绍管理Activity生命周期上简要提到当Activity暂停或是停止时,Activity的状态会暂时保持。这是因为当Activity处于暂停或是停止状态时,Activity对象依然在内存中,关于这个Activity的所有信息和状态依旧是活动的。因此用户所做的对Activity的改变在Activity回到前台后,Activity仍旧能保持之前的状态。
然而,如果Android系统在系统内存不足时销毁了某个Activity,Activity对象不会继续保持着内存中,此时系统再恢复这个Activity到前台时就不能简单的恢复到Activity之前的状态了。如果用户需要重新回到这个Activity,Android系统是通过重新创建这个Activity的实例来实现的。对于最终用户来说,他并一定知道他回到的这个Activity是由系统重新创建的新的Activity对象,他一样希望这个Activity能够保持之前的状态。在这种情况下,你必须保证有关Activity一些重要的状态信息能够保存下来以便恢复,这可以通过实现另外一个回调函数onSaveInstanceState()来完成Activity状态的保存。
Android系统会在销毁Activity之前调用onSaveInstanceState()。系统传入Bundle参数,在这个参数中可以保存name-value对,你可以用来使用如 putString()或putInt()等方法保存Activity的状态。之后,如果系统销毁了你的Activity而用户又想回到你的Activity时,Android系统将重新创建你的Activity的实例并在onCreate()或onRestoreInstanceState()方法这传入这个Bundle参数。你可以在这两个方法中任选其一来恢复Activity之前的状态。如果没有可以恢复的状态,Bundle的值为Null(这种情况他通常为首次创建该Activity实例)。
图2. 保存/恢复Activity状态
要值得注意系统并不会完全保证在销毁Activity之前一定会调用onSaveInstanceState(),这是因为在某些情况下没有必要保存这些状态(比如用户使用“回退”键离开你的Activity,用户明确指明要退出你的Activity,此时也没有保存之前的状态),如果系统调用onSaveInstanceState(),它将在onStop()之前有时也在onPause()之前调用。
然而,即使你没有实现onSaveInstanceState(),某些Activity的状态也被Activity的缺省onSaveInstanceState()实现保存。尤其是这个缺省实现将会调用UI布局中每个View的onSaveInstanceState()方法,这允许每个View有机会保存一些需要保存的信息。几乎每个Android内置的UI小组件都实现了这个方法,比如一些UI变化会在Activity重建时自动保存和恢复。比如EditText(文本框)可以保持用户输入的文本。CheckBox可以保存之前的选择状态。唯一需要的工作是为这些UI小组件提供唯一的ID(使用android:id属性)。如果没有指定小组件的ID,这些UI组件的状态就不会自动保存和恢复。
尽管onSaveInstanceState()的缺省实现可以保持一些有用的UI信息,你可能还是需要重载这个函数以支持额外的信息保存。要记住这重载的方法中调用基类的方法以支持缺省的UI变化的自动保存和恢复。
测试你的应用是否支持保存Activity的状态的一个好方法是通过旋转设备从而使屏幕的显示方向发生变化。当屏幕显示方向发生变化时,系统将首先销毁然后重新创建Activity以便使用可能存在的新的资源来显示以适应新的屏幕配置变化。由此,在系统重新创建Activity完整的恢复Activity状态非常重要,用户可能不断的转动屏幕。
处理配置变化
某些设置实时改变一些系统配置(比如屏幕显示方向,键盘及语言),每当有这种配置上的变化时,Android系统将重新创建正在运行的Activity(系统将先调用onDestroy(),然后紧接着调用onCreate()方法),这样的设计可以帮助你的应用在这些配置发生变化时,可以根据新配置调用适合的资源(比如给不同哦屏幕显示方向使用不同的布局设计)。
如果你的Activiy具有正确的设计以适应由于比如显示方向变化引起的Activity重启,能够正确的保存和恢复Activity状态,你的应用将能更好适应一些生命周期中的突发事件。
实现上面所说的Activity重启的最好的方法时重之前介绍过的onSaveInstanceState()和onRestoreInstanceState(或onCreate)方法。
协调多个Activity
当一个Activity启动另外一个Activity时,这两个Activity都会发生生命周期状态的转变。第一个Activity会暂停而后停止(某些情况下,如果它在后台仍可见的话,不会转到停止状态)。而另外一个Activity会被创建。如果这些Activity需要共享一些保存在SD卡或是其它什么地方的数据,了解在创建好第二个Activity后第一个Activity有可能没有完全停止非常重要。这两个过程完全有可能重叠。
生命周期函数调用的顺序是定义好的,尤其是运行在同一个进程中的两个Activity,其中一个Activity启动另外一个Activity。下面为ActivityA启动 Activity B 后可能发生的操作顺序:
1. 执行Activity A的 onPause()方法。
2. 顺序执行Activity B的onCreate(),onStart()和onResume()方法(ActivityB获得用户焦点)
3. 如果之后,Activity A不可见,将执行Activity A的onStop()方法
这种可以事先预计的生命周期方法执行的顺序允许你有效的管理数据在不同Activity之间的传递。比如,你必须在第一个Activity停止前完成对数据库的写入以便后续的Activity可以从数据库中读取。因此你应该在onPause而不是onStop中完成上述操作。