Activity是Android应用的四大组件之一(其他三大组件分别是Service、Broadcast Receiver、Content Provider)。
Activity是Android应用的表示层。Activity通常作为全屏窗口呈现给用户,另外也可以作为浮动窗口或者嵌入另一个Activity内部这两种办法使用。
类比WEB开发,可以把Activity理解成网页中的一个JSP文件;或者也可以把它理解成一个Windows的窗口。
从Activity类的继承关系可以看到Activity是Context类的子类:
一个程序一般由多个Activity组成,各Activities之间耦合关系通常很松散。程序必须有一个Activity被指定为主Activity,它是程序启动时首先显示的界面。每个Activity都可以随意启动其它的Activity。每当一个Activity被启动,则前一个Activity就被停止。一个程序中的所有启动的Activity都被放在一个栈中,所有被停止的Activity并没有销毁,而在置存于栈中。新启动的Activity被置于栈顶,然后获得输入焦点。在当前活动的Activity上点返回键,它就会被从栈中取出然后销毁,之后在栈中紧接着的它的前一个Activity被恢复,取而代之获得输入焦点。
Acitvity主要有四种状态:
Activity这四种状态可以分别对应四个进程状态:
当一个Activity因为新的Activity启动而被停止时,它会收到状态变化的通知,这样的变化有多个,每个都会引起系统调用一个相应的回调方法以通知Activity,这些回调方法被统称为“生命周期回调方法”。这些回调方法分别在Activity被创建、停止、恢复、销毁时被调用。
下图是活动的重要状态的路径图,彩色的椭圆框是Activity的状态,矩形框是生命周期回调方法:
这里用实例来演示各生命周期回调方法的触发时机:
本例创建了两个Activity。主Activity名叫MyActivity,包含一个输入框,输入文字后点击按钮,会启动另一个Activity(名叫AnotherActivity),AnotherActivity会显示用户所输入的文字。控制台(Logcat)会输出当前Activity触发的回调函数(本文省略了每个控制台输出的时间戳和前缀等信息,只按顺序保留方法名)。
从启动应用到MyActivity变为运行状态:
com.example.lesson.MyActivity.onCreate(MyActivity.java:18) com.example.lesson.MyActivity.onStart(MyActivity.java:34) com.example.lesson.MyActivity.onResume(MyActivity.java:66) |
这里onCreate,onStart,onResume三个方法依次被调用。
在文本框输入文字:
点击Android设备电源按键,关闭设备屏幕:
com.example.lesson.MyActivity.onSaveInstanceState(MyActivity.java:72) com.example.lesson.MyActivity.onPause(MyActivity.java:40) |
这里onSaveInstanceState,onPause方法被依次调用,说明MyActivity进入了暂停状态。
重新打开设备屏幕:
com.example.lesson.MyActivity.onResume(MyActivity.java:66) |
发现文本框文字保持原样。这里仅onResume方法被调用,此时MyActivity恢复运行状态。
点击Android设备主页(Home)按键,退到Android启动界面(laucher):
com.example.lesson.MyActivity.onSaveInstanceState(MyActivity.java:72) com.example.lesson.MyActivity.onPause(MyActivity.java:40) com.example.lesson.MyActivity.onStop(MyActivity.java:47) |
这里onSaveInstanceState,onPause,onStop三个回调方法被依次调用,MyActivity进入停止状态。
然后再点击应用按钮回到界面:
com.example.lesson.MyActivity.onRestart(MyActivity.java:60) com.example.lesson.MyActivity.onStart(MyActivity.java:34) com.example.lesson.MyActivity.onResume(MyActivity.java:66) |
重新打开应用后发现文本框中的文字还在,说明MyActivity的状态依然是保存完好的。这里onRestart,onStart,onResume方法被依次调用,说明进入停止状态后需要先重启才能恢复。
触碰屏幕中”Launch Another Activity"按钮:
com.example.lesson.MyActivity.onSaveInstanceState(MyActivity.java:72) com.example.lesson.MyActivity.onPause(MyActivity.java:40) com.example.lesson.AnotherActivity.onCreate(AnotherActivity.java:16) com.example.lesson.AnotherActivity.onStart(AnotherActivity.java:25) com.example.lesson.AnotherActivity.onResume(AnotherActivity.java:57) com.example.lesson.MyActivity.onStop(MyActivity.java:47) |
界面跳转到AnotherActivity,并显示出MyActivity文本框输入的文字。这里MyActivity先调用onSaveInstanceState和onPause方法,然后AnotherActivity创建并启动,完成后MyActivity再调用onStop方法。
点击Android设备的后退按键:
com.example.lesson.AnotherActivity.onPause(AnotherActivity.java:31) com.example.lesson.MyActivity.onRestart(MyActivity.java:60) com.example.lesson.MyActivity.onStart(MyActivity.java:34) com.example.lesson.MyActivity.onResume(MyActivity.java:66) com.example.lesson.AnotherActivity.onStop(AnotherActivity.java:38) com.example.lesson.AnotherActivity.onDestroy(AnotherActivity.java:45) |
界面回到MyActivity,其文本框的文字还保持原样;另外后退按键使得AnotherActivity在onDestroy方法被调用,说明它被销毁了,下次启动需重新调用onCreate方法。
将设备旋转至横屏:
com.example.lesson.MyActivity.onSaveInstanceState(MyActivity.java:72) com.example.lesson.MyActivity.onPause(MyActivity.java:40) com.example.lesson.MyActivity.onStop(MyActivity.java:47) com.example.lesson.MyActivity.onDestroy(MyActivity.java:54) com.example.lesson.MyActivity.onCreate(MyActivity.java:18) com.example.lesson.MyActivity.onStart(MyActivity.java:34) com.example.lesson.MyActivity.onResume(MyActivity.java:66) |
旋转屏幕会导致MyActivity被摧毁然后重新创建再启动,当然由于onSaveInstanceState方法被调用,其状态被保存,重新创建后文本框文字被原样恢复。
点击Android设备后退按钮再打开应用:
com.example.lesson.MyActivity.onPause(MyActivity.java:40) com.example.lesson.MyActivity.onStop(MyActivity.java:47) com.example.lesson.MyActivity.onDestroy(MyActivity.java:54) com.example.lesson.MyActivity.onCreate(MyActivity.java:18) com.example.lesson.MyActivity.onStart(MyActivity.java:34) com.example.lesson.MyActivity.onResume(MyActivity.java:66) |
发现文本框中的文字没有被恢复,这是因为点击后退键不会触发onSaveInstance方法,MyActivity状态丢失,下次重建再次调用onCreate(Bundle)方法时,其参数Bundle为空。
MyActivity代码如下:
package com.example.lesson; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; public class MyActivity extends Activity { public static final String TAG = "MyActivity"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Log.d(TAG, new Throwable().getStackTrace()[0].toString()); Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(view.getContext(), AnotherActivity.class); EditText editText = (EditText) findViewById(R.id.editText); intent.putExtra(TAG, editText.getText().toString()); startActivity(intent); } }); } @Override protected void onStart() { super.onStart(); Log.d(TAG, new Throwable().getStackTrace()[0].toString()); } @Override protected void onPause() { super.onPause(); Log.d(TAG, new Throwable().getStackTrace()[0].toString()); } @Override protected void onStop() { super.onStop(); Log.d(TAG, new Throwable().getStackTrace()[0].toString()); } @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, new Throwable().getStackTrace()[0].toString()); } @Override protected void onRestart() { super.onRestart(); Log.d(TAG, new Throwable().getStackTrace()[0].toString()); } @Override protected void onResume() { super.onResume(); Log.d(TAG, new Throwable().getStackTrace()[0].toString()); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Log.d(TAG, new Throwable().getStackTrace()[0].toString()); } }AnotherActivity代码如下:
package com.example.lesson; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.widget.TextView; public class AnotherActivity extends Activity { public static final String TAG = "AnotherActivity"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.another_activity); Log.d(TAG, new Throwable().getStackTrace()[0].toString()); TextView textView = (TextView) findViewById(R.id.textView); Intent intent = getIntent(); textView.setText(intent.getStringExtra(MyActivity.TAG)); } @Override protected void onStart() { super.onStart(); Log.d(TAG, new Throwable().getStackTrace()[0].toString()); } @Override protected void onPause() { super.onPause(); Log.d(TAG, new Throwable().getStackTrace()[0].toString()); } @Override protected void onStop() { super.onStop(); Log.d(TAG, new Throwable().getStackTrace()[0].toString()); } @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, new Throwable().getStackTrace()[0].toString()); } @Override protected void onRestart() { super.onRestart(); Log.d(TAG, new Throwable().getStackTrace()[0].toString()); } @Override protected void onResume() { super.onResume(); Log.d(TAG, new Throwable().getStackTrace()[0].toString()); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Log.d(TAG, new Throwable().getStackTrace()[0].toString()); } }
类似旋转屏幕这种更改Android设备配置(由Resources.Configuration类定义)的操作还有改变语言、输入设备等,它们都会导致Activity被销毁然后重建。如果想避免这些原因导致的重启的话,可以在manifest中修改android:configChanges属性。当这些配置被修改时,当前Activity的onConfigurationOnChanged(Configuration)方法就会被调用而不会直接重启Activity。
不过一旦涉及到了未声明处理的配置变更,Activity依然会被重启且onConfigurationOnChanged方法不会被调用。举个例子:API 13以后,在AndroidManifest.xml中设置android:configChanges="orientation“后,旋转屏幕时不会触发onConfigurationOnChanged,活动依然会重启,因为screenSize属性也跟着改变了。此时需要增加属性声明为android:configChanges="orientation|screenSize"才能防止活动重启,当然如果仅仅是为了这个目的的话,onConfigurationOnChanged方法不一定要重写(override)。
从示例可知,onSaveInstanceState(Bundle)方法被调用时,系统会将Activity的状态存储到Bundle里。如果进程未被杀死,经过onRestart()后Activity状态和成员信息依旧完好,无需恢复;如果被杀死了,则系统会调用onCreate(Bundle)或者onRestoreInstanceState(Bundle)方法恢复Activity状态。概括成图示如下:
onSaveInstanceState方法会在系统认为Activity“容易”(vulnerable)被系统销毁时调用,如果是用户主动销毁的,如示例中按下设备的后退键,则不会调用。以下几种会被调用的常见情况(在示例中均出现了):
很多资料说长按主页键调出多任务栏也会触发该方法,博主在小米2S(Android 4.1.1)上实测并未成功重现,不知何故。
Android标准UI控件基本都实现了onSaveInstanceState方法,如文本框(EditText)会用户保存输入的文本、复选框(CheckBox)会保存用户打钩的位置等,前提是这些UI控件被分配了唯一的ID,才能被恢复状态。开发者需要覆写该方法时,需要先调用父类的该方法再在后面编写自己的代码。
由于onSaveInstanceState方法并不总是被调用,因此它常用来保存一些临时的状态、成员变量值等,而类似持久化数据(保存数据到数据库或文件中)这样的工作通常被放在onPause方法里(多提一句,如果要保存的数据量很大可能耗时较长,可以另开线程,将这些工作异步处理,尽量不要阻塞UI线程)。
需要持久化的状态一般有两种:
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 savedInstanceState ) { super . onCreate ( savedInstanceState ); 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 (); } }
Android官方API文档:http://developer.android.com/reference/android/app/Activity.html
nkmnkm的专栏:http://blog.csdn.net/niu_gao/article/details/7101178
Android学习指南:http://android.yaohuiji.com/archives/141
Healtheon的博客:http://www.cnblogs.com/hanyonglu/archive/2012/03/28/2420515.html