Android Activity生命周期 超详解
生命周期
四大启动模式
标识位
Task栈
Android
1.初识 Activity
- 四大组件之一
- 与用户交互的接口,提供了一个用户完成相关操作的窗口
- 通过生命周期的方法来管理自身的创建与销毁
- activity 是 Context 的子类
2.Android 任务栈( Task )
- 栈结构,后进先出,用于存放 Acitivity 组件
浅显理解:
- 打开一个新的Activity ---> 在任务栈的结构中添加一个Activity组件
- 退出当前Activity ---> 任务栈的结构中减少一个Activity组件
- 一个任务栈包含了一个activity的集合
android系统可以通过Task有序地管理每个activity,并决定哪个Activity与用户进行交互:
只有在任务栈栈顶的activity才可以跟用户进行交互(这里的交互就是指点击、输入文字等操作)
- Task和activity默认启动方式
启动一个Application的时候,系统会为它默认创建一个对应的Task,用来放置根Activity(应用程序启动的第一个Activity)。默认启动Activity会放在同一个Task中,新启动的Activity会被压入启动它的那个Activity的栈中,并且显示它。当用户按下回退键时,这个Activity就会被弹出栈,按下Home键回到桌面,再启动另一个应用,这时候之前那个Task就被移到后台,成为后台任务栈,而刚启动的那个Task就被调到前台,成为前台任务栈,Android系统显示的就是前台任务栈中的Top实例Activity。
ps
:activity还有其他启动方式,在文章末尾介绍
- task是可以跨应用的
有的Activity,虽然不在同一个app中,但为了保持用户操作的连贯性,把他们放在同一个任务(Task)中。
例如,在我们的应用中的一个Activity A中点击发送邮件,会启动邮件程序的一个Activity B来发送邮件,这两个activity是存在于不同app中的,但是被系统放在一个任务(Task)中,这样当发送完邮件后,用户按back键返回,可以返回到原来的Activity A中,这样就确保了用户体验。
- 退出应用时必须清空栈内所有Activity,才能销毁Task
3. Activity的四种形态
状态名 | 解释 | 状态 |
---|---|---|
Active/Running | 运行状态:位于栈顶,正好处于屏幕最前方 | 可见+可交互 |
Paused | 暂停状态:失去了焦点,不能与用户交互,但仍然可见(比如栈顶的 Activity是透明或没有铺满屏幕) | 可见+不可交互 |
Stopped | 停止状态:对用户不可见,也不能交互 | 不可见+不可交互 |
Killed | 销毁状态:由于人为或者系统原因被销毁 | 不可见+不可交互 |
在每个状态,Android 系统对 Activity 都写了相应的回调方法。因此,我们在程序中写 Android时,一般都是继承 Activity 时,一般都是继承 Acitivity 类并重写相应的回调方法
4. Activity的生命周期
4.1 Activity的正常一生
oncreate()->onstart()->onResume()->onRestart()->onPouse()->onStop()->onDestory()
Activity 的生命周期.png
onCreat()
: 当我们点击activity的时候,系统会调用activity的oncreate()方法,在这个方法中我们会初始化当前布局setContentLayout()方法。
onStart()
:onCreate()方法完成后,此时activity进入onStart()方法,当前activity是用户可见状态,但没有焦点,与用户不能交互,一般可在当前方法做一些动画的初始化操作
onResume()
:onStart()方法完成之后,此时activity进入onResume()方法中,当前activity状态属于运行状态 (Running),可与用户进行交互。
onPause()
:当另外一个activity覆盖当前的acitivty时([1]启动了一个新的Activity [2]返回上一个Activity),此时当前activity会进入到onPause()方法中,当前activity是可见的,但不能与用户交互状态。通常情况下onPause()函数不会被单独执行,执行完onPause()方法后会继续执行onStop()方法。
onStop()
:onPause()方法完成之后,此时activity进入onStop()方法,此时activity对用户是不可见的,在系统内存紧张的情况下,有可能会被系统进行回收。所以一般在当前方法可做资源回收。(ps:只要activity还可见,就不会调用onStop,比如下一个开启的activity是完全透明度)
onDestory()
:onStop()方法完成之后,此时activity进入到onDestory()方法中,结束当前activity。
执行完onDestory()方法的Activity接下来面对的是被GC回收,宣告生命终结。
onRestart()
:当用户在其他的Activity或者桌面回切到这个Activity时,这个Activity就会先去执行onRestart()函数。调用顺序onPause()->onStop()->onRestart()->onStart()->onResume().
生命周期和状态.pngonCreate和onDestory是配对的,分别标识着Activity的创建和销毁,并且只可能有一次调用。
从Activity是否可见来说,onStart和onStop是配对的,随着用户的操作或者设备屏幕的点亮和熄灭,这两个方法可能被调用多次;
从Activity是否在前台来说,onResume和onPause是配对的,Activity位于其他 Activity 之前,可与用户交互并具有输入焦点。
4.2 Acitivity的异常生命周期
- 横竖屏切换
当Activity处于竖屏状态,如果突然旋转屏幕,由于系统配置发生了变化,在默认的情况下,Activity会被销毁并重新创建,当然我们可以人为干预来防止这种情况。
这里有点需要特别注意,onSaveInstanceState和onRestoreInstanceState只有在Activity异常终止时才会被调用的,正常情况是不会调用这两个方法的。
-
内存不足导致优先级低的 Activity 被杀死
image.png
5. Android的进程层次
进程层次.pngactivity的进程优先级:前台进程>可见进程>service进程>后台进程>空进程
5.1 前台进程
当前进程activity正在与用户进行交互。
当前进程service正在与activity进行交互或者当前service调用了startForground()属于前台进程或者当前service正在执行生命周期(onCreate(),onStart(),onDestory())
进程持有一个BroadcostReceiver,这个BroadcostReceiver正在执行onReceive()方法
5.2 可见进程
进程持有一个activity,这个activity不再前台,处于onPouse()状态下,当前覆盖的activity是以dialog形式存在的。
进程有一个service,这个service和一个可见的Activity进行绑定。
5.3 service进程
当前开启startSerice()启动一个service服务就可以认为进程是一个服务进程。
5.4 后台进程
activity的onStop()被调用,但是onDestroy()没有调用的状态。该进程属于后台进程。
5.5 空进程
进程没有任何运行的数据了,且保留在内存空间,并没有被系统killed,属于空进程。该进程很容易被杀死。
6. Activity 的四种启动模式
6.1 Standard 模式
Standard.png标准模式,也是系统的默认模式(可以不指定),在这样模式下,每启动一个Activity都会重新创建一个Activity的新实例,并且将其加入任务栈中,而且完全不会去考虑这个实例是否已存在。
6.2 singleTop 模式
isingleTop1.png栈顶复用模式,顾名思义,在这种模式下,如果有新的Activity已经存在任务栈的栈顶,那么此Activity就不会被重新创建新实例,而是复用已存在任务栈栈顶的Activity。这里重点是位于栈顶,才会被复用,如果新的Activity的实例已存在但没有位于栈顶,那么新的Activity仍然会被重建。
singleTop2.png当需要新创建的MainActivity位于栈顶时,MainActivity并没有重新创建。下面我们再来看看新创建的MainActivity没有位于栈顶的情况。
嗯,这就是singTop模式。这种模式通常比较适用于接收到消息后显示的界面,如qq接收到消息后弹出Activity界面,如果一次来10条消息,总不能一次弹10个Activity,是吧?再比如新闻客户端收到了100个推送,你每次点一下推送他都会进入某个activiy界面(显示新闻只用一个activity,只是内容不同而已),这时也比较适合使用singleTop模式。
6.3 singleTask 模式
singleTask.png栈内复用模式。这是一种单例模式,与singTop点类似,只不过singTop是检测栈顶元素是否有需要启动的Activity,而singTask则是检测整个栈中是否存在当前需要启动的Activity,如果存在就直接将该Activity置于栈顶,并将该Activity以上的Activity都从任务栈中移出销毁,同时也会回调onNewIntent方法。情况如下图:
从图中可以看出,当我们再次启动MainActivity时,由于MainActivity位于栈中,所以系统直接将其置于栈顶,并移除其上方的所有Activity。当然如果所需要的MainActivity不存在栈中,则会创建新的Activity并添加到栈中。singleTask 模式比较适合应用的主界面activity(频繁使用的主架构),可以用于主架构的activity,(如新闻,侧滑,应用主界面等)里面有好多fragment,一般不会被销毁,它可以跳转其它的activity 界面再回主架构界面,此时其他Activity就销毁了。当然singTask还有一些比较特殊的场景这个我们后面会一一通过情景代码分析。
6.4 singleInstance 模式
singleInstance.png在singleInstance模式下,该Activity在整个android系统内存中有且只有一个实例,而且该实例单独尊享一个Task。换句话说,A应用需要启动的MainActivity 是singleInstance模式,当A启动后,系统会为它创建一个新的任务栈,然后A单独在这个新的任务栈中,如果此时B应用也要激活MainActivity,由于栈内复用的特性,则不会重新创建,而是两个应用共享一个Activity的实例。如下图所示:
从图中我们可以看到最终AB应用都共享一个singleInstance模式的MainActivity,也没有去重新创建。
ps
:启动 Activity 时,先看是否已经存在它需要的栈,没有则创建一个空栈,再在该栈中添加该 Activity 组件。比如在标准模式下,点开 app,启动根 Activity,是没有已经存在的栈,就创建一个栈,并向该栈中添加根 Activity 组件,此时再从根 Activity 启动另一个 ActivityB,对于B来说已经它需要的栈(即刚才存放根 Activity 的栈),就不用再重新创建栈了。但是如果将 ActivityB 的启动模式改成singleInstancce,则需要再重新创建一个栈
7.设置 Activity 的启动方式
7.1通过AndroidMenifest.xml文件为Activity指定启动模式
<activity android:name=".ActivityC"
android:launchMode="singleTask" />
7.2 通过在Intent中设置标志位(addFlags方法)来为Activity指定启动模式
Intent.FLAG_ACTIVITY_SINGLE_TOP 该标志位表示使用singleTop模式来启动一个Activity,与在清单文件指定android:launchMode="singleTop"效果相同。
Intent.FLAG_ACTIVITY_CLEAR_TOP 该标志位表示使用singleTask模式来启动一个Activity,与在清单文件指定android:launchMode="singleTask"效果相同。
Intent.FLAG_ACTIVITY_NO_HISTORY 使用该模式来启动Activity,当该Activity启动其他Activity后,该Activity就被销毁了,不会保留在任务栈中。如A-B,B中以这种模式启动C,C再启动D,则任务栈只有ABD。
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 使用该标识位启动的Activity不添加到最近应用列表,也即我们从最近应用里面查看不到我们启动的这个activity。与属性android:excludeFromRecents="true"效果相同。
Intent intent = new Intent();
intent.setClass(ActivityB.this,ActivityA.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
注
第2种指定方式的优先级高,同时存在时,以第2种为准。
思考问题
- 问题1
situation1.pngABCD四个Activity的启动模式均为SingleTask。此时,先启动CD再启动AB,之后由B启动D。请问现在的后退列表是什么样子的?
result1.png按照DCBA的顺序退出
- 问题2
situation2.pngABCD四个Activity的启动模式均为SingleTask。此时,先启动CD再启动AB,之后由B启动C。请问现在的后退列表是什么样子的?
result2.png按照CBA的顺序退出