Activity的生命周期和启动模式再解析

目录

前言

一、Activity的生命周期再解析

(一)典型情况下的生命周期

(二)异常情况下的生命周期

1、资源相关的系统配置发生改变导致Activity被杀死并重新创建

2、资源内存不足导致低优先级的Activity被杀死

问题1:当系统配置发生改变后,Activity会被重新创建,如何才能不被重建呢?

二、Activity的启动模式

(一)Activity的启动模式(LaunchMode)

1、Activity为什么需要启动模式?

2、standard:标准模式(系统的默认模式)

3、singleTop:栈顶复用模式

4、singleTask:栈内复用模式

5、singleInstance:单实例模式

问题1:什么是Activity所需的任务栈?

问题2:如何给Activity指定启动模式呢?

(二)Activity的Flags

1、FLAG_ACTIVITY_NEW_TASK

2、FLAG_ACTIVITY_SINGLE_TOP

3、FLAG_ACTIVITY_CLEAR_TOP

4、FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS


前言

这篇文章写于几个月之前,当时有事给耽搁了,放在草稿箱里居然给忘了,今天翻了翻博客才想起来,连忙抽时间补全了。这是一篇基础的理论知识,看起来会有点枯燥,相信很多朋友也都是了解的,内容呢,主要是来自主席的《安卓开发艺术探索》,来深入总结一下Activity的生命周期和启动模式。

一、Activity的生命周期再解析

(一)典型情况下的生命周期

1、有过Android开发基础的朋友们都知道,在正常情况下,Activity会经历如下生命周期:

(1)onCreate:表示Activity正在被创建,这是生命周期的第一个方法,在这个方法中,我们可以做一些初始化的工作,比如:调用setContentView去加载界面布局资源,做一些数据的初始化工作等。

(2)onRestart:表示Activity正在重新启动,当当前Activity从不可见重新变为可见状态时,onRestart就会被调用。常见场景:比如用户按Home键切换到桌面或者重新打开一个新的Activity,此时原有的Activity就会被暂停,当用户再次返回原Activity时,就会调用onRestart方法。

(3)onStart:表示Activity正在被启动,即将开始,这时Activity已经可见了,只不过还没有出现在前台,无法和用户进行交互,此时Activity还处在后台,说白了就是我们还看不到。

(4)onResume:表示Activity已经可见了,并且出现在前台,可以和用户进行交互,人眼是可见的。

(5)onPause:表示Activity正在停止,正常情况下,紧接着onStop就会被调用,在这种情况下,如果此时快速的再回到当前Activity,则onResume还会被调用,但是这属于极端情况,用户很难复现这一场景。在这个方法中,可以做一些存储数据、停止动画等工作,需要注意的是,该方法中的这些处理工作不能太耗时,否则会影响新Activity的显示。(当年刚去杭州找的第一份工作就被当时的面试官问到了这个问题,当时还是个新手,和面试官探讨了一会才有了更深入的理解)

(6)onStop:表示Activity即将停止,可以做一些稍微重量级的回收工作,注意是稍微,同样的道理,不能太耗时。

(7)onDestroy:表示Activity即将被销毁,这是生命周期中的最后一个回调方法,在该方法中,我们可以做一些回收工作和最终的资源释放。

正常情况下,Activity的常用生命周期就只有上面7个,具体切换过程如下图所示:

总结:关于以上这些内容,最好的方法就是自己写一个Demo,然后重写这些生命周期方法,在方法内部打上日志,自己跑一边程序,模拟一下可能发生的场景,然后根据输出日志的先后顺序来理解生命周期的先后顺序。

(二)异常情况下的生命周期

1、资源相关的系统配置发生改变导致Activity被杀死并重新创建

分析:在默认情况下,如果Activity不做特殊处理,当系统配置发生改变后,Activity就会被销毁并重新创建。可能的场景:比如手机横竖屏切换时,就是系统配置发生了改变。它的onPause、onStop、onDestroy均会被调用,同时由于它是在异常情况下被终止的,所以系统还会调用onSaveInstanceState来保存当前Activity的状态。这个方法调用时机在onStop之前,并且正常情况下该方法不会被调用。当Activity被重新创建以后,系统会调用onRestoreInstanceState,并且把Activity销毁时onSaveInstanceState方法所保存的Bundle对象作为参数同时传递给onRestoreInstanceState和onCreate方法。因此我们可以通过这两个方法中的Bundle参数是否为空来判断Activity是否被重建了,如果被重建了,可以取出之前保存的数据并回复,从时序上看,onRestoreInstanceState在onStart之后调用。

2、资源内存不足导致低优先级的Activity被杀死

这种情况的数据存储和恢复过程和第一种情况完全一致,但是场景不好模拟。这里说一下Activity的优先级情况。Activity按照优先级从高到低可以分为如下三种:

1、前台Activity——正在和用户交互的Activity,优先级最高;

2、可见但非前台Activity——比如Activity中弹出了一个对话框,导致Activity可见但是位于后台无法和用户直接交互;

3、后台Activity——已经被暂停的Activity,比如执行了onStop,优先级最低。

当系统内存不足时,系统会按照上述优先级去杀死目标Activity所在的进程,并在后续通过onSaveInstanceState和onRestoreInstanceState来存储和恢复数据。如果一个进程中没有四大组件在执行,那么这个进程将很快被杀死,因此一些后台工作不适合脱离四大组件独自运行在后台中,这样很容易被杀死。比较好的方法是将后台工作放入Service中从而保证进程有一定的优先级,这样就不会轻易被系统杀死。

