掌握生命周期对开发者来说很基础,但至关重要,当理解好了生命周期,才可以写出连贯流畅的程序,并且能掌握如何管理应用程序的资源,得到更好的用户体验。
什么是Activity(活动)?
所谓活动就是吸引用户的地方,活动包括一系列用户界面,其作用就是呈现给用户信息,并且用户可以在上面进行交互。一个应用可以包含一个或多个活动,没有活动的应用几乎是没有的,因为应用要提供用户界面进行交互.
经常玩手机应用的人就会发现,Android中的Activity是可叠加的,每次新建一个Activity活动,新的活动就会覆盖在旧的活动上面,当点击返回按钮或者Back键时,最前面的活动就会销毁让下面的Activity回到上面。
Android使用任务(Task)来管理活动群,一个任务就是一组存放在栈里的活动集合,这个栈也被称作任务栈(Task Stack)或者返回栈(Back Stack).
栈是什么?
是一种后进先出(Last in First out:LIFO)的数据结构,这个数据结构有点类似于一个水桶,先进去的东西只能最后出来。形象一点,栈只有一个出口,先进去的就在最底下,后进来的就在最前面,要是拿出来的话,肯定是从开口端拿出去,所以说先进后出,后进先出。
那么说一下Android的任务栈,默认情况下,每当我们启动一个新的活动,它会在任务栈中入栈,并处于栈顶的位置。而每当我们按下Back键去销毁一个活动时,处于栈顶的活动会出栈,这是前一个入栈的活动就会重新处于栈顶的位置。系统总会显示处于栈顶的(Activity)活动给用户。下图为基本原理。
下面了解Activity活动的状态:
每一个活动在其生命周期中会有以下四种状态。
1.运行状态:
这种状态就是当活动在栈顶位置的时候了。系统这个时候是最不愿意回收处于运行状态的活动的,因为当你正在操作的时候,交互界面突然没了,这会带来非常差劲的用户体验。
2. 暂停状态
一个活动不再处于栈顶位置, 但仍然可见时, 这时活动就进入了暂停状态。 你可能会觉得既然活动已经不在栈顶了, 还怎么会可见呢?当然并不是每个处于栈顶的Activity都会沾满屏幕的,比如对话框就不会。处于暂停状态的活动仍然是完全存活着的, 系统也不愿意去回收这种活动(因为它还是可见的,回收可见的东西都会在用户体验方面有不好的影响),只有在内存极低的情况下,系统才会去考虑回收这种活动。(下图背后的选择用户Activity就处于暂停状态,因为前面的对话框处于栈顶位置)。
![share](http://miketech.it/wp-content/uploads/2015/08/share.jpg)
3. 停止状态
当一个活动不再处于栈顶位置, 并且完全不可见的时候, 就进入了停止状态。 系统仍然会为这种活动保存相应的状态和成员变量, 但是这并不是完全可靠的, 当其他地方需要内存时,处于停止状态的活动有可能会被系统回收。所以很早的安卓手机有时候你一点返回键发现呈现的界面被刷新了,就是因为内存不够,那个活动被回收了,所以当你按下返回键后那个活动会被重新加载。
4. 销毁状态
当一个活动从返回栈中移除后就变成了销毁状态。 系统会最倾向于回收处于这种状态的活动,从而保证手机的内存充足。这个是实际开发时候才会用到,我们调用finish()方法来手动结束一个Activity。(下图是我开发时候对于用户没有联网的一个处理,这个判断语句在没有联网的时候会手动销毁那个新创建的活动)
Activity 类中定义了七个回调方法,覆盖了活动生命周期的每一个环节
1. onCreate()
每个活动中我们都重写了这个方法, 它会在活动第一次被创建的时候调用。 你应该在这个方法中完成活动的初始化操作, 比如说加载布局、绑定事件等。
2. onStart()
这个方法在活动由不可见变为可见的时候调用。
3. onResume()
这个方法在活动准备好和用户进行交互的时候调用。 此时的活动一定位于返回栈的栈顶,并且处于运行状态。
4. onPause()
这个方法在系统准备去启动或者恢复另一个活动的时候调用。 我们通常会在这个方法中将一些消耗 CPU 的资源释放掉(比如显示地图或者大规模图形),以及保存一些关键数据(比如用户输入的数据等等),但这个方法的执行速度一定要快,不然会影响到新的栈顶活动的使用。
5. onStop()
这个方法在活动完全不可见的时候调用。它和 onPause()方法的主要区别在于,如果启动的新活动是一个对话框式的活动,那么 onPause()方法会得到执行,而 onStop()方法并不会执行。
6. onDestroy()
这个方法在活动被销毁之前调用,之后活动的状态将变为销毁状态。
7. onRestart()
这个方法在活动由停止状态变为运行状态之前调用,也就是活动被重新启动了。
以上七个方法中除了 onRestart()方法, 其他都是两两相对的, 从而又可以将活动分为三种生存期。
1. 完整生存期
活动在 onCreate()方法和 onDestroy()方法之间所经历的,就是完整生存期。一般情况下,一个活动会在 onCreate()方法中完成各种初始化操作,而在 onDestroy()方法中完成释放内存的操作。
2. 可见生存期
活动在 onStart()方法和 onStop()方法之间所经历的,就是可见生存期。在可见生存期内, 活动对于用户总是可见的, 即便有可能无法和用户进行交互。 我们可以通过这两个方法,合理地管理那些对用户可见的资源。比如在 onStart()方法中对资源进行加载,而在 onStop()方法中对资源进行释放, 从而保证处于停止状态的活动不会占用过多内存。
3. 前台生存期
活动在 onResume()方法和 onPause()方法之间所经历的,就是前台生存期。在前台生存期内, 活动总是处于运行状态的, 此时的活动是可以和用户进行相互的, 我们平时看到和接触最多的也这个状态下的活动。
以下是谷歌官方的演示图
![activity_lifecycle](http://miketech.it/wp-content/uploads/2015/08/activity_lifecycle.png)
下面再聊聊Activity的启动方式:
Activity有四种启动方式:standard,singleTop,singleTask,singleInstance
根据具实际的需求为Activity设置对应的启动模式,从而避免创建大量重复的Activity问题。
设置Activity的启动模式,只要在AndroidManifest.xml里对应的<activity>标签设置android:lanuchMode属性,例如:
<activity
android:name=".A1"
android:launchMode="standard" />
下面说一下四种模式的作用区别(
基本是面试必考点):
standard
默认模式,可以不用写配置。在这个模式下,都会默认创建一个新的实例。因此,在这种模式下,可以有多个相同的实例,也允许多个相同Activity叠加。
例如:
若我有一个Activity名为A1, 上面有一个按钮可跳转到A1。那么如果我点击按钮,便会新启一个Activity A1叠在刚才的A1之上,再点击,又会再新启一个在它之上……
点back键会依照栈顺序依次退出。
singleTop
可以有多个实例,但是不允许多个相同Activity叠加。即,如果Activity在栈顶的时候,启动相同的Activity,不会创建新的实例,而会调用其onNewIntent方法。
例如:
若我有两个Activity名为B1,B2,两个Activity内容功能完全相同,都有两个按钮可以跳到B1或者B2,唯一不同的是B1为standard,B2为singleTop。
若我意图打开的顺序为B1->B2->B2,则实际打开的顺序为B1->B2(后一次意图打开B2,实际只调用了前一个的onNewIntent方法)
若我意图打开的顺序为B1->B2->B1->B2,则实际打开的顺序与意图的一致,为B1->B2->B1->B2。
singleTask
只有一个实例。在同一个应用程序中启动他的时候,若Activity不存在,则会在当前task创建一个新的实例,若存在,则会把task中在其之上的其它Activity destory掉并调用它的onNewIntent方法。
如果是在别的应用程序中启动它,则会新建一个task,并在该task中启动这个Activity,singleTask允许别的Activity与其在一个task中共存,也就是说,如果我在这个singleTask的实例中再打开新的Activity,这个新的Activity还是会在singleTask的实例的task中。
例如:
若我的应用程序中有三个Activity,C1,C2,C3,三个Activity可互相启动,其中C2为singleTask模式,那么,无论我在这个程序中如何点击启动,如:C1->C2->C3->C2->C3->C1-C2,C1,C3可能存在多个实例,但是C2只会存在一个,并且这三个Activity都在同一个task里面。
但是C1->C2->C3->C2->C3->C1-C2,这样的操作过程实际应该是如下这样的,因为singleTask会把task中在其之上的其它Activity destory掉。
操作:C1->C2 C1->C2->C3 C1->C2->C3->C2 C1->C2->C3->C2->C3->C1 C1->C2->C3->C2->C3->C1-C2
实际:C1->C2 C1->C2->C3 C1->C2 C1->C2->C3->C1 C1->C2
若是别的应用程序打开C2,则会新启一个task。
如别的应用Other中有一个activity,taskId为200,从它打开C2,则C2的taskIdI不会为200,例如C2的taskId为201,那么再从C2打开C1、C3,则C2、C3的taskId仍为201。
注意:如果此时你点击home,然后再打开Other,发现这时显示的肯定会是Other应用中的内容,而不会是我们应用中的C1 C2 C3中的其中一个。
singleInstance
只有一个实例,并且这个实例独立运行在一个task中,这个task只有这个实例,不允许有别的Activity存在。
例如:
程序有三个ActivityD1,D2,D3,三个Activity可互相启动,其中D2为singleInstance模式。那么程序从D1开始运行,假设D1的taskId为200,那么从D1启动D2时,D2会新启动一个task,即D2与D1不在一个task中运行。假设D2的taskId为201,再从D2启动D3时,D3的taskId为200,也就是说它被压到了D1启动的任务栈中。
若是在别的应用程序打开D2,假设Other的taskId为200,打开D2,D2会新建一个task运行,假设它的taskId为201,那么如果这时再从D2启动D1或者D3,则又会再创建一个task,因此,若操作步骤为other->D2->D1,这过程就涉及到了3个task了。
总结:
standard 每次都会新建,每个Task都可以有,且每个Task都可以有多个实例(每个Task都可以有,且可以有多个)
singleTop 当前实例如果在栈顶,就不新建实例,调用其OnNewIntent。 如不在栈顶,则新建实例 (每个Task都可以有,且可以有多个,在栈顶时可复用)
singleTask 新建一个Task,如果已经有其他的Task并且包含该实例,那就直接调用那个Task的实例。(只有一个Task中会有)
singleInstance 新建一个Task,且在该Task中只有它的唯一一个实例。 (只有一个Task会有,且该Task中只有它)
Activity跳转Flag
在 android.content.Intent 中一共定义了20种不同的 flag,其中和 Task 紧密关联的有四种:
1.FLAG_ACTIVITY_NEW_TASK
2.FLAG_ACTIVITY_CLEAR_TOP
3.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
4.FLAG_ACTIVITY_SINGLE_TOP
使用这四个 flag 时,一个 Intent 可以设置一个 flag,也可以选择若干个进行组合。
在传递给 startActivity() 的 Intent 对象里包含了 FLAG_ACTION_NEW_TASK,情况将发生变化,–系统将为新的 Activity “寻找”一个不同于调用者的 Task。不过要找的 Task 是不是一定就是 NEW 呢?如果是第一次执行,则这个设想成立,如果说不是,也就是说已经有一个包含此 Activity 的Task 存在,则不会再启动 Activity。
如果 flag 是 FLAG_ACTIVITY_CLEAR_TOP,同时当前的 Task 里已经有了这个 Activity,那么情形又将不一样。Android 不但不会启动新的 Activity 实例,而且还会将 Task 里 该 Activity 之上的所有 Activity 一律结束掉,然后将 Intent 发给这个已存在的 Activity。Activity 收到 Intent 之后,可以在 onNewIntent() 里做下一步的处理,也可以自行结束然后重新创建自己。如果 Activity 在 AndroidMainifest.xml 里将启动模式设置成”multiple”,– 默认模式,并且 Intent 里也没有设置 FLAG_ACTIVITY_SINGLE_TOP,那么它将选择后者。否则,它将选择前者。FLAG_ACTIVITY_CLEAR_TOP 还可以和 FLAG_ACTION_NEW_TASK 配合使用。
如果 flag 设置的是 FLAG_ACTIVITY_SINGLE_TOP,则意味着如果 Activity 已经是运行在 Task 的 top,则该 Activity 将不会再被启动。
设置intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
参考:http://miketech.it/androidactivitylifecircle/