发现当前Android的资料不是很多,而且对于Activity的介绍也很少,所以把官方文档的android.app.Activity的介绍翻译了一下,加入了一些自己的理解。各位如果觉得我自己理解的不对,请无视。欢迎邮件讨论。
android.app
public class
android.app.Activity
android.app.ApplicationContext ViewInflate.Factory
android.app.Activity KeyEvent.Callback Window.Callback
Activity 是用户唯一可以看得到的东西。几乎所有的 activity 都与用户进行交互,所以 Activity 主要负责的就是创建显示窗口,你可以在这些窗口里使用 setContentView(View) 来显示你自己的 UI 。 activity 展现在用户面前的经常是全屏窗口,你也可以将 activity 作为浮动窗口来使用(使用设置了 windowIsFloating 的主题),或者嵌入到其他的 activity (使用 ActivityGroup )中。下面是两个几乎所有 Activity 的子类都实现了的方法。
onCreate(Bundle) 这个方法是初始化 activity 的地方 . 最重要的是,你经常需要在这里使用 setContentView(int) 来设置 UI 布局所使用的 layout 资源 , 当你需要使用程序控制 UI 中的组件时可以使用 findViewById(int) 来获得对应的视图。
当用户离开 activity 时你可以在 onPause() 进行相应的操作 . 更重要的是,用户做的任何改变都应该在该点上提交 ( 经常提交到 ContentProvider 这里保存数据 ) 。
如果要使用
Context.startActivity()
来启动
activity, activity
都必须在启动者应用包的
AndroidManifest.xml
文件中
有对应的
<activity>
定义。
Activity 类是 application's overall lifecycle 的一个重要部分。
这里涉及到的主题 :
Activity 生命周期
系统中的 Activity 可以通过一个 activity 栈来进行管理。当一个新的 activity 启动的时候,它首先会被放置在 activity 栈顶部并成为 running 状态的 activity —— 之前的 activity 也在 activity 栈中,但总是被保存在它的下边,只有当这个新的 activity 退出以后之前的 activity 才能重新回到前景界面。
所有的 activity 本质上有四种状态:
- activity 在屏幕的前景中( activity 栈的顶端),它是 active 或者 running 状态。
- activity 失去了焦点但是仍然可见(这个 activity 顶上遮挡了一个透明的或者非全屏的 activity ),它的状态是 paused 。一个 paused 状态的 activity 完全是 alive 的(它维护自己所有的状态和成员信息,而且仍然在 window manager 的管理中),但当系统内存极度贫乏时也会将其 killed 。
- activity 由于其他的 activity 而完全变暗,它就进入了 stopped 状态。它仍然保持着所有的状态和成员的信息,可是,他对于用户来说不可见,当别的地方需要内存的时候它经常会被 killed 。
- activity 是 paused 或者 stopped ,系统需要将其清理出内存的时可以命令其 finish 或者简单 kill 其进程。当它重新在用户面前显示的时候,它必须完全重新启动并且将其关闭之前的状态全部恢复回来。
下面的图表是 Activity 的状态图,直角矩形代表了 callback 方法,你可以实现这些方法从而使 Activity 在改变状态的时候执行你制定的操作。带颜色的椭圆形是 Activity 的主要状态。
这里有三个比较关键的生命周期。
- 从最初 调用 onCreate(Bundle) 到最终调用 onDestroy() 称为 完整生命周期 。 Activity 会在 onCreate() 进行所有“全局”状态的设置,在 onDestroy() 中释放所有持有的资源。举个例子,如果它有一个从网络上下载数据的后台线程,那他可能就会在 onCreate() 中创建这个线程并在 onDestroy() 中停止这个线程。
- 从 activity 调用 onStart() 开始,到调用对应的 onStop() 为止称为 可见生命周期 。在这段时间内用户可以在屏幕上看到这个 activity ,尽管并不一定是在前景也不一定可以和用户交互。在这两个方法之间你可以维护那些 activity 在用户显示时所需的资源。举个例子来说,你可以在 onStart() 中注册一个 IntentReceiver 来监控那些可以对你的 UI 产生影响的环境改变,当你的 UI 不继续在用户面前显示时你可以在 onStop() 中注销这个 IntentReceiver 。每当 activity 在用户面前显示或者隐藏时都会调用相应的方法,所以 onStart() 和 onStop() 方法在整个生命周期中可以多次被调用。
- 从 activity 调用 onResume() 开始,到调用对应的 onPause() 为止称为 前景生命周期 ,这段时间 activity 处于其他所有 activity 的前面,且与用户交互。一个 activity 可以经常在 resumed 和 paused 状态之间转换——例如手机进入休眠时、 activity 的结果返回时、新的 intent 到来时——所以这两个方法中的代码应该非常的简短。
下面的 Activity 方法定义了 activity 完整的生命周期。他们全都是 hook 方法,你可以重载这些方法从而使 activity 在状态改变时执行你所期望的操作。所有 activity 都应该实现自己的 onCreate(Bundle) 方法来进行初始化设置;大部分还应该实现 onPause() 方法提交数据的修改并且准备终止与用户的交互。尽管我们计划在系统中添加更多的工具来管理应用,现在大多 activity 仍需要实现 onFreeze() 并且在 onCreate(Bundle) 中执行对应的状态恢复。其他的方法可以在需要时进行实现,当实现这些方法的时候需要注意的是一定要调用父类中的对应方法。
public class Activity extends ApplicationContext {
protected void onCreate(Bundle icicle);
protected void onStart();
protected void onRestart();
protected void onResume();
protected void onFreeze(Bundle outIcicle);
protected void onPause();
protected void onStop();
protected void onDestroy();
}
一般来说 activity 的生命周期变化看起来比较象下面的表格:
(此处译者进行了大块的修改,请参考 原文 阅读下面表格)
方法 |
描述 |
Killable? |
下一方法 |
|
Activity 初次创建时被调用,你应该在这里进行一般的静态设置:创建 view 、将数据绑定到 list 等等。如果 activity 之前存在冻结状态,那么此状态将在 Bundle 中提供。 如果 activity 首次创建,本方法后将会调用 onStart() ,如果 activity 是停止后重新显示,则将调用 onRestart() 。 |
No |
|
||
|
当 activity 对用户即将可见的时候调用。 其后调用 onRestart() 或 onResume() ( 框架是否进行选择性调用 onResume() 仅仅是猜测 ) |
No |
|
|
当 activity 从停止状态重新启动时调用。其后调用 onResume() 。 |
No |
|
||
|
当 activity 将要与用户交互时调用此方法,此时 activity 在 activity 栈的栈顶,用户输入已经可以传递给它。 如果其他的 activity 在它的上方恢复显示,则将调用 onFreeze() 。 |
No |
|
|
当你的 activity 被暂停而其他的 activity 恢复与用户交互的时候这个方法会被调用(在其他 activity 显示之前),你可以使用这个方法保存你当前的用户状态(一般来说是当前实例的用户状态)。暂停之后,为了回收资源供给前景 activity ,系统会在需要的时间停止(或者 kill )你的应用。以后如果你的 activity 启动一个新的实例重新与用户进行交互,你保存在这里的状态都将通过 onCreate() 方法传递给新的实例。 其后总是调用 onPause() 方法。 |
No |
|
||
当系统要启动一个其他的 activity 时调用(其他的 activity 显示之前),这个方法被用来提交那些持久数据的改变、停止动画、和其他占用 CPU 资源的东西。由于下一个 activity 在这个方法返回之前不会 resumed ,所以实现这个方法时代码执行要尽可能快。 如果 activity 重新回到前景时将调用 onResume() , 如果对用户彻底不可见则会调用 onStop() 。 |
Yes |
|
||
当另外一个 activity 恢复并遮盖住此 activity, 导致其对用户不再可见时调用。一个新 activity 启动、其它 activity 被切换至前景、当前 activity 被销毁时都会发生这种场景。 当 activity 重新回到前景与用户交互时调用 onRestart() ,如果 activity 将退出则调用 onDestory() 。 |
Yes |
|
||
在你的 activity 被销毁前所调用的最后一个方法,当进程终止时会出现这种情况(对 activity 直接调用 finish() 方法或者系统为了节省空间而临时销毁此 activity 的实例,你可以通过 isFinishing() 的返回值来区分这两种情况)。 |
Yes |
nothing |
① : 这个表格本人觉得还有些值得商榷的地方,建议作为参考阅读,不管是原文还是译文。
注意上表中“ Killable ”这一列 —— 对于那些标记 killable 的方法,当这些方法结束后, activity 的进程可能在任何时间被系统 kill 而不再执行 activity 中的任何代码。因此你应该利用 onFreeze() (保存你当前 UI 的状态)和 onPause() (将所有的修改写回持久存储),这样 activity 才能在被 kill 的时候正确的保存当前的状态。如果需要了解一个进程的生命周期与他所执行的 activity 之间的关系 参见 进程生命周期 部分。
对于那些标记 killable 的方法,从这些方法启动开始直到返回之前, activity 的进程都不回被系统 kill 。举个例子,一个 activity 在 onPause() 方法返回后处于 killable 的状态,这种状态会一直持续到 onResume() 方法开始执行。
配置改变
如果设备的配置(在 Resources.Configuration 中进行了定义)发生改变,那么所有用户界面上的东西都需要进行更新,以适应新的配置。因为 Activity 是与用户交互的最主要的机制,它包含了处理配置改变的专门支持。
除非你特殊指定,否则当配置发生改变(比如屏幕方向、语言、输入设备等等的改变)时你当前的 activity 都将被销毁,这销毁是通过一个正常的 activity 生命周期过程( onFreeze(Bundle) , onPause() , onStop() , 和 onDestroy() )进行的。如果 activity 之前正在前景画面,当这个实例的 onDestroy() 调用完成后将会启动这个 activity 的一个新的实例,并将前面那个实例中 onFreeze(Bundle) 所保存的内容传递给新的实例。
因为任何的应用资源(包括 layout 文件)都有可能由于任何配置值而改变。因此处理配置改变的唯一安全的方法就是重新获取所有的资源,包括 layout 、绘图资源(原文 drawables )、字符串资源。由于 activity 已经如何保存自己的状态并从这些状态中重建自身,所以 activity 重新启动自身来获得新的配置将是一个非常便利的途径。
在一些特殊的情况中,你可能希望当一种或者多种配置改变时避免重新启动你的 activity 。你可以通过在 manifest 中设置 android:configChanges 属性来实现这点。你可以在这里声明 activity 可以处理的任何配置改变,当这些配置改变时不会重新启动 activity ,而会调用 activity 的 onConfigurationChanged(Resources.Configuration) 方法。如果改变的配置中包含了你所无法处理的配置(在 android:configChanges 并未声明),你的 activity 仍然要被重新启动,而 onConfigurationChanged(Resources.Configuration) 将不会被调用。
启动 Activity 并获得结果
startActivity(Intent) 方法可以用来启动一个新的 activity ,这个 activity 将被放置在 activity 栈的栈顶。这个方法只有一个参数 Intent ,这个参数描述了将被执行的 activity 。
有时候你希望在一个 activity 结束时得到它返回的结果。举个例子,你可能启动一个 activity 来让用户从通讯簿中选择一个人;当它结束的时候将会返回这个所选择的人。为了得到这个返回的信息,你可以使用 startSubActivity(Intent, int) 这个方法来启动新的 activity ,第二个整形参数将会作为这次调用的识别标记。这个 activity 返回的结果你可以通过 onActivityResult(int, int, String, Bundle) 方法来获得,此方法的第一个参数就是之前调用所使用的识别标记。
当
activity
退出的时候,它可以调用
setResult(int)
来将数据返回给他的父进程。这个方法必须提供一个结果码,这个结果码可以使标准结果
RESULT_CANCELED, RESULT_OK
,也可以是其他任何从
RESULT_FIRST_USER
开始的自定义值。此外,它还可以返回一段字符串(经常是一段数据的
URL
地址),一个包含它所有希望值的
Bundle
。这些信息都会在父
activity
的回调函数
Activity.onActivityResult()
中出现,并连同最初提供的识别标记一起(此处有些拗口,意思其实就是子
activity
返回的内容、返回码、识别标记都将作为参数,按照不同的返回情况来调用父
activity
的
Activity.onActivityResult()
方法,以实现出现各种返回时父
activity
做出响应的处理)。
如果子 activity 由于某种情况发生失败(例如 crashing ),父 activity 将会收到 RESULT_CANCELED 结果码。
这里是一个例子,说明了如何启动一个新的 activity 并处理结果。
public class CalendarActivity extends Activity {
...
static final int DAY_VIEW_MODE = 0;
static final int WEEK_VIEW_MODE = 1;
private SharedPreferences mPrefs;
private int mCurViewMode;
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
SharedPreferences mPrefs = getSharedPreferences();
mCurViewMode = mPrefs.getInt("view_mode" DAY_VIEW_MODE);
}
protected void onPause() {
super.onPause();
SharedPreferences.Editor ed = mPrefs.edit();
ed.putInt("view_mode", mCurViewMode);
ed.commit();
}
}
许可
你可以通过在 Activity 所属应用的 manifest 文件中对应的 <activity> 标签中进行
声明来限制哪些应用可以启动此 Activity 。如果你进行了声明,其它应用需要在他们
自己的 manifest 文件中声明对应的 <uses-permission> 元素( 而且需要在安装时
被授予许可:译者注 )才可以启动这个 activity 。
如果需要了解更多有关于一般安全机制和许可方面的信息可以参考 Security Model 。
进程生命周期
Android 系统会尽量久的保留应用进程。但是当内存降低时最终还是要移除旧的进程。就像 Activity 生命周期 中描述的一样,移除哪个进程还是要取决于关联的用户与之交互的程度。一般来说,进程可以基于其中运行的 activity 所处的生命周期而分成四种状态,下面将这些状态根据重要程度排列。系统会在 kill 更重要的进程(第一个)之前首先 kill 那些不那么重要的进程(最后一个)。
前景 activity (处于屏幕最上方,用户与之交互的 activity )被认为是最重要的。如果设备上的内存无法满足它的使用, Kill 此进程只能作为最后的手段。一般来说这个时候设备处于内存 paging 状态,为了
使用户界面保持响应才会发出这个 kill 请求。
可见 activity (一个对用户可见,但是不在前景的 activity ,比如处在浮动对话框后)被认为是非常重要的,除非为了保证前景 activity 运行,否则不会被 kill 。
后台 activity (一个对用户不可见,并处于 paused 状态的 activity )就不再重要了,所以当需要为其它前景的或者可见的 activity 运行而回收内存时系统可以很安全的 kill 它们。
空进程 是一个没有运行 activity 或者其他应用组件(比如 Service 或者 IntentReceiver 类)的进程。当内存开始降低时系统很快就会 kill 掉这些进程。因此当你要在 activity 外运行任何的后台操作时,必须在 IntentReceiver 或 Service 的上下文环境中运行,这样系统才知道需要将你的进程保留而不是 kill 。
有些时候 Activity 可能需要长时间运行一个操作,且它并不依赖于 activity 的生命周期而存在。例如一个照相机应用可能允许你将照片上传到 web 站点。上传可能需要很长时间,在上传过程中应该允许用户离开这个应用。为了做到这一点,你的 activity 应该在上传时启动一个 Service 来执行此工作。这将使系统在你的进程上传数据的过程中能够恰当的区分它的优先级(认为此进程比其他不可见应用更重要),不管原来的 activity 的状态是 paused 、 stopped 还是 finished 。
marshal.hird@gmail.com