问题1:当系统配置发生改变后,Activity会被重新创建,如何才能不被重建呢?

答:可以给Activity指定configChanges属性,指定多个值时,可以用 “|” 连接起来,比如:android:configChanges="orientation|keyboardHidden"。具体每个值的含义如下表中所示:

二、Activity的启动模式

(一)Activity的启动模式(LaunchMode)

1、Activity为什么需要启动模式?

答:默认情况下,多次启动同一个Activity,系统会创建多个实例并把它们一一放入任务栈中,当我们按返回键的时候,这些Activity会一一回退。那么问题来了,这样不是很傻吗?一样的东西为什么每次都要重新创建出来呢?所以设计Android系统的时候,就已经考虑到了这个问题,所以提供了启动模式来修改系统的默认行为。目前启动模式有四种,分别是:standard、singleTop、singleTask、singleInstance。

2、standard:标准模式(系统的默认模式)

每次启动一个Activity都会重新创建一个新的实例,不管这个实例是否已经存在。被创建的实例的生命周期符合典型情况下的生命周期。在这种模式下,谁启动了这个Activity,那么Activity就运行在启动它的那个Activity所在的栈中。有这样一种情况相信你曾经遇到过,使用ApplicationContext启动标准模式的Activity会报错,如下所示:

android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity  context requires the FLAG_ACTIVITY_NEW_TASK flag.Is this really what you want?

这就是因为标准模式的Activity默认会进入启动它的Activity所属的任务栈中,但是由于非Activity类型的Context并没有任务栈,所以报错了,解决这个问题的方法就是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位。

3、singleTop:栈顶复用模式

这种模式下,如果新的Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,同时它的onNewIntent方法会被回调,通过此方法的参数我们可以取出当前请求的信息。如果新Activity的实例已经存在但不是位于栈顶,那么新Activity仍然会重新创建。

4、singleTask:栈内复用模式

这是一种单实例模式,在这种模式下,只要Activity在一个栈中存在,那么多次启动此Activity都不会重新创建实例,和singleTop一样,系统也会回调它的onNewIntent方法。具体点来说,当一个具有singleTask模式的Activity请求启动后,系统首先寻找是否存在它想要的任务栈,如果不存在,则创建任务栈创建实例入栈;如果存在任务栈,则查找这个Activity实例是否存在,若不存在创建后入栈,若存在,则将它置于栈顶(它之上的Activity都会被pop出栈)并且回调它的onNewIntent方法。举几个例子:

5、singleInstance:单实例模式

这是一种加强的singleTask模式,它除了具有singleTask模式的所有特性外,还加强了一点,就是具有这种模式的Activity只能单独的位于一个任务栈中,换句话说,如果Activity A是这种模式,当A启动后,系统会为它创建一个新的任务栈,然后A独自在这个任务栈中,由于栈内复用的特性,后续的请求均不会创建新的Activity,除非这个独特的任务栈被系统销毁了。

问题1:什么是Activity所需的任务栈?

答:这要从一个参数说起:TaskAffinity——任务相关性。这个参数标识了一个Activity所需要的任务栈的名字,默认情况下,所有Activity所需的任务栈的名字是应用的包名。当然,我们可以为每个Activity单独指定TaskAffinity属性,这个属性值不能和包名相同,否则没有任何实际意义,等同于没指定。TaskAffinity主要和singleTask启动模式或者allowTaskReparenting属性配对使用,在其他情况下没有意义。任务栈分为前台任务栈和后台任务栈,后台任务栈中的Activity处于暂停状态,用户可以通过切换将后台任务栈再次调到前台。下面对两种配对的使用加以解释说明,如下图所示:

问题2:如何给Activity指定启动模式呢?

答:有两种方法,如下图:

(二)Activity的Flags

Activity的Flags很多,这里说一些比较常用的标记位。标记位的作用很多,有的标记位可以设定为Activity的启动模式,比如:FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_SINGLE_TOP等;有的标记位可以影响Activity的运行状态,比如:FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS等。下面具体介绍几个常用的标记位:

1、FLAG_ACTIVITY_NEW_TASK

这个标记位的作用是为Activity指定“singleTask”启动模式,效果和在XML中指定该启动模式相同。

2、FLAG_ACTIVITY_SINGLE_TOP

这个标记位的作用是为Activity指定“singleTop”启动模式,效果和在XML中指定该启动模式相同。

3、FLAG_ACTIVITY_CLEAR_TOP

具有这个标记位的Activity,当它启动时,在同一个任务栈中所有位于它上面的Activity都要出栈。这个标记位一般会和singleTask启动模式一起出现,在这种情况下,被启动Activity的实例如果已经存在,那么系统就会调用它的onNewIntent。如果被启动Activity采用standard模式启动,那么连同它之上的Activity都要出栈,系统会创建新的Activity实例并放入栈顶。

4、FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

具有这个标记位的Activity不会出现在历史Activity的列表中,当某些情况下我们不希望用户通过历史列表回到我们的Activity的时候这个标记比较有用。它等同于在XML中指定Activity的属性android:excludeFromRecents="true"

以上内容基本都是来自《安卓开发艺术探索》,重新总结一遍是为了加深理解,以后有忘记的时候可以很方便的找到,这也是写博文的一个好处吧,今天就写到这里,加油everybody!

发布了48 篇原创文章 · 获赞 47 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/JArchie520/article/details/80534881