Activity作为Android四大组件之一,它有着举足轻重的地位。一般的情况下一个用户交互界面对应一个Activity,它为用户提供一个可视的界面,方便用户操作,比如说拔打电话、照相、发邮件或者是浏览地图等。
开发常用的
Activity
有FragmentActivitiy、ListActivity、PreferenceActivity、TabAcitivty...
如果界面有共同的特点或者功能的时候,还会自己定义一个BaseActivity
。
- 每一个Activity都会获得一个用于绘制其用户界面的窗口,一般情况下这个窗口会覆盖整个屏幕,但在某此情况下也会出现一些比屏幕小的窗口飘浮在另外一个窗口上面。
Activity是一个view对象的容器,通过Window类的setContentView()方法添加到Activity上
,最终提供与用户交互的界面;- Activity是上下文对象 Context的子类, 并且实现了
Window.callback,KeyEvent.callback 这两个接口
,因此 Activity 可以响应和处理用户与窗口的交互事件,以及键盘相关的输入事件;- 一个Android应用通常由一个或者多个Activity 组成;如果是多个Activity,一般会指定应用中的某个 Activity 为“主” Activity,即首次启动应用时呈现给用户的那个 Activity。 而且每个Activity
通过使用Intent(意图)均可启动另一个Activity,这里使用的是IPC机制
。- 每次启动一个新的Activity 启动时,前一 Activity 便会停止,但系统会在回退桟中保留该 Activity,而新启动的Activity可以放在该栈顶,也可以设置其启动方式放入新的回退桟,这就和Activity四种启动方式有关了。(这里要了解 栈 Stack)
附上一张图,对Activity有一个更直观和全面的认识: 这里写图片描述
对Activity有了一个比较直观的认识后,我们自然想到的是怎么使用它,其实对于Activity Google对其封装的还是挺厉害,开发者使用时主要就是继承Activity,通过setContentView()方法设置布局界面,然后重写它的几个生命周期函数;而跳转新的Acitivty更简单,通过一个Intent然后调用startActivity方法即可。
Activity是通过回退桟来管理的,此时Activity可呈现出四种状态:
Running状态
:一个新的Activity启动入栈后,它在屏幕最前端,处于栈的最顶端,此时它处于可见并可和用户交互的激活状态,可以用来响应用户的输入。Paused状态
:当Activity被另一个透明或者Dialog样式的Activity覆盖时的状态。此时它依然与窗口管理器保持连接,系统继续维护其内部状态,它仍然可见,但它已经失去了焦点,故不可与用户交互。Stopped状态
:当Activity不可见时,Activity处于Stopped状态。当Activity处于此状态时,一定要保存当前数据和当前的UI状态,否则一旦Activity退出或关闭时,当前的数据和UI状态就丢失了。Killed状态
:Activity被杀掉以后或者被启动以前,处于Killed状态。这是Activity已从Activity堆栈中移除,需要重新启动才可以显示和使用。4种状态中,Running状态和Paused状态是可见的(Running相当于Resumed
),Stopped状态和Killed状态时不可见的(Killed相当于Destoryed
)。
在这里,才接触 Android 的人肯定有疑问,什么是生命周期。其实Activity的生命周期就像是人的一生,人从出生到成长,再到衰老,最后到死亡。Activity的生命周期跟人的一生相似,也有出生(创建)-成长(启动)-衰老(暂定)-死亡(销毁)的过程。
这个是Google官方给的一张示意图,这里面就详细介绍了一个Activity的声生命周期的每一个过程。
大体的流程是:onCreate() --> onStart() --> onResume() -->onPause() --> onStop() --> onDestroy()
;
注:onCreate()的官方文档注释,建议 setContentView()、findViewById() 在 onCreate() 中调用,但在onStart()中调用 setContentView()、findViewById() 功能也是正常的,只是不建议,并且很少会这样做;
另外提一点,Google的文档里有写onStart方法可以直接到onStop方法不经过onResume和onPause,我想了一下就在onStart方法里调用了一下finish()方法,果不其然onStart后就直接onStop了,但是没有什么实际意义,只能算是一个脑机急转弯吧。
这里需要提到的一点是,即使一个Activity被销毁后app内部的static变量是不会被销毁的,因为static变量是全局的,activity销毁但是该app的进程并没有被杀死。所以说这一点尤为需要注意我们的static变量的使用,否则稍有不慎再次启动该activity的时候该static变量就会是一个dirty data!
其实Activity生命周期函数可以看成是成对的,onCreate和onDestory成对,onStart和onStop成对,onResume和onPause成对,这在上述每个回调函数的介绍中也可以看出来。
一个Activity的启动顺序:
onCreate()——>onStart()——>onResume()
当另一个Activity启动时:
正常情况:第一个Activity onPause()——>第二个Activity onCreate()——>onStart()——>onResume() ——>第一个Activity onStop()
特殊情况:第二个Activity因为尺寸小没有完全覆盖第一个Activity,或者第二个Activity是透明的, 此时第一个Activity的onStop方法是不会被执行的,即第一个Activity onPause()——>第二个Activity onCreate()——>onStart()——>onResume()
当返回到第一个Activity时:
第二个Activity onPause() ——> 第一个Activity onRestart()——>onStart()——>onResume() ——>第二个Activity onStop()——>onDestroy()
一个Activity的销毁顺序:
(情况一)onPause()——>
(情况二)onPause()——>onStop()——>
(情况三)onPause()——>onStop()——>onDestroy()
注:因此应在onPause()和onResume()做保存或者暂停动作。 同时我们应该思考Google工程师为什么要这样设计生命周期的顺序。他们是基于什么样的考虑,这样便于我们更好的理解Activity的生命周期。
同时利用下图对照四种状态来理解生命周期:
这个生命周期跟清单文件里的配置有关系 (注意:横屏、竖屏切换时候生命周期也不一样)
不设置Activity的Android:configChanges时,切屏会重新调用各个生命周期 默认首先销毁当前activity,然后重新加载
1. 切换到横屏
onSaveInstanceState
onPause
onStop
onDestroy
onCreate
onStart
onRestoreInstanceState
onResume
2. 切换到竖屏,销毁了两次
onSaveInstanceState
onPause
onStop
onDestroyonCreate
onStart
onRestoreInstanceState
onResume
onSaveInstanceState
onPause
onStop
onDestroy
onCreate
onStart
onRestoreInstanceState
onResume
设置Activity的android:configChanges="orientation|keyboardHidden|screenSize"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法
3. 修改AndroidManifest.xml,把该Activity添加 android:configChanges="orientation",切横屏,只销毁一次。
onSaveInstanceState
onPause
onStop
onDestroy
onCreate
onStart
onRestoreInstanceState
onResume
4. 再切回竖屏,发现不会再打印相同信息,但多打印了一行onConfigChanged
onSaveInstanceState
onPause
onStop
onDestroy
onCreate
onStart
onRestoreInstanceState
onResume
onConfigurationChanged
5. 更改android:configChanges="orientation"改成android:configChanges="orientation|keyboardHidden"
切横屏,就只打印onConfigChanged
onConfigurationChanged
6. 切回竖屏
onConfigurationChanged
onConfigurationChanged
总结:
1. 不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
2. 设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
3. 设置Activity的Android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行
onConfigurationChanged方法。(执行这个方法,必须设置sdk version 大于等于13.0)
附:设置android:screenOrientation="portrait"则无论手机如何变动,拥有这个属性的activity都将是竖屏显示。
同样的,android:screenOrientation="landscape",为横屏显示。
以上是Activity的正常生命周期,下面是Activity的异常生命周期:
比如说当前Activity处于竖屏状态,如果突然旋转屏幕,由于系统配置发生了改变,在默认情况下,Activity就会被销毁并重建,也可以阻止系统重新创建Activity,生命周期如下:
当系统配置发生改变,Activity会被销毁,其onPause、onStop、onDestory均会被调用。但是因为在异常情况下终止的,所以系统会调用onSaveInstanceState来保存当前Activity状态。这个方法是在onStop之前,与onPause没有固定的时序关系
。当Activity重建的时候系统会把onSaveInstanceState所保存的Bundle作为对象传递给onRestoreInstanceState和onCreate方法。
Activity的onSaveInstanceState和onRestoreInstanceState
- 出现异常情况。
- Activity被销毁,回调onPause -> onStop -> onDestroy。
- 因为Activity是在异常情况下终止的,系统会调用onSaveInstanceState来保存当前Activity的状态。此方法在onStop之前调用。它与onPause没有既定的时序关系。
- Activity被重新创建,回调onCreate -> onStart -> onResume; 系统会将上次异常终止时保存的状态传递onRestoreInstanceState和onCreate。onRestoreInstanceState会在onStart之后被调用。
View的onSaveInstanceState和onRestoreInstanceState
- 出现异常情况。
- Activity被销毁,系统会默认保存当前Activity的视图结构:
- Activity调动onSaveInstanceState去保存数据;
- 然后Activity会委托Window去保存数据;
- Window再委托它的顶级ViewGroup去保存数据;
- 其顶级ViewGroup再一一调用它的子视图View的onSaveInstanceState来保存数据。
- Activity被重新创建,系统会恢复上次异常终止时保存的视图结构,其onRestoreInstanceState流程与上面类似,也是上层委托下层。
总结:
- View的源码中每个View都有onSaveInstanceState和onRestoreInstanceState这两个方法。
- 接收位置可以是onRestoreInstanceState和onCreate方法,区别是:onRestoreInstanceState如果被调用,参数Bundle一定是有值的,在onCreate中需要判断参数是否为null。
- onSaveInstanceState
只有在Activity即将销毁并有机会重新显示时才会调用(切记是有机会重新显示才会调用否则就不调用)
,比如:旋转屏幕,按Home键等,正常销毁的Activity生命周期中不会调用。- 在onSaveInstanceState和onRestoreInstanceState方法中,系统默认做了一定的恢复工作,默认保存Activity的试图结构,并在Acitivity重启后恢复这些数据,比如文本框中的数据,ListView滚动的位置等。
Activity按照优先级从高到低,可分为如下三种:
如果当某项内容发生改变后,不想Activity重新创建,就可以给Activity指定configChanges属性,多个值用“|”连接起来。
android:configChanges="orientation | keyboardHidden"
参考:
http://blog.csdn.net/u010784887/article/details/51669699
http://www.jianshu.com/p/a30cec0f426b
http://blog.csdn.net/liuhe688/article/details/6733407
本文仅是作为学习笔记API,参考查阅,如有错误,欢迎指正,谢谢!