来自 http://blog.sina.com.cn/s/blog_5ffeae360100ekoo.html~type=v5_one&label=rela_prevarticle
在AndroidSDK发布的官方文档里,实际上已经对Activity所包含的各个重要状态的概念和相互之间转换机制作了较为详细的描述,但梨子的滋味总是自己尝了才能体会的深刻,笔者就在开发一些应用过程中出现过奇怪的错误,后来经过确定发现是由于没有对Activity状态转换的一些细节理解到位,从而导致应用本身的一些运行状态被打乱而出现各种错误。因此笔者找个时间特地基于AndroidSDK的Activity描述(AndroidSDK路径/docs/reference/android/app/Activity.html)做了一些实验,目的是搞清楚Activity各个重要状态的转换机制。
在做实验前,先依照官方文档的介绍理清一些重要状态的概念,笔者会依照自己开发的经验和理解加上一些解释,如果大家觉得有疑问和待商榷的地方也可以提出来探讨。
Android针对Activity的管理使用的是栈,就是说某一个时刻只有一个Activity处在栈顶,当这个Activity被销毁后,下面的Activity才有可能浮到栈顶,或者有一个新的Activity被创建出来,则旧的Activity就被压栈沉下去了。从这里我们可以看出Android似乎是依照一种层次管理所有的Activity的,为什么这么做,个人觉得原因在于Android对Activity的作用定位很重要的一点是考虑其管理与用户交互的作用,而谈到交互,首要的就是界面了,因此Activity是直接涉及到与用户交互的界面处理的,而任意时刻与用户处于交互状态的界面只能有一个,所以Android针对Activity的管理采用了具有层次感的栈的数据结构,理解这一点对于Activity一些状态转换的细节处理非常重要。
依据这种基于界面层次性的理解,官方文档主要提了两点:
1. 只有处于栈顶的Activity一定是出于运行状态的。当启动一个新的Activity时,系统会将它置于栈
顶,同时使其运行,这个时候先前处于栈顶的Activity则被压栈,只有当栈顶的Activity退出时才
可能重新处于栈顶。
2.一个Activity总的来说拥有四种状态,分别是
1)激活(运行)状态: 此时它一定是在屏幕的最前端的,对应Activity栈来说,它是在栈顶的;
2)暂停状态:此时它在屏幕上仍然是可见的,但是失去了焦点。这种情况会发生在当有一个不会占
满整个屏幕或者拥有透明属性的Activity启动并获得了屏幕焦点时。一个暂停状态的Activity还
是存活的(alive),它仍然维持着自己的各个内部状态和成员信息,仍然和window manager保持
连接,但是系统可能会在手机内存极低的情况下杀掉该Activity;
3)终止状态:此时这个Activity在屏幕上完全不可见,它已经被其他的Activity挡住了。这时它维
持着自己的各个内部状态和成员信息,但是由于用户已完全看不见它,其window也会被隐藏掉,
也就是说window manager不再管理其window信息了。停止状态下的Activity会经常由于手机的内
存征用问题被系统杀掉。
4)当Activity被暂停或者终止时,系统可以把它从内存中清除,这个过程也许会提示用户是否要结
束该Activity,也许只会简单地杀掉其进程。当这个Activity重新被用户调出来显示在界面上
时,用户自己必须保证Activity能够完全恢复先前维护的内部状态信息,以使其回到Activity内
部所处先前的状态,以笔者的经验这种情况如果处理不好很容易出现问题。
这第二点实际上依据界面层次考虑就是3种情况:
a.active/running: 此时Activity管理的界面可见,且直接面对和用户的交互;
b.pause: 此时Activity管理的界面可见,但处于后端,类似变成了背景;
c.stop: 此时Activity管理的界面完全不可见。
澄清了上述重要的概念之后,再来看需要研究的Activity具体的函数,很自然可以知道最重要的函数一定就是针对上面三种情况加上Activity自身创建和消毁所涉及到的几个函数了:
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之间的跳转:Activity1的界面上只有一个按钮,点击后转到到Activity2上,Activity2上同样一个按钮,点击后跳到Activity1上。我将上述的函数均打上log,便于观察,同时为了过滤其他的log,使用了Error级别,纯粹是为了方便显示。
大致代码如下:
----------------------------------------------------------------------------------------------
public class ALStudyActivity extends Activity {
private String LOG_TAG = "ALStudyActivity";
@Override
public void onCreate(Bundle savedInstanceState) {
Log.e(LOG_TAG,"onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button button01 = (Button)findViewById(R.id.Button01);
button01.setOnClickListener(new Button.OnClickListener(){
public void onClick(View v){
Intent it = new Intent(ALStudyActivity.this,NewActivity.class);
startActivity(it);
}
});
}
@Override
protected void onDestroy() {
Log.e(LOG_TAG,"onDestroy");
super.onDestroy();
}
.... //其他几个测试函数,不再列出
}
public class NewActivity extends Activity {
private String LOG_TAG = "NewActivity";
@Override
public void onCreate(Bundle savedInstanceState) {
Log.e(LOG_TAG,"onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
Button button02 = (Button)findViewById(R.id.Button02);
button02.setOnClickListener(new Button.OnClickListener(){
public void onClick(View v){
Intent it = new Intent(NewActivity.this,ALStudyActivity.class);
startActivity(it);
}
});
}
... //省略,同上
}
----------------------------------------------------------------------------------------------
这里ALStudyActivity对应Activity1, NewActivity对应Activity2
实验1. Activity1->按手机(模拟器)Back键退出
输出结果:
实验1 显示了Activity在正常情况下从正常到退出的全部情形。 onDestory只有在Activity真正退出时才会被调用。
实验2. Activity1->手机Home键回主界面
输出结果:
![【原创】【Android笔记】Activity不同状态转换研究 【原创】【Android笔记】Activity不同状态转换研究](http://s9.sinaimg.cn/bmiddle/5ffeae36t732134c739c8&690)
实验2 显示了一在按手机Home键退出时的函数调用时序。 由于不是退出,因此最终没有调用onDestroy(),但是应用变得不可见,因此调用了onStop。可以看到系统还调用了保存实例状态的函数:onSaveInstanceState(),也就是说按Home键退出到主界面系统会帮助我们保持Activity的一些信息,同时给了我们保存额外需要维护的信息的机会,这一点请注意,因
为系统不是在任何时候都会调用onSaveInstanceState()的,下面的实验将可以看到。
实验3.Activity1->Activity2
输出结果:
从实验3可以看到系统在Activity1的onPause()之后先将Activity2创建并显示出来,然后在执行的Activity1后续的操作,onPause()之后Activity1就不再是最前端显示了。
实验4.Activity1->Activity2->Activity1
输出结果:
![【原创】【Android笔记】Activity不同状态转换研究 【原创】【Android笔记】Activity不同状态转换研究](http://s8.sinaimg.cn/bmiddle/5ffeae36t73219fca6557&690)
实验4要注意的是,由于我们使用的是
Intent it = new Intent(...)
startActivity(it);
的方式,实际上Activity2->Activity1执行的是创建了一个新的Activity1实例,这和一开始的Activity1不是同一个,也就是说,这里Activity栈里已经有了3个Activity实例,这一点很容易被弄错,可能界面上是好像回到了Activity1,但实际上完全不同。
实验5.Activity1->Activity2->Activity1->Back键
输出结果:
实验5承接了实验4,只是多按了Back键,这时候系统销毁了一个Activity1实例,栈里还有两个,栈顶的是Activity2,也就是说界面上看见的是Activity2.
实验6.Activity1->Activity2->Home键->Activity1
输出结果: ![【原创】【Android笔记】Activity不同状态转换研究 【原创】【Android笔记】Activity不同状态转换研究](http://s16.sinaimg.cn/bmiddle/5ffeae36t7321a8d3950f&690)
实验6的特别地方在于,在按了Home键回到主界面后,Activity2也被转到后台,程序走到了onStop(),然后我们重新启动Activity1应用,可以看到执行onRestart()的是Activity2,而不是Activity1!!这是因为此时Activity2处在栈顶,这一点也是很容易出错的地方。
实验7.Activity1->屏幕旋转90度
输出结果:
实验7单独列出来也是因为代表了一类Activity被打断的情况,就是系统配置发生了变化,官方文档里也对为什么一定要采用这种先销毁再重新创建的方式做了解释,可以参考Configuration
Changes一段,我就不再多介绍了,这里只是实际跑了个实验验证。
----------------------------------------------------------------------------------------------
实验就做这么多,其实还可以做更多一些情况的测试,但基本的转换情况这里都已提到。虽然运行,暂停,退出的基本概念大家比较清楚,但是从上面的实验也可以看到,一些细节仍然需要在设计时考虑清楚,因为毕竟我们无法估计用户的操作序列和系统本身的各种情况,如果要想使我们的应用够健壮,必须充分考虑到Activity不同状态间转换的问题。
此外,还有一些可以深入探讨的问题:
1. 关于finish()的使用,上面的实验没有用到它,但我们看到前面的实验只有在按Back键的时候系统会销毁当前的Activity,如果处理不当会产生大量Activity新的实例,而显然很多不是我们需要的,必须以合适的方式在恰当的时机消除,这时就需要finish(),函数使用虽然简单,但是要用在什么时机和地方是需要考虑的。
2. 关于onSaveInstanceState()的使用,我们前面的实验也说明了,不是任何时候系统都会调用onSaveInstanceState()的,原因官方文档也提了一些,请仔细阅读,官方举的一个例子是当一个Activity不再处于最前端时,如果系统由于内存原因需要关掉这个Activity,此时系统会调用onSaveInstanceState(),这样我们才有机会做些保存信息以便将来重启的工作。
3.关于onPause()函数,由于Activity从最前端显示到其他状态的转换会很频繁,这个函数也会很频繁的被调到,而它是一个同步函数,这个操作不完成,后面的操作无法进行,因此请不要在其中做太多操作,造成性能问题。
关于Activity的状态转换还有很多可以研究的地方,以后有机会再贴出来。