有关Activity介绍的文字实在是太多了,本着对Android开发基础知识进行查漏补缺的原则,还是将这部分的知识梳理成了这篇文章。
Activity作为Android中最为重要的组件,在汉语中似乎一直没有合适的词语表达。Activity是“界面展示”+“后台逻辑”的结合,用“活动”一词对这一组件进行描述实在是太牵强啦。因此,本文仍然使用Activity来指代这一组件吧。本文从生命周期和启动模式两个部分总结了Activity相关的知识点。
一、 Activity的生命周期
都闪开,首先还是官方文档中那张熟悉得不能再熟悉的生命周期流程图:
虽然图中未列出,Activity在与用户进行交互的过程中,在如下四种状态之间互相切换:
Running: Activity正处于Activity栈最顶端,与用户正在进行交互。(完全可见)
Paused:与Running状态不同,Activity此时未完全显示出来,例如被一个dialog或者透明的Activity所部分遮盖,无法与用户交互,但仍然保持着状态信息数据。(部分可见)
Stopped:当Activity被其他Activity完全覆盖不可见,此时不再处于Activity栈顶端。(完全不可见)
Destroyed:当Activity被系统回收或者被用户关闭,则会被系统销毁掉。
如图所示在这四种状态的切换过程中,系统将会回调不同的方法:
onCreate():该方法在Activity第一次被创建的时候被回调,我们通常在该方法中进行一些静态初始化工作,例如加载页面布局,创建View,绑定数据等等。
onStart():在Activity被启动或重新启动过程中会调用该方法。
onResume():在Activity能够与用户交互之前调用此方法。我们通常会在该方法中初始化并打开一些独占设备,例如相机,重新初始化在onPause方法中释放的资源等。
onPause():当系统开始调用其他Activity时会回调此方法。我们通常会在其中释放那些在onResume中打开的资源,确认对持久性数据的未保存更改、停止动画、释放sensor,reciever,ContentProvider保存数据,以及其他可能消耗 CPU 的内容。需要注意的是这里进行的操作不能太耗时,因为它返回后,下一个 Activity 才能继续执行。该方法与onResume方法相对应,这两对方法从Activity是否可交互的角度来回调的。
onStop():当系统要停止或覆盖该Activity时会回调此方法,此时Activity已经不可见了,该方法与onStart方法相对应,这两对方法是从Activity是否可见的角度来回调的。
onRestart():当Activity未被完全销毁掉,需要重新启动该Activity时候将会在onStop和onStart方法之间回调该方法。
onDestroy():当系统要销毁掉Activity前会调用该方法。无论是Activity是人为finish掉还是系统由于资源短缺而回收,都会回调该方法。这里可以进行一些资源释放和回收工作。
上述方法的回调顺序都是在Activity处于正常的生命周期内,而当Activity配置发生改变或系统资源紧张对Activity进行了回收时,Activity则会被销毁,抑或重新创建。为了应对Activity非正常生命周期内的情况,系统使用了onSaveInstanceState和onRestoreInstanceState两对方法来完成对Activity状态信息和数据的存储和恢复。这对方法在Activity正常的生命周期中是不会被调用的,只有当资源不足或配置改变而导致的异常销毁重建时才会调用。
在这两个方法中,系统自动做了一定的存储和恢复工作,我们也可以将我们需要保存的数据存于Bundle中,从而保证我们Activity的强壮性。
二、 Activity的任务栈和启动模式
Android系统采用了栈结构来组织和存储Activity实例对象集合,这样的栈结构被称为任务栈(Task Stack)。一个任务栈中的Activity可以来自不同的App,而同一个App的不同Activity也可以存在于不同的任务栈中。栈结构采用FIFO的组织模式,当前正与用户交互的Activity位于栈顶,该任务栈也位于前台。
为了满足开发中的各种特殊需求,Activity可以采用四种模式完成启动:
standard 默认的启动模式。每次都要新创建Activity的实例对象,然后进入原Activity所在的的任务栈,覆盖在原Activity之上。(在这种启动模式下,哪个Activity启动了新的Activity,那么这个新的Activity就运行在哪个Activity所在任务栈中。)
singleTop 栈顶复用模式。在这种启动模式下,如果当前任务栈顶的Activity与要启动的Activity相同,则不创建新的的Activity对象(自然也不会回调生命周期中的方法),而是复用当前栈顶的Activity对象,但仍然会调用onNewIntent()方法。如果栈顶Activity与要启动的Activity不同,则创建新的Activity实例对象,并入栈。这种启动模式通常用于接收消息点击后所显示的Activity。
singleTask 栈内复用。在这种启动模式下,系统首先判断当前任务栈中是否存在要启动的Activity实例对象。如果当前栈中已经存在该Activity实例对象,则覆盖在该Activity实例对象之上的Activity都要出栈并销毁,从而复用已经存在的Activity对象,但仍然会调用onNewIntent()方法。使用该模式时请注意Activity所要运行的任务栈,该模式具有创建不存在的任务栈的作用。
另外有一点需要注意的是,当所启动的Activity实例对象并未位于当前任务栈,而是位于后台任务栈中,则该后台任务栈中的Activity实例对象都被切换到前台任务栈中,并仍按原栈中顺序组织和存储。
singleInstance 单例模式。使用该模式启动的Activity,系统为该Activity新建一个任务栈来运行,从而可以满足多个应用共享该Activity实例的需求。
在Activity实例对象的创建和启动过程中,系统必然要通过某种方式指定是所启动的Activity实例对象,以及该对象所属的任务栈。通常情况下,Activity A所启动了Activity B,如无特殊指定,系统会将B运行于A所属的任务栈中。
Android系统使用TaskAffinity参数标识了该Activity对象期望所属的任务栈(默认为该app的包名),仅仅是期望,而不是一定要运行在该任务栈中,如果该任务栈还不存在,未必会创建该任务栈(singleTask和singleInstance模式会创建)。singleTask这种栈内复用模式就是在TaskAffinity所指定的栈内进行复用。TaskAffinity参数通常与allowTaskReparenting结合使用。
我们可以通过manifest文件和Intent Flag两种方式来指定Activity的启动模式,前者的优先级低于后者。常用的Intent Flag包括以下几种方式:
Intent.FLAG_ACTIVITY_NEW_TASK:新创建一个任务栈来运行Activity,通常用于service启动Activity的场景。
Intent.FLAG_ACTIVITY_SINGLE_TOP:以singleTop模式启动Activity。
Intent.FLAG_ACTIVITY_CLEAR_TOP:通常与Intent.FLAG_ACTIVITY_NEW_TASK相结合使用,达到singleTask模式的效果。
Intent.FLAG_ACTIVITY_NO_HISTORY:以该模式启动的Activity,当该Activity启动其他Activity后就会被销毁掉,不存储于任务栈中。
另外,我们还可以在manifest文件中指定clearTaskOnLaunch属性,保证每次返回该Activity时,其上的Activity都被清除掉,从而保证该Task每次初始化时都只有这个一个Activity。
Note:
良好的编程习惯:在你负责开发的activity中加入启动该activity的方法。
参考书目:
Android官方文档
《Android群英传》 作者:徐宜生
《Android开发艺术探索》 作者:任玉刚