再次回顾四大组件的使用和基础概念,Activity章节第一部分是典型的生命周期7个部分,第二部分是特殊情况下的生命周期。
一、典型的生命周期
img在正常情况下,一个Activity从启动到结束会以如下顺序经历整个生命周期: onCreate()→onStart()→onResume()→onPause()→onStop()→onDestory()。包含了六个部分,还有一个onRestart()没有调用,下面我们一一介绍这七部分内容。
回调方法 | 说明 |
---|---|
onCreate() | 当Activity第一次创建时会被调用。 这是生命周期的第一个方法。 在这个方法中,可以做一些初始化工作,比如调用setContentView去加载界面布局资源,初始化Activity所需的数据。当然也可借助onCreate()方法中的Bundle对象来回复异常情况下Activity结束时的状态 |
onRestart() | 表示Activity正在重新启动。一般情况下,当当前Activity从不可见重新变为可见状态时,onRestart就会被调用。这种情形一般是用户行为导致的,比如用户按Home键切换到桌面或打开了另一个新的Activity,接着用户又回到了这个Actvity。 |
onStart() | 表示Activity正在被启动,即将开始,这时Activity已经出现了,但是还没有出现在前台,无法与用户交互。这个时候可以理解为Activity已经显示出来, 但是我们还看不到 |
onResume() | 表示Activity已经可见了,并且出现在前台并开始活动。需要和onStart()对比,onStart的时候Activity还在后台,onResume的时候Activity才显示到前台 |
onPause() | 表示Activity正在停止,仍可见,正常情况下,紧接着onStop就会被调用。在特殊情况下,如果这个时候快速地回到当前Activity,那么onResume就会被调用(极端情况)。 onPause中不能进行耗时操作,会影响到新Activity的显示。因为onPause必须执行完,新的Activity的onResume才会执行。 |
onStop() | 表示Activity即将停止,不可见,位于后台。可以做稍微重量级的回收工作,同样不能太耗时 |
onDestroy() | 表示Activity即将销毁,这是Activity生命周期的最后一个回调,可以做一些回收工作和最终的资源回收 |
使用说明 | 在平常的开发中,我们经常用到的就是 onCreate() 和onDestory(),做一些初始化和回收操作 |
典型生命周期的普通情况
动作 | 回调顺序 |
---|---|
针对一个特定的Activity,第一次启动 | onCreate()→onStart()→onResume() |
用户打开新的Activiy的时候,上述Activity的回调如下 | onPause()→onStop() |
再次回到原Activity时,回调如下 | onRestart()→onStart()→onResume() |
按back键回退时,回调如下 | onPause()→onStop()→onDestory() |
按Home键切换到桌面后又回到该Actitivy,回调如下 | onPause()→onStop()→onRestart()→onStart()→onResume() |
调用finish()方法后,回调如下 | onDestory()(以在onCreate()方法中调用为例,不同方法中回调不同,通常都是在onCreate()方法中调用) |
二、特殊情况下的生命周期
两种特殊情况如下表:
情况 | 说明 |
---|---|
横竖屏切换 | 在横竖屏切换的过程中,会发生Activity被销毁并重建的过程。 |
资源内存不足导致优先级低的Activity被杀死 | 当系统内存不足时,会按照上述优先级从低到高去杀死目标Activity所在的进程。 |
横竖屏要了解的内容
回调方法 | 说明 |
---|---|
onSaveInstanceState | 在Activity由于异常情况下终止时,系统会调用onSaveInstanceState来保存当前 Activity的状态。这个方法的调用是在onStop之前,它和onPause没有既定的时序关 系,该方法只在Activity被异常终止的情况下调用。 |
onRestoreInstanceState | 当异常终止的Activity被重建以 后,系统会调用onRestoreInstanceState,并且把Activity销毁时 onSaveInstanceState方法所保存的Bundle对象参数同时传递给onRestoreInstanceState和onCreate方法。 因此,可以通过onRestoreInstanceState方法来恢复Activity的状态,该方法的调用时机是在onStart 之后。 |
回调方法 | 差别 |
---|---|
在onRestoreInstanceState来恢复Activity的状态 | onRestoreInstanceState回调则表明其中Bundle对象非空,不用加非空判断。 建议使用onRestoreInstanceState。 |
在onCreate恢复Activity的状态 | onCreate需要非空判断。 |
-
横竖屏切换的生命周期为:onPause()→onSaveInstanceState()→onStop()→onDestroy()→onCreate()→onStart()→onRestoreInstanceState→onResume()
-
如果在AndroidManifest.xml中给Activity制定如下属性:
android:configChanges = "orientation| screenSize"
那么就可以避免横竖屏切换时Activity的销毁和重建。并且此时回调的是如下方法:
@Override public void onConfigurationChanged(Configuration newConfig){ super.onConfigurationChanged(newConfig); }
资源内存不足要了解的内容
Activity优先级的划分和Activity三种运行状态
优先级 | 对应的运行状态 | 说明 |
---|---|---|
前台Activity | Resumed(活动状态) | 正在和用户交互的Activity,优先级最高。 又叫Running状态,这个Activity正在屏幕上显示,并且有用户焦点,就是用户正在操作的界面。 |
可见但非前台Activity | Paused(暂停状态) | 比如Activity中弹出了一个对话框,导致Activity可见但 是位于后台无法和用户交互。 这是一个比较不常见的状态。这个Activity在屏幕上是可见的,但是并不是在屏幕最前端的那个Activity。比如有另一个非全屏或者透明的Activity是Resumed状态,没有完全遮盖这个Activity。 |
后台Activity | Stopped(停止状态) | 已经被暂停的Activity,比如执行了onStop,优先级最低。 当Activity完全不可见时,此时Activity还在后台运行,仍然在内存中保留Activity的状态,并不是完全销毁。这个也很好理解,当跳转的另外一个界面,之前的界面还在后台,按回退按钮还会恢复原来的状态,大部分软件在打开的时候,直接按Home键,并不会关闭它,此时的Activity就是Stopped状态。 |
当系统内存不足时,会按照上述优先级从低到高去杀死目标Activity所在的进程。我们在平常使用手机时,能经常感受到这一现象。这种情况下数组存储和恢复过程和上述情况一致,生命周期情况也一样。
三、启动模式
Activity的管理是采用任务栈的形式,任务栈采用“后进先出”的数据结构。
启动模式 | 说明 | 特殊情况 | 应用场景 |
---|---|---|---|
标准模式standard | 每启动一次Activity,就会创建一个新的Activity实例并置于栈顶。谁启动了这个 Activity,那么这个Activity就运行在启动它的那个Activity所在的栈中。Activity A启动了Activity B,则就会在A所在的栈顶压入一个新的Activity。 | 如果在Service或Application中启动一个Activity,其并没有所谓的任 务栈,可以使用标记位Flag来解决。解决办法:为待启动的Activity指定 FLAG_ACTIVITY_NEW_TASK标记位,创建一个新栈 | 绝大多数Activity。 如果以这种方式启动的Activity被跨进程调用,在5.0 之前新启动的Activity实例会放入发送Intent的Task的栈的顶部,尽管它们属于不同的程序,这似乎有点费解看起来也不是那么合理,所以在5.0之后,上述情景会创建一个新的Task,新启动的Activity就会放入刚创建的Task中,这样就合理多了。 |
栈顶复用singleTop | 如果需要新建的Activity位于任务栈栈顶,那么此Activity的实例就不会重建,而是重 用栈顶的实例。并回调onNewIntent()方法,并且由于不会重建一个Activity实例,则不会回调其他生命周期方法。 | 如果栈顶不是新建的Activity,就会创建该Activity新的实例,并放入栈顶。 | 在通知栏点击收到的通知,然后需要启动一个Activity,这个Activity就 可以用singleTop,否则每次点击都会新建一个Activity。 实际的开发过程中,测试没准给你提过这样的bug:某个场景下连续快速点击,启动了两个Activity。 如果这个时候待启动的Activity使用singleTop模式也是可以避免这个Bug的。同standard模式,如果是外部程序启动singleTop的Activity,在Android 5.0之前新创建 的Activity会位于调用者的Task中,5.0及以后会放入新的Task中。 |
栈内复用singleTask | 该模式是一种单例模式,即一个栈内只有一个该Activity实例。可以通过在AndroidManifest文件的Activity中指定该Activity需要加载到那个栈中,即singleTask的Activity可以指定想要加载的目标栈。singleTask和taskAffinity配合使用,指定开启的Activity加入到哪个栈中。 | 在这种模式下,如果Activity指定的栈不存在,则创建一个栈,并把创建的Activity压入栈内。 如果Activity指定的栈存在,如果其中没有该Activity实例,则会创建Activity并压入栈顶。 如果其中有该Activity实例,则把该Activity实例之上的Activity 清除出栈,重用并让该Activity实例处在栈顶,然后调用onNewIntent()方法。 |
大多数App的主页。 对于大部分应用,当我们在主界面点击回退按钮的时候都是退出应用,那么当我们第一次进入主界面之后,主界面位于栈底,以后不管我们打开了多少个Activity,只要我们再次回到主界面,都应该使用将主界面Activity上所有的Activity移除的方式来让主界面Activity处于栈顶,而不是往栈顶新加一个主界面Activity的实例,通过这种方式能够保证退出应用时所有的Activity都能被销毁。 在跨应用Intent传递时,如果系统中不存在singleTask Activity的实例,那么将创建一个新的Task,然后创建SingleTask Activity的实例,将其放入新的Task中。 |
单例模式singleInstance | 作为singleTask的加强版,打开该Activity时,直接创建一个新的任务栈,并创建该Activity实例放入新栈中。一旦该模式的Activity实例已经存在于某个栈中,任何应用再激活该Activity时都会重用该栈中的实例。 | 无 | 呼叫来电界面。这种模式的使用情况比较罕见,在Launcher中可能使用。或者你确定你需要使Activity只有一个实例。建议谨慎使用。 |
taskAffinity:每个Activity都有taskAffinity属性,这个属性指出了它希望进入的Task。如果一个Activity没有显式地指明该Activity的taskAffinity,那么它的这个属性就等于Application指明的taskAffinity,如果Application也没有指明,那么该 taskAffinity的值就等于包名。
四、Activity的Flags
这里介绍集中常用的、用于设定Activity的启动模式。可以在启动Activity时,通过Intent的addFlags()方法设置。
名称 | 说明 |
---|---|
FLAG_ACTIVITY_NEW_TASK | 效果与指定Activity为singleTask模式一致 |
FLAG_ACTIVITY_SINGLE_TOP | 效果与指定Activity为singleTop模式一致 |
FLAG_ACTIVITY_CLEAR_TOP | 具有此标记位的Activity,当它启动时,在同一个任务栈中所有位于它上面的Activity都要出栈。 如果和singleTask模式一起出现, 若被启动的Activity已经存在栈中,则清除其之上的Activity,并调用该Activity的onNewIntent方法。 如果被启动的Activity采用standard模式,那么该Activity连同之上的所有Activity出栈,然后创建新的Activity实例并压入栈中。 |
补充更多FLAG
Task就是一个任务栈(数据结构),里面用来存放Activity,第一个进去的(Activity)处于栈的最下面,而最后创建的(Activity)则处于栈的最上面。从Task中取出(Activity)是从最顶端取出,也就是说先进后出,后进先出。而Activity在Task中的顺序是可以控制的,在Activity跳转时用到Intent Flag可以设置新建Activity的创建方式。
名称 | 中文 | 说明 |
---|---|---|
FLAG_ACTIVITY_BROUGHT_TO_FRONT | 默认标志 | 通常在应用代码中不需要设置这个FLAG,当launchMode为singleTask时系统会默认设置这个标志。 |
FLAG_ACTIVITY_CLEAR_TASK | 清空任务标志 | 如果Intent中设置了这个标志,会导致含有待启动Activity的Task在Activity被启动前清空。也就是说,这个Activity会成为一个新的root,并且所有旧的activity都被finish掉。这个标志只能与FLAG_ACTIVITY_NEW_TASK 一起使用。 |
FLAG_ACTIVITY_CLEAR_TOP | 清空任务栈中在其之上的Activity | 如果设置了这个标志,并且待启动的Activity已经存在于当前的task中,那就不会再给这个activity新起一个实例,而是将task中在它之上的其它activity全部关闭,然后把Intent作为一个新的Intent传给这个Activity(当前已在栈顶)。 例如,一个task中存在A,B,C,D四个Activity。如果D调用startActivity() 启动B,那么C和D会被finish掉并且B收到这个Intent,最后栈中只有A,B。 上面例子中运行的B activity既可以在onNewIntent()中接收新的Intent,也可以将自己finish掉然后使用新的Intent重启。如果在它的launch mode中设置了"multiple"(默认),并且intent中没有设置 FLAG_ACTIVITY_SINGLE_TOP 标志,那它就会被finish掉然后重新创建。如果是其它的launchMode或者是设置了FLAG_ACTIVITY_SINGLE_TOP 属性,那就会使用现有的实例的OnNewIntent()方法来接受Intent。 这种启动模式也可以与 FLAG_ACTIVITY_NEW_TASK 一起使用:如果用来启动一个任务的root activity,它会将这个任务中现在运行的实例调到前台,然后将任务清空至只有根Activity的状态。这很有用,例如要从通知中心里启动一个Activity时。 |
FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET | 任务重置时将任务中在此标记之后的Activity清空 | 设置这个标志意味着在activity栈中做一个标记,在Task重置的时候栈就把从标记往上的activity都清除。也就是说,下次这个Task被通过FLAG_ACTIVITY_RESET_TASK_IF_NEEDED调到前台时(通常是由于用户从桌面重新启动),这个activity和它之上的activity都会被finish掉,这样用户就不会再回到他们,而是直接回到在它们之前的activity。 这在应用切换时非常有用。比如,Email应用会需要查看附件,就要调用查看图片的Activity来显示,那这个查看图片的Activity就会成为Email应用任务里的一部分。但是,如果用户离开了Email的任务,过了一会儿由通过Home来选择Email应用,我们会希望它回到查看邮件会话的页面,而不是浏览图片附件的页面,不然就感觉太诡异了。如果在启动查看图片Activity时设置了这个标志,那这个Activity及由它启动的Activity在下一次用户返回邮件时都会被清除。 |
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | 不显示在近期任务中 | 如果设置这个标志,这个Activity就不会在近期任务中显示。 |
FLAG_ACTIVITY_FORWARD_RESULT | 转发结果 | 如果Activity A 在启动 Activity B时设置了这个标志,那A的答复目标会传递给B,这样一来B就可以通过调用setResult(int) 将返回结果返回给A的答复目标。简单如下:O ----startActivityForResult()----> A ----FLAG_ACTIVITY_FORWARD_RESULT----> B。 A的答复目标是O,如果A在启动B时使用了这个标志,A就会把答复目标O的信息传递给B,以便B将O作为它的答复目标。此时B调用setResult()时的结果信息都会传递给O,而不会给A。并且此时在A中调用setResult()的内容不会生效。我还没发现使A中setResult()生效的方法。注意:这个标志不能与startActivityForResult()一起使用。 |
FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY | 从近期任务中启动的标志 | 这个标志通常情况下不会通过应用的代码来设置,而是在通过最近任务启动activity时由系统设置的。 |
FLAG_ACTIVITY_MULTIPLE_TASK Activity | 可在多任务运行的标志 | 除非你实现了自己的顶级应用启动器,否则不要使用这个标志。与 FLAG_ACTIVITY_NEW_TASK 一起使用可以不再把已存在的任务唤起到前台。 当被设置时,系统总会为Intent的Activity启动一个新的task,而不管是否已经有已存在的任务在做同样的事情。 因为默认系统不包含图形化的任务管理功能,所以除非你给用户提供了返回到已启动任务的方法,否则就不要用这个标志。如果FLAG_ACTIVITY_NEW_TASK没有设置,则这个标志也会被忽略。 |
FLAG_ACTIVITY_NEW_TASK | 尝试在新任务中启动Activity的标志(并不一定就会在新的任务栈中) | If set, this activity will become the start of a new task on this history stack. A task (from the activity that started it to the next task activity) defines an atomic group of activities that the user can move to. Tasks can be moved to the foreground and background; all of the activities inside of a particular task always remain in the same order. See Tasks and Back Stack for more information about tasks. This flag is generally used by activities that want to present a "launcher" style behavior: they give the user a list of separate things that can be done, which otherwise run completely independently of the activity launching them. When using this flag, if a task is already running for the activity you are now starting, then a new activity will not be started; instead, the current task will simply be brought to the front of the screen with the state it was last in. See FLAG_ACTIVITY_MULTIPLE_TASK for a flag to disable this behavior.This flag can not be used when the caller is requesting a result from the activity being launched. |
FLAG_ACTIVITY_NO_ANIMATION | 禁用切换动画 | 禁用掉系统默认的Activity切换动画。If set in an Intent passed to Context.startActivity() , this flag will prevent the system from applying an activity transition animation to go to the next activity state. This doesn't mean an animation will never run -- if another activity change happens that doesn't specify this flag before the activity started here is displayed, then that transition will be used. This flag can be put to good use when you are going to do a series of activity operations but the animation seen by the user shouldn't be driven by the first activity change but rather a later one. |
FLAG_ACTIVITY_NO_HISTORY | 不保存Activity的历史状态 | 如果设置这个标志,新的Activity就不会在历史栈中保存。用户一旦离开,这个Activity就会finish掉。也可以使用noHistory属性设置。 |
FLAG_ACTIVITY_NO_USER_ACTION | 不响应onUserLeaveHint方法 | 如果设置了这个标志,可以在避免用户离开当前Activity时回调到 onUserLeaveHint(). 通常,Activity可以通过这个回调表明有明确的用户行为将当前activity切出前台。 这个回调标记了activity生命周期中的一个恰当的点,可以用来“在用户看过通知之后”将它们清除,如闪烁LED灯。如果Activity是由非用户驱动的事件(如电话呼入或闹钟响铃)启动的,那这个标志就应该被传入Context.startActivity,以确保被打断的activity不会认为用户已经看过了通知。 |
FLAG_ACTIVITY_PREVIOUS_IS_TOP | 如果启动Activity时设置了这个标志,那当前这个 Activity 不会被当作顶部的 Activity 来判断是否之后新Intent应该被传给栈顶Activity而不是启动一个新的Activity。之前一个的Activity会被当作栈顶,假定当前的Acitvity会立即自己finish掉。即 A---> B --->C,若B启动C时用了这个标志位,那在启动时B并不会被当作栈顶的Activity,而是用A做栈顶来启动C。此过程中B充当一个跳转页面。典型的场景是在应用选择页面,如果在文本中点击一个网址要跳转到浏览器,而系统中又装了不止一个浏览器应用,此时会弹出应用选择页面。在应用选择页面选择某一款浏览器启动时,就会用到这个Flag。然后应用选择页面将自己finish,以保证从浏览器返回时不会在回到选择页面。经常与FLAG_ACTIVITY_FORWARD_RESULT 一起使用。 | |
FLAG_ACTIVITY_REORDER_TO_FRONT | 任务中的Activity顺序重排 | 如果设置了这个标志,而且被启动的Activity如果已经在运行,那这个Activity会被调到栈顶。比如,一个任务中有4个Activity:A,B,C,D。如果D调用了startActivity() 来启动B时使用了这个标志,那B就会被调到历史栈的栈顶,结果顺序:A,C,D,B,否则顺序会是:A,B,C,D,B。 如果使用了标志 FLAG_ACTIVITY_CLEAR_TOP,那这个FLAG_ACTIVITY_REORDER_TO_FRONT标志会被忽略。 |
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | If set, and this activity is either being started in a new task or bringing to the top an existing task, then it will be launched as the front door of the task. This will result in the application of any affinities needed to have that task in the proper state (either moving activities to or from it), or simply resetting that task to its initial state if needed. | |
FLAG_ACTIVITY_SINGLE_TOP | 设置这个标志之后,如果被启动的Activity已经在栈顶,那它就不会被再次启动。 | |
FLAG_ACTIVITY_TASK_ON_HOME | 直接返回桌面 | 这个标志可以将一个新启动的任务置于当前的home任务(home activity task)之上(如果有的话)。也就是说,在任务中按back键总是会回到home界面,而不是回到他们之前看到的activity。这个标志只能与FLAG_ACTIVITY_NEW_TASK标志一起用。 比如,A->B->C->D,如果在C启动D的时候设置了这个标志,那在D中按Back键则是直接回到桌面,而不是C。注意:只有D是在新的task中被创建时(\也就是D的launchMode是singleInstance时,或者是给D指定了与C不同的taskAffinity并且加了FLAG_ACTIVITY_NEW_TASK标志时),使用 FLAG_ACTIVITY_TASK_ON_HOME标志才会生效。感觉实际使用效果和用 FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK 的效果一样。 |
FLAG_DEBUG_LOG_RESOLUTION | 打开Activity解析的Log开关 | 可以用来启用调试的标志:设置以后,在intent的处理过程中log信息会打印出来,可以看到都找到了些什么来创建最终的解析列表。 |
FLAG_EXCLUDE_STOPPED_PACKAGES | 排除已停止的包 | 设置之后,Intent就不会再匹配那些当前被停止的包里的组件。如果没有设置,默认的匹配行为会包含这些被停止的包。 |
FLAG_RECEIVER_FOREGROUND | 接受器以前台优先级运行 | 当发送广播的时候设置了这个标志,会允许接收者以前台的优先级运行,有更短的时间间隔。正常广播的接受者是后台优先级,不会被自动提升。 |
FLAG_RECEIVER_REGISTERED_ONLY | 只调用手动注册的接收器(忽略manifest中声明的) | 如果发送广播时设置了这个标志,那只会调用注册了的接收器——BroadcastReceiver组件不会被启动。 |
FLAG_RECEIVER_REPLACE_PENDING | 替换掉等待中的广播 | 如果在发送广播时设置了这个标志,那新的广播会替换掉那些已存在的相同广播。相同的定义是通过Intent.filterEquals方法对两个广播的Intent处理返回true。 当匹配到相同的,新的广播和对应的接收器会将待发送的广播列表中已存在的替换掉,在列表中保留同样的位置。这个标志通常被粘性广播(Sticky Broadcast)使用,只保证将最新的广播的值传递给接收器。 |
如果有什么不理解,再看新的这一篇Android Intent的FLAG标志详解
网友评论