本文翻译自Android开发者文档,原文链接:
http://developer.android.com/training/basics/activity-lifecycle/index.html在Android系统中,是通过一系列的回调方法来实现在生命周期中不同状态间的切换的,通过回调函数的调用,Activity在前后台之间来回切换。在Activity的整个生命周期历程中,系统采用类似金字塔的形式依次调用了一整套回调函数,当系统创建一个新的Activity实例时,每个回调函数都使Activity朝着金字塔顶端——即Resumed状态(此时Activity在前台运行且可与用户进行交互)前进一步;同理,当用户开始离开Activity时,系统又将调用对应的回调函数,使得Activity转入后台运行甚至销毁,图1-1展示了Activity生命周期的简图。
图1-1
合理运用回调函数,增强使得开发出来的程序可用性,做到下面几点:
在app正常使用的情况下,保证当用户接到拨入的电话或者切换到另外一个app时,程序不会出现崩溃;
在程序不在前台运行时,不消耗有限的系统资源;
保证当用户中途短时间退出app并再次返回时,能保存退出前的状态进度信息并恢复;
屏幕在横竖屏间切换时程序不崩溃且不丢失用户状态进度信息。
如图1-1所示,一个Activity可以在几种不同状态间切换,即Created,Started,Resumed,Pause,Stopped和Destroyed。值得一提的是,在这其中,只有三个状态下能保存在较长的一段时间内稳定存在并维持,也就是Resumed,Paused和Stopped,这三个状态分别介绍如下:
Resumed:在这个状态下,Activity正处于系统前台,且此时用户可以和其进行交互操作(有时也被称为“运行”状态)。
Paused:在这个状态下,Activity部分被另外一个Activity所遮挡——另外一个处于前台的Activity是半透明的或者没有覆盖整个屏幕,处于此状态下的Activity不能接受到用户的输入,其代码也不能被执行。
Stopped:在这个状态下,Activity对用户彻底不可见,此时被视为在后台运行,处于此状态下时,Activity实例和其所有状态信息,比如成员变量等仍被保留可用,但是不能执行任何代码。
其他状态,如Created和Started属于过渡性的状态,通过调用下一阶段的回调函数,系统很快会从他们切换到下一个状态,例如,当系统调用onCreate()后,下一步马上就会调用onStart(),紧接着又调用了onResume()。
以上就是Activity基本的生命周期流程介绍,下面我们就一起来看一下具体的每个阶段吧。
当用户点击应用图标启动时,系统就会调用被声明为App入口的Activity,也就是程序第一次启动时进入的Activity,我们可以在Android manifest文件中进行声明,如下所示:
如果上面的MAIN 或者是LAUNCHER未被声明,那么app的图标将不会出现在Home屏幕的应用程序列表中。
绝大多数的app都包含了多个不同的Activity以便用户执行不同的操作,对于每一个Activity,系统都通过调用onCreate()
方法来创建其实例。
我们需要覆盖onCreate()方法以执行程序启动的基本操作,这些操作应该
是在整个生命周期中只执行一次的,例如,我们应该在onCreate()方法中定义用户界面,同时可能还需要初始化一些全局变量。在下面的例子中,我们在onCreate()中执行了一些基本的初始化操作,如加载用户界面(定义在XML文件中),定义成员变量,以及对UI进行配置。
TextView mTextView; // Member variable for text view in the layout
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set the user interface layout for this Activity
// The layout file is defined in the project res/layout/main_activity.xml file
setContentView(R.layout.main_activity);
// Initialize member TextView so we can manipulate it later
mTextView = (TextView) findViewById(R.id.text_message);
// Make sure we're running on Honeycomb or higher to use ActionBar APIs
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
// For the main activity, make sure the app icon in the action bar
// does not behave as a button
ActionBar actionBar = getActionBar();
actionBar.setHomeButtonEnabled(false);
}
}
一旦执行完onCreate()中的操作后,系统将会立即调用onStart()
和onResume()
方法,这意味着
Activity永远不会停留在Created或者是Started状态,更准确地讲,Activity在onStart()
被调用后就开始对用户可见,然而紧接着就执行了onResume(),这使得在发生下一步变化(如有新来电或者是用户切换到下一个Activity)之前,
Activity都停留在Resumed状态。
图1-2将生命周期中的在创建一个Activity实例的三个方法——onCreate()
,onStart()和onResume()执行流程用虚线圈了出来,当这个流程执行完毕后,
Activity就进入到Resumed状态,也就是在前台并且可以与用户进行交互操作。
图 1-2
Activity销毁
Activity的生命周期回调函数都是一一对应的, 有用于启动的onCreate()
,也就少不了用于销毁的onDestroy(),在系统调用onDestroy()后,
Activity实例就将被从系统内存中彻底移除。
绝大多数app不需要覆盖onDestroy()
方法,因为本地类引用将会被销毁,并且大部分清理操作都应该在onPause()和onStop()中先执行了,但是,如果Activity包含了在onCreate()
创建的
后台线程或者是其他没有关闭可能会造成内存泄漏的资源,我们就应该在onDestroy()
销毁它
,下面给出示例代码。
@Override
public void onDestroy() {
super.onDestroy(); // Always call the superclass
// Stop method tracing that the activity started during onCreate()
android.os.Debug.stopMethodTracing();
}
注:系统只会在一种情况下不调用onPause()和onStop()而直接调用onDestroy()
——在onCreate()中调用finish()。在某些情况下,如当在当前Activity中临时决定启动另一个Activity的时候,我们可能会这样做,这时,系统将会不调用任何其他生命周期回调函数而直接调用onDestroy()
在一般的app使用过程中,处于前台的Activity
有时可能会因为被其他可见控件遮挡而进入到pause(暂停)状态,例如,当打开一个半透明新的Activity时,之前的Activity将会pause,只要Activity一直都是部分可见并且不是获得焦点的Activity,那么它就保持处于Paused的状态。
但是,如果一个Activity完全被遮挡,对用户不可见,那么系统将会调用Activity的onPause()方法,在此方法中我们可以中止那些在Paused状态下不应该继续进行的操作,或者是保存那些在用户进一步退出时应该被保存的信息。在用户从Paused状态返回Activity时,系统将会调用onResume()方法恢复Activity。
图2-1中用虚线圈出的部分表示了执行pause与resume的过程。
当系统调用了Activity的onPause()方法时,Activity还是部分可见的,但是大多数情况下这预示着用户将会离开Activity,并可能很快就会进入Stopped状态,我们应该在onPause()方法中做这些事:
停止动画或其他正在进行的消耗CPU的操作;
提交未被保存的改动,但仅限于那些用户希望在离开时被永久保存的改动(如Email草稿);
释放系统资源如BroadcastReceiver,传感器(如GPS)的handler,等任何在Paused状态下用户不需要的资源。
举个例子,如果我们在应用中用到了相机,那么在onPause()
中释放它就是一个不错的选择,如下:
@Override
public void onPause() {
super.onPause(); // Always call the superclass method first
// Release the Camera because we don't need it when paused
// and other activities might need to use it.
if (mCamera != null) {
mCamera.release()
mCamera = null;
}
}
一般来说,我们不应该使用onPause()
将用户输入的变动保存到永久存储中,例如填入到表单中的数据等,只有那些确认为用户希望被保存的改动(如邮件草稿)才需要被保存。需要注意的是,我们应该避免在onPause()中执行耗费CPU比较严重的操作,比如写数据到数据库等,因为这类操作将会影响切换到下一个Activity的速度和流畅性(这类操作应该放到onStop()中进行)。
我们应该
尽量减少在onPause()中执行的操作的数量,使得当Activity进一步被stop时,让用户的下一步操作体验更加流畅
。
注:当Activity处于Paused状态时,其实例仍驻守在系统内存中,以便在下一次pause时重新被调用,因此,在返回Resumed状态时,我们不需要再次初始化系统组件。
系统调用onResume()方法使得
Activity从Paused状态恢复到Resumed状态。值得一提的是,每次当Activity返回到前台运行时,onResume()
都会被调用,在
Activity第一次被创建时也是如此。因此,我们应该在onResume()中初始化那些在onPause()中被释放的资源组件,并执行其他的用户进入到
Resumed状态时需要的初始化操作,如开始动画和初始化获得用户焦点时用到的组件。
下面的onResume()
示例与上面的onPause()例子相对应,初始化了在onPause()中被释放的相机。
@Override
public void onResume() {
super.onResume(); // Always call the superclass method first
// Get the Camera instance as the activity achieves full user focus
if (mCamera == null) {
initializeCamera(); // Local method to handle camera init
}
}
在恰当的时机停止和重启Activity对于让用户得知app一直都在保持运行且没有丢失进度信息来说十分重要,下面举几个Activity停止和重启的例子吧:
用户打开最近使用的应用列表,并点击选择从当前应用切换到另一个应用,这时当前应用的处于前台的Activity将会被stop,如果用户接着又在最近应用列表或者是屏幕主界面选择返回刚刚离开的当前应用,那么Activity就会被restart;
用户在app中启动了一个新的Activity,那么当前的Activity将会被stop,而新的Activity将被创建进入create流程,如果用户接着点击返回按钮,那么第一个Activity将被restart;
用户在使用app的过程中接到一个来电。
为了Activity的停止和重启,我们可以使用它的两个生命周期回调函数,分别是onStop()和onRestart(),来控制Activity的stop和restart流程。与Paused状态中的部分可不同的是,在Stopped状态下,UI界面是完全不可见的,并且用户的焦点已经转移到了另一个Activity甚至已经到了另一个app中去了。
注:因为在处于Stopped状态下时,Activity的实例仍然保留在内存中,我们是可以不必实现onStop()和onRestart()方法的(甚至连start()方法也可以不管),对于大多数Activity来说,都是可以正常停止和重启的,我们可以只在onPause()中暂停正在进行的动作,断开资源连接即可。
如图3-1所示,当用户离开Activity时,系统将会调用onStop()执行停止Activity的动作,即图中步骤1。当用户返回已经被stopped的Activity时,系统又会调用onRestart(),即步骤2,紧接着是步骤3——onStart()和步骤4——onResume(),需要注意的是无论是哪种情况导致了Activity被stop,系统总会在调用onStop()之前调用onPause()。
当Activity的onStop()被调用时,它就变为不可见了,在这种情况下,它应该释放所有在没有用户使用时不需要的资源。在Activity处于Stopped状态时,在必要的情况下,系统可能会销毁其实例以收集恢复内存,在某些极端情况下,系统会不调用onDestroy()而直接杀死应用进程,因此,在onStop()中必须要释放那些可能造成内存泄漏的资源。
尽管在onStop()之前会先调用onPause(),但是对于那些消耗大量CPU资源的关闭操作,例如将数据存到数据库,我们还是应该将其放到onStop()中去,下面是一个onStop()实现的例子,在例子中把记事本草稿内容写到持久存储中去。
@Override
protected void onStop() {
super.onStop(); // Always call the superclass method first
// Save the note's current draft, because the activity is stopping
// and we want to be sure the current note progress isn't lost.
ContentValues values = new ContentValues();
values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText());
values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle());
getContentResolver().update(
mUri, // The URI for the note to update.
values, // The map of column names and new values to apply to them.
null, // No SELECT criteria are used.
null // No WHERE columns are used.
);
}
在Activity被stop之后,其实例仍然驻留在内存中,并且在resume的时候重新被调用。我们不需要重新初始化在Resumed及之前的状态下已经被创建的组件,系统也会保留每个控件的当前状态,若用户在输入框中输入了一些文本内容,这些内容还会被保留,就不需要自己保存和恢复它们了。
注:即使Activity在Stopped的状态下被系统销毁了,控件的状态也仍然能得到保留(如输入框中的内容),这是通过Bundle对象(一种键值对组成的块对象)做到的,在用户导航返回到Activity对象时,从Bundle中获取之前的状态信息。
当Activity从Stopped状态回到前台,也就是每次Activity变为可见时,其回调函数onRestart()将会被调用,与此同时,onStart()也会被调用。onStart()在Activity第一次被创建时也会被调用,onRestart()则有所不同,只会在从Stopped返回Resumed状态时才会被调用,所以,我们可以利用onRestart()进行一些特定的恢复操作,用于在Activity被stop但尚未被destroy时返回的恢复还原操作。
事实上,使用onRestart()来恢复Activity状态并不十分常见,但是,因为我们在onStop()中执行了一些必要的关闭操作以清理系统资源,我们需要在Activity重新启动时再次初始化那些在onStop()被关闭的资源,当然,在Activity第一次被创建时也同样需要执行这些初始化操作。基于这个原因,onStart()与onStop()经常被成对使用,彼此呼应。
在下面的例子中,因为用户可能是在已经离开应用较长一段时候后再重新返回的,我们就应该在onStart()中检查某些系统资源(如GPS)是否还可用。
@Override
protected void onStart() {
super.onStart(); // Always call the superclass method first
// The activity is either being restarted or started for the first time
// so this is where we should make sure that GPS is enabled
LocationManager locationManager =
(LocationManager)getSystemService(Context.LOCATION_SERVICE);
boolean gpsEnabled =
locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (!gpsEnabled) {
// Create a dialog here that requests the user to enable GPS, and use
// an intent with the
//android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS action
// to take the user to the Settings screen to enable GPS when they click
//"OK"
}
}
@Override
protected void onRestart() {
super.onRestart(); // Always call the superclass method first
// Activity being restarted from stopped state
}
当系统销毁Activity时,调用了其onDestroy()方法。因为绝大多数系统资源应该在onStop()中就被回收了,当执行到回调函数onDestroy()的时候,大多数情况下还需要执行的操作并不多了。onDestroy()是最后一个可以清理造成内存泄漏的资源的地方,因此我们应该确保所有不需要的进程,以及其他耗时较长的诸如方法调用都已经被关闭。
在正常使用的情况下,Activity最终将会被销毁,比如用户点击返回键退出或者通过直接调用finish()方法。另外,在长时间处于Stopped状态下或者是当前处于前台的Activity需要更多的资源时,系统都可能会做出销毁Activity的动作。
当Activity时因为用户点击返回键等正常操作被销毁时,系统会认为已经不需要用到此Activity实例了,Activity实例会被直接销毁。但是,如果是因为系统资源不足而造成的Activity销毁,系统将会记录下当前Activity的状态,在重新返回被销毁的Activity时,系统将会使用之前记录的状态信息来创建恢复新的Activity,Activity实例的状态信息会被存储在一个Bundle对象中,Bundle对象本质上是一个个键-值对组成的集合。
注:每次用户旋转屏幕切换横竖屏时,Activity实例都会被销毁并重新创建,这是因为当屏幕旋转时,屏幕显示参数发生了改变,因此Activity可能需要重新加载相关的资源文件,如布局文件。
默认情况下,系统会使用Bundle对象来记录Activity布局中的所有控件信息(如输入框中的文本内容)。所以,如果Activity实例被销毁并重新创建,布局相关的状态将会被保存起来,我们不需要自己去保存和恢复这些信息。当然,我们可能还需要额外保存另外的一些信息,例如当前用户的使用进度等。
注:在Android系统中,为了能恢复控件的状态,每个控件都需要有一个唯一的ID,也就是控件的“android:id”属性了。
为了能够保存有关Activity的一些额外的信息,我们必须重写onSaveInstanceState()
方法,在用户离开Activity时,系统调用这个方法并将相关信息传入到Bundle对象中去,以防Activity实例被意外销毁。如果系统稍后需要重新创建此实例,这个Bundle对象将被传递给onRestoreInstanceState()
和onCreate()方法。
如图4-1所示,当系统开始stop一个Activity的时候,它将会调用onSaveInstanceState()
方法,即图中的步骤1,这时我们就可以将那些在Activity实例被重新创建时需要用到的状态信息保存起来。当Activity实例被销毁后又需要重新创建的时候,系统就会把在步骤1中保存的状态信息数据传递给onRestoreInstanceState()
和onCreate()方法,如步骤2,3所示。
图 4-1
在Activity被stop的时候,系统会调用其onSaveInstanceState()
方法,从而Activity就可以将当前状态信息存储到键-值对集合中。默认地,onSaveInstanceState()
会自动保存有关界面控件布局的内容。当然,如果我们需要保存一些额外的信息,也可以自己实现onSaveInstanceState(),将相关信息用保存到
键-值对的形式保存到Bundle对象中去,如下例所示:
static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
...
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current game state
savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
注:记得要调用父类的onSaveInstanceState()
实现,确保有关控件布局的内容已经被保存。
恢复Activity状态
当Activity在销毁后重新创建时,我们可以借助系统传递过来的Bundle对象来恢复到销毁之前的状态,onCreate()和onRestoreInstanceState()
都会接收到这个对象。
因为在系统创建一个全新的Activity和重新创建恢复一个之前已经被销毁的Activity情况下,onCreate()都会被调用,所以我们必须在读取Bundle对象之前检查其是否为空。如果Bundle为空,那么系统就是在创建一个全新的Activity,而不是创建恢复之前已经被销毁的Activity。下面的例子说明了如何在onCreate()中恢复部分状态信息数据。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Always call the superclass first
// Check whether we're recreating a previously destroyed instance
if (savedInstanceState != null) {
// Restore value of members from saved state
mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
} else {
// Probably initialize members with default values for a new instance
}
...
}
除了在onCreate()中恢复状态信息之外,我们也可以选择在onRestoreInstanceState()
——即系统在onStart()之后调用的方法,进行恢复操作,onRestoreInstanceState()方法只会在存在已经保存的状态信息的情况下才会被调用,因此我们可以不必检查
Bundle对象是否为空,如下所示:
public void onRestoreInstanceState(Bundle savedInstanceState) {
// Always call the superclass so it can restore the view hierarchy
super.onRestoreInstanceState(savedInstanceState);
// Restore state members from saved instance
mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
}
注:和onSaveInstanceState()
类似,同样记得要调用父类的onRestoreInstanceState()
实现,确保有关控件布局的内容已经被恢复。