Activity在Android中的运行是由Activity的回退栈来管理的,当前活动的Activity位于回退栈的栈顶,而回退栈中的其它Activity处于停止状态。在一个Activity中运行的过程中,会受到一些突发事件的影响。例如:当前展示的Activity被一个来点打断了,开启了另外一个Activity,从另一个Activity中返回了等等事件,为了处理这些突发事件,需要用到Activity的生命周期。
Activity在Android中的生命周期,下图做了完美的诠释:
从上图可以看出,Activity大致可以分为四种状态:
· 活动状态:当前Activity处于活动,被至于回退栈的顶端,处于前台用户可见,可以获得焦点。
· 暂停状态:其它Activity处于前台显示,但该Activity依然部分可见,只是不能获得焦点。
· 停止状态:其它Activity处于前台,该Activity完全不可见,无法获取焦点。
· 销毁状态:该Activity被销毁或者被系统回收。
在Activity的不同生命周期中,均有对应的生命周期方法被系统回调,熟悉这些方法的回调时机,对于开发Android应用非常的重要,这些方法如下:
· onCreate(Bundle savedStatus):创建Activity时被回调。
· onStart():启动Activity时被回调。
· onRestart():重新启动Activity时被回调。
· onPause():暂停Activity时被回调。
· onStop():停止Activity时被回调。
· onResume():恢复Activity时被回调。
· onDestroy():销毁Activity时被回调。
前面已经讲解了Activity的生命周期的基本知识,接下来使用实例来模拟Activity生命周期的触发与执行顺序。
首先创建一个新的Android项目,然后重写其中Activity的所有生命周期方法。
代码清单:\codes\05\02\ActivityLifecycleDemo\src\com\bookdemo\activitylifecycledemo \MainActivity.java
public class MainActivity extends Activity { private final static String TAG="main"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i(TAG, "---onCreate方法被回调---"); } @Override protected void onStart() { Log.i(TAG, "---onStart方法被回调---"); super.onStart(); } @Override protected void onRestart() { Log.i(TAG, "---onRestart方法被回调---"); super.onRestart(); } @Override protected void onResume() { Log.i(TAG, "---onResume方法被回调---"); super.onResume(); } @Override protected void onPause() { Log.i(TAG, "---onPause方法被回调---"); super.onPause(); } @Override protected void onStop() { Log.i(TAG, "---onStop方法被回调---"); super.onStop(); } @Override protected void onDestroy() { Log.i(TAG, "---onDestroy方法被回调---"); super.onDestroy(); } }
在启动时候从LogCat里可以看到输出的日期:
当onResume()方法被回调之后,Activity处于运行状态。
再来演示从运行状态到暂停状态时生命周期方法的回调过程。在这个Activity中增加一个Button,为其打开一个窗口化的Activity。
代码清单:\codes\05\02\ActivityLifecycleDemo\src\com\bookdemo\activitylifecycledemo \MainActivity.java
btnOpenDialog = (Button) findViewById(R.id.btnOpenDialog); btnOpenDialog.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this, DialogActivity.class); startActivity(intent); } })
对于一个Activity而言,如果要把它设置成一个窗口模式,在清单文件AndroidManifest.xml中配置Activity元素的时候,为其android:theme属性设置为:@android:style/Theme.Dialog,这是Android为我们提供的一个窗口主题,可以直接使用。
代码清单:\codes\05\02\ActivityLifecycleDemo\AndroidManifest.xml
<activity android:name="com.bookdemo.activitylifecycledemo.DialogActivity" android:theme="@android:style/Theme.Dialog" > </activity>
在模拟器上运行会打开一个窗口,并显示一个关闭按钮,运行效果如下:
打开Activity窗口,再点击关闭按钮,LogCat输出日志如下图:
此时,原Activity从运行状态转换为暂停状态,先回调onPause()方法置为暂停状态,当关闭窗口后,重新获得前台显示,并可以获得焦点后,再回调Resume()方法重新置为运行状态。
接下来再来演示停止状态的回调方法执行过程,在Activity中再增加一个Button,响应其点击事件开启一个普通的Activity,开启时完全遮挡原Activity,使其运行在后台并不可见,然后在点击关闭按钮,退回到原Activity中。此时LogCat输出的日志信息如图:
从上图可以看出,当打开一个新的Activity时,原Activity会回调onPause()和onStop()方法,使原Activity置为暂停状态,当原Activity重新回到前台显示的时候,会回调onRestart()、onStart()、onResume()方法置原Activity为运行状态。
最后再来看看当一个Activity被退出销毁时,回调方法的调用顺序。在运行状态,点击回退按钮退出应用,观察LogCat输出的日志信息:
从上图退出应用的时候可以看出Activity销毁的时候,会先后调用onPause()、onStop()、onDestroy方法。
清楚了Activity各个不同的生命周期的转换过程,就明白了在什么场景下需要对哪些生命周期方法进行重写。通常对Activity生命周期应用最多的场景就是,对于非常重要的数据,可以通过重写onPause()方法在Activity被暂停之后进行数据持久化,然后重写onResume()方法,在Activity被恢复的同时也恢复数据。其实这也保证了非常良好的用户体验,用户在录入了一部分数据后被突发事件打断了,在解决完突发事件之后,回到原Activity还可以对之前录入的数据再次进行编辑。
下面通过一个实例来演示如何在Activity被销毁的时候保存数据,并且在Activity再次被创建的时候恢复数据。Android系统是支持屏幕方向切换的,也就是说Activity在横屏与竖屏的时候会需要不同的长宽比。在默认情况下,当屏幕方向进行切换的时候,Android的策略是先销毁当前Activity,然后再重新创建。当然这一点可以通过在清单文件AndroidManifest.xml中为这个Activity设置android:configChanges属性来改变,android:configChanges属性的设置在前面的章节中有讲到。
在屏幕方向切换时,Activity中有UI组件被录入数据,它是会被清空的,回到初始化的状态。如果需要保存UI组件被录入的数据,需要重写onPause()和onResume()方法,以方便在销毁的时候保存数据,在重新创建的时候恢复数据。
代码清单:\codes\05\02\ActivityLifecycleUseDemo\src\com\example\activitylifecycleusedemo\MainActivity.java
public class MainActivity extends Activity { private EditText etUsername; private String middleUsername; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); etUsername = (EditText) findViewById(R.id.etUsername); } @Override protected void onPause() { // 在Activity暂停的时候保存数据 if (etUsername != null) { String username = etUsername.getText().toString(); if (!TextUtils.isEmpty(username)) { middleUsername = username; } } super.onPause(); } @Override protected void onResume() { // 在Activity恢复的时候恢复数据 if (etUsername != null) { if (!TextUtils.isEmpty(middleUsername)) { etUsername.setText(middleUsername); } } super.onResume(); } }
在模拟器上,随意输入姓名,使用ctrl+F11快捷键,对模拟器进行屏幕方向的切换,可以看出,在Activity销毁之前保存了输入的姓名,在恢复之后同时也恢复了姓名。
这里是使用Activity类中的属性来保存数据,在实际项目中不能如此使用,因为很可能整个Activity都已经被销毁了,在其中的属性保存的数据自然也不存在了。所以一般而言,会使用数据持久化到内存中或者SD卡上,这部分内容会在后面的章节中讲到。屏幕方向的切换是一个特殊的例子,这里只对生命周期的讲解使用到了。