关于Activity的粗略翻译
原地址:Activity
类概述:
Activity是独立的、突出的可被用户操作的东西。几乎所有的Activity都是与用户进行交互的,所以这些Activity会很小心的帮我们创建一个窗体(Window),而在这个窗体中,你可以通过调用 setContentView(View) 来设置你自己的界面(UI)。虽然Activities经常被用来作为全屏(full-screen)展示给用户,但是它们不仅可以这样,它们还可以用在其它方面:作为一个浮动的窗体来使用(通过在主题中设置 windowIsFloating 属性来实现),你还可以将一个Activity镶嵌在另一个Activity中(通过使用 ActivityGroup)。而在我们继承Activity时,几乎都会实现下面两种方法:
- onCreate(Bundle) :该方法就是用来初始化你的Activity的。然而更重要的是,在这个方法里面,通常会调用 setContentView(int) 方法,传入的参数就是一个你自己定义的布局资源文件(layout resource),然后使用 findViewById(int) 方法来在你刚刚设置的那个布局文件中查找对应的你需要的控件。
- onPasue(): 在该方法中你可以处理一些当用户离开当Activity后的操作。然而更重要的是,用户所进行的操作导致的新的数据变化都应该在这个方法中来保存。
为了在使用 Context.startActivity()方法能正常找到对应的Activity,所以所有的Activity都应该在对应包下的 AndroidManifest.xml 文件中通过使用 <activity>标签来定义。
Activity生命周期
Activity在系统中是通过Activity栈来管理的。当一个新的Activity开始时,这个新启动的Activity将会被放在栈的顶部,同时该Activity将会运行起来,而前一个Activity将会在当前Activity的下面(栈下面),并且在当前Activity退出前台之前,前一个不会出现在前台的。
本质上来说, 一个Activity中会有下面四种状态:
- 如果一个Activity在屏幕前台的话,便是运行状态。
- 如果一个Activity失去焦点但是仍可见(也就是说此时有一个非全屏的Activity弹出了,或者一个透明的Activity在前台),此时对应Activity就会处于暂停状态(paused)。此时Activity尽管是暂停状态,当它仍然是具有"活力的"(具有活力也就是说会保持所有的状态、记住对应信息,并且和Window Manager一直是还是绑定(attach)状态的),但是尽管是这样,它毕竟是暂停的,如果此时系统内存空间严重不足的时候还是会把暂停的这个Activity“杀掉”的。
- 如果一个Activity被另一Activity完全挡住时,它将会停止(stopped)。但是此时仍然会保持所有之前的状态、记住用户信息。然而,因为它不可见了嘛,所以与它对应的的窗体(window)便会隐藏,并且经常会在系统需要时被系统"杀掉"。
- 如果一个Activity处于暂停(paused)或者停止(stopped)状态时,系统会不加询问的从内存中终止该Activity,或者“杀掉”对应的进程。当该Activity将被再次显示到前台给用户时,该Activity必须重新启动并且完全恢复之前的状态。
下面的图解展示了一个Activity的一些重要状态的转换路径。其中矩形代表当Activity状态变化时的一些回调方法,你可以实现这些回调方法来执行一些操作。其中有色的椭圆是Activity当前的的主要状态。
下面将会介绍三种在Activity中你可能会感兴趣的主要的循环
- 在Activity中发生的全部的生命周期循环,这个循环过程是发生在 onCreate(Bundle)方法到最后的onDestroy()方法之间。一个Activity将在 onCreate(Bundle) 中来设置、初始化一些东西,然后最后在onDestroy()释放所有之前的资源,比如现在你有一个需求就是创建一个线程在后台工作,用来执行从网上下载数据的工作,这样的话你可以在 onCreate() 中来初始化创建这个线程,最后在 onDestroy() 中来停止这个线程。
- 可见的生命周期循环,这个循环过程是发生在 onStart() 方法到 onStop() 方法之间。在这段时间内,用户在屏幕上是可以看见这个Activity的,尽管有可能没有在前台与用户进行交互。在这两个方法中,你可以保持(我感觉应该是翻译成 初始化创建 会更好点)以那些用来给用户展示的资源。比如你可以在 onStart() 中注册一个 广播接收器 (BroadcastReceiver)来监听那些对界面产生影响的那些变化,然后在当用户不再能看到的界面时,即在 onStop() 中解除注册(unRegister)。值得注意的是, onStart()和onStop()方法可以被调用好多次,因为只要当Activity显示给用户和对用户隐藏而使用户看不到时就会调用这两个方法,明显在一个应用中这样的场景会多次发生的。
- 前台生命周期循环,这个循环发生在 onResume() 和 onPause() 方法之间。在这段时间内,当前Activity会在所有其他Activity的前面并且与用户进行交互。一个Activity可以频繁地调用这两个方法来恢复Activity和暂停Activity。比如当你的设备息屏开始休眠时,当一个Activity的结果送达时,当一个新的Intent到达时....因此在你重写实现了这两个方法时不应该再里面放掏过耗时的工作,而应该是一些"轻量级"的。
整个的Activity生命周期是通过下面代码中的方式来定义的。所有的这些方法都是hooks(原谅我不知道怎么把它翻译到这句中去 (∩_∩) ),你可以重写对应需要的方法,以实现在Activity状态变化时执行对应的、恰当的工作的目的。所有的Activities都将实现 onCreate(Buldle) 方法来初始化,有些也实现了 onPause() 方法来提交一些变化的数据,或者做一些准备工作来以停止和用户的交互。你应该在实现这些方法时记得调用一下父类的对应方法。
public class Activity extends ApplicationContext {
protected void onCreate(Bundle savedInstanceState); protected void onStart(); protected void onRestart(); protected void onResume(); protected void onPause(); protected void onStop(); protected void onDestroy();
}
通常情况下,在Activity生命周期的转变会类似于下面的方法顺序和介绍。
方法 | 描述 | 是否可以被系统杀掉? | 下一步执行 |
onCreate() | 当Activity第一次创建时会调用该方法,在该方法中,你应该做好全部的初始化设置工作:创建视图(View),绑定数据, 比如List的数据绑定。该方法还给我们提供了一个Bundle对象,这个Bundle中包括了上一次的状态信息,前提是上一次有储存数据到Bundle中。在该方法之后,经常会执行 onStart()。 | 不可以 | onStart() |
onRestart() | 当你当前的的Activity被停止(stopped)了,前一个Activity要重新启动了,此时会调用本方法。在此方法执行之后,通常会执行 onStart() 方法 | 不可以 | onStart() |
onStart() | 当Activity对于用户来说变得可见时,会调用该方法。如果Activity转到前台来显示时会紧跟着调用 onResume() 方法,如果它离开前台而被隐藏时会调用 onStop() (PS:感觉这里有问题,onStart()怎么能接着直接执行到的onStop()呢?) | 不可以 | onResume() 或者onStop() |
onResume() | 当Activity开始和用户产生交互时会调用该方法。在此时,该Activity已经在Activity栈的顶部了,开始可以处理来自用户的交互了。该方法后一般会执行 onPause() | 不可以 | onPause() |
onPasue() | 当系统准备将要启动前一个Activity时,会调用本方法。在本方法中,典型的做法就是在本方法中提交保存那些未保存的数据变化、停止动画、还有其他之类的,只要是坑了消耗占用CPU的都应该在本方法中停掉。在实现本方法时要注意:实现后的方法中不能有太过耗时的操作,应该越快越好,因为下一个Activity会在等着本方法返回(return)后才会恢复(resume)。在本方法执行完成后,如果本Activity又被返回到了前台,那么此时会跟着执行 onResume(),而如果在本方法之后,该Activity变得不可见了,那么紧跟着就会执行 onStop()方法 | onResume() 或者onStop() | |
onStop() | 当Activity对于用户不再可见时,会调用本方法,因为此时另一个Activity已经恢复(resumed)到哦了前台,挡住了本Activity。当另一个新的Activity启动(started)时、一个先前存在了的Activity再次返回到前台时、或者当前Activity被销毁时,以上三种情景都会调用本方法。在本方法执行之后,如果当前Activity又转到前台显示了,就会调用 onRestart(),如果当前Activity不再可见就会调用 onDestroy()方法销毁掉当前的Activity | 可以 | onRestart() 或者 onDestroy() |
onDestroy() | 在你的Activity被销毁之前调用的最后一个方法就是本方法。调用该方法是因为类Activity正在被结束(finishing)(有时是因为有的人调用了finish(),或者是因为系统临时的销毁掉了这个Activity的实例以用来保存空间。你可以尝试区分 isFinishing() finish() onDestroy()这几种方法) | 可以 | 后面没啦 |
注意上面那个第三列的可被系统"杀掉"的那一列-----其中有些方法是被标记成可以被系统杀掉的,在这些方法返回后,掌握着当前Activity的进程(Process)也许会被系统在任何需要的时候杀掉,尽管你没有在你的代码中通知系统当前可以杀掉。正是因为这样,你应该在 onPause() 方法中将一些应该持续保存的数据(比如用户的输入)保存到存储单元中去。另外, onSaveInstanceState(Bundle) 是在放置Activity到前台调用的(测试onSaveInstanceState()是在onPasue() 之后,在onStop()之前调用的 ),所以在onSaveInstanceState()中你可以任何非实例状态的东西存储到给定的Bundle中来保存起来,然后在之后的 onCreate(Bundle) 中来接收到上次储存了信息的Bundle,如果你需要的话,可以的话使用这个Bundle 来重新创建(恢复)上一个Activity的状态。你可以看一下 进程的生命周期 部分以获取和当前Activity有关的那个进程的更多信息。注意,你最好在 onPause() 中来保存信息,而不知在 onSaveInstanceState()中,因为后者不是生命周期循环中的方法,所以并不能保证在每个这种类似场景中都会调用它(比如在onPause()方法之后就立马被系统杀掉了,此时就没有执行到onSaveInstanceState()方法)。
你应该知道的是在 HONEYCOMB 版本之前,这些语义会有一些变化和不同。从 HONEYCOMB 版本开始到之后,生命周期中,在onStop()方法返回之前,系统是不会杀掉对应的Activity的,这就对上面讲到的onSaveInstanceState()来储存数据有点影响了(也就是说该方法将会在 onPause() 和 onStop() 之间安全可靠的调用)
而对于其它没有被标记为可被杀掉的方法来说,对应的Activity的进程 在方法被调用一直到方法返回后,都是一直保持执行状态的。因此一个Activity处于一个可被杀掉的状态,比如在 onPause() 和 onResume()之间时。
参数变化(Configuration Changes)
如果设备的参数(在 Resources.Configuration 类中定义的参数)发生变化时,设备的所有显示都需要发生变化来适应当前的参数变化。因为Activity是用来作为主要的用来和用户交互的,它包括了对于设备参数变化的支持。
除非你特别定义,否则的话,当设备参数发生变化(比如屏幕方向发生变化,语言发生变化,输入设备发生变化等等),这些变化将会导致当前的Activity被销毁(Destroyed),而这个过程是通过正常的生命周期来形成的,即 onPause() 然后到 onStop(),最后执行到 onDestroy() 。 如果对应的Activity现在在前台正在显示的话,一旦 onDestroy() 被调用之后,后面就会紧跟着又创建一个新的实例来显示,新创建时会从 onSaveInstanceState()中读取上一次保存的实例来恢复。
这样做的原因是任何应用的资源,包括布局文件,都应该根据参数的变化来变化。因此,唯一安全可靠的方式就是重新恢复原先所有的资源,包括布局文件、图片资源以及字符串资源等等。因为Activity必须事先要知道怎样保存它们原先的状态,并且要知道怎样去重新创建这些,因此简单方便的方法就是在新的参数环境下重新启动Activity。
在一些特殊性情况下,我们不想在某些参数发生变化时区重新创建启动Activity。要达到这样的目的的话,你可以在 AndroidManifest文件中通过 android:configChanges 属性来实现。在Manifest文件中声明的哪些参数后,在这些参数发生变化时系统就不会去重新创建Activity了,而是会去调用 onConfigurationChanged(Configuration)方法。相反的,如果有个参数你没有在Manifest文件中去使用 android:configChanges 属性来声明的话,那么当该参数发生变化时,Activity就会发生重建,而不是去调用onConfigurationChanged(Configuration)方法
启动Activities和获得返回结果(Starting Activities and Getting Results)
startActivity(Intent)方法就是用来启动一个新的Activity的,这个新启动的Activity就会处于Activity栈的顶部。该方法只需要一个参数传入,即 Intent ,这个参数中描述了将要被执行的那个Activity。
有些时候,你想要当一个Activity结束时,能够"通知"你一下,告诉你“我已经结束了哦”。比如,你可能会启动另一个Activity用来让用户从通讯录中选择一个人的名片,当选择完成后后,应该将选择的结果返回给“我”。为了实现这样的效果,我们可以使用startActivityForResult(Intent,int)方法来实现,其中第二个整形参数是用来标记区分当前的方法调用。最后返回过来的结果是在 onActivityResult(int,int,Intent)方法中的。
当那个被启动的Activity启动了后,可以在它里面通过调用 setResult(int) 方法来返回数据到启动它的那个Activity中去(即它的父Activity)。然后,它通常需要应用一个返回码,可以是标准的返回码比如 RESULT_CANCELED,RESULT_OK,或者是一些自定义的返回码。另外,如果有需要的话你可以的返回一个包含了数据的Intent对象到启动它的Activity中去。所有的数据信息都会出现在启动它的那个Activity的onActivityResult()方法,连同它刚开始时传入的那些标记码。
如果被启动的Activity由于一些不可抗因素导致中途失败了(比如直接崩溃了),对应的启动它的那个Activity将会收到 RESULT_CANCELED 结果码。
public class MyActivity extends Activity {
... static final int PICK_CONTACT_REQUEST = 0; public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
// When the user center presses, let them pick a contact.
startActivityForResult(
new Intent(Intent.ACTION_PICK,
new Uri("content://contacts")),
PICK_CONTACT_REQUEST);
return true;
}
return false;
} protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode == PICK_CONTACT_REQUEST) {
if (resultCode == RESULT_OK) {
// A contact was picked. Here we will just display it
// to the user.
startActivity(new Intent(Intent.ACTION_VIEW, data));
}
}
}
}
持久保存状态(Saving Persistent State)
后面待续